← Canyon Run results

Canyon Run

Qwen 3.6 27B MTP GGUF UD Q5 K XL · typescript

◈Preview unavailable — try playing the result

Browser check: Preview unavailable or timed out. This is a technical check, not a quality review.

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,252 bytes · SHA-256 573ffcc3dda9
<!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 Procedural Run</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<style>
</style>
</head>
<body>
<div id="app"></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./state":"src/state.ts","./player":"src/player.ts","./chunks":"src/chunks.ts","./input":"src/input.ts","./audio":"src/audio.ts","./collision":"src/collision.ts","./particles":"src/particles.ts","./hud":"src/hud.ts"},"src/state.ts":{"./types":"src/types.ts"},"src/player.ts":{"./input":"src/input.ts"},"src/collision.ts":{"./chunks":"src/chunks.ts"},"src/hud.ts":{"./state":"src/state.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.start = start;
// Main game loop and scene management
const state_1 = require("./state");
const player_1 = require("./player");
const chunks_1 = require("./chunks");
const input_1 = require("./input");
const audio_1 = require("./audio");
const collision_1 = require("./collision");
const particles_1 = require("./particles");
const hud_1 = require("./hud");
const CHUNK_LENGTH = 80;
class Game {
    constructor() {
        this.cameraTarget = new THREE.Vector3();
        this.cameraLookTarget = new THREE.Vector3();
        this.currentFov = 65;
        this.shakeX = 0;
        this.shakeY = 0;
        // Renderer
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        this.renderer.setClearColor(0x2a1020);
        this.renderer.shadowMap.enabled = true;
        document.getElementById('app').appendChild(this.renderer.domElement);
        // Scene
        this.scene = new THREE.Scene();
        this.scene.fog = new THREE.FogExp2(0x2a1020, 0.002);
        this.scene.background = new THREE.Color(0x2a1020);
        // Camera
        this.camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 500);
        this.camera.position.set(0, 5, 14);
        this.cameraTarget.set(0, 5, 14);
        this.cameraLookTarget.set(0, 1, -8);
        // === LIGHTING ===
        this.scene.add(new THREE.AmbientLight(0xffcc88, 5.0));
        this.scene.add(new THREE.HemisphereLight(0xffeedd, 0x8844aa, 4.0));
        this.dirLight = new THREE.DirectionalLight(0xffeedd, 6.0);
        this.dirLight.position.set(-15, 25, 15);
        this.dirLight.castShadow = true;
        this.dirLight.shadow.camera.left = -40;
        this.dirLight.shadow.camera.right = 40;
        this.dirLight.shadow.camera.top = 40;
        this.dirLight.shadow.camera.bottom = -40;
        this.dirLight.shadow.camera.near = 1;
        this.dirLight.shadow.camera.far = 120;
        this.scene.add(this.dirLight);
        this.scene.add(this.dirLight.target);
        // Purple fill from opposite side
        const fillLight = new THREE.DirectionalLight(0xcc88ff, 1.5);
        fillLight.position.set(25, 12, -10);
        this.scene.add(fillLight);
        // Sunset glow point
        this.sunLight = new THREE.PointLight(0xff6633, 5, 200);
        this.scene.add(this.sunLight);
        // Sun sphere
        const sunGeo = new THREE.SphereGeometry(10, 8, 8);
        const sunMat = new THREE.MeshBasicMaterial({ color: 0xff8844 });
        this.sunSphere = new THREE.Mesh(sunGeo, sunMat);
        this.sunSphere.position.set(0, 12, -120);
        this.scene.add(this.sunSphere);
        // === SKY DOME ===
        const skyGeo = new THREE.SphereGeometry(200, 16, 8);
        const skyMat = new THREE.ShaderMaterial({
            uniforms: {
                uTop: { value: new THREE.Color(0x331144) },
                uMid: { value: new THREE.Color(0xcc4455) },
                uHorizon: { value: new THREE.Color(0xff8844) },
                uBottom: { value: new THREE.Color(0xffcc88) },
            },
            vertexShader: `
        varying float vY;
        void main() {
          vec4 wp = modelMatrix * vec4(position, 1.0);
          vY = normalize(wp.xyz).y;
          gl_Position = projectionMatrix * viewMatrix * wp;
        }
      `,
            fragmentShader: `
        uniform vec3 uTop, uMid, uHorizon, uBottom;
        varying float vY;
        void main() {
          vec3 col;
          if (vY > 0.3) col = mix(uMid, uTop, smoothstep(0.3, 1.0, vY));
          else if (vY > 0.0) col = mix(uHorizon, uMid, smoothstep(0.0, 0.3, vY));
          else col = mix(uBottom, uHorizon, smoothstep(-0.5, 0.0, vY));
          gl_FragColor = vec4(col, 1.0);
        }
      `,
            side: 2,
            depthWrite: false,
        });
        this.skyDome = new THREE.Mesh(skyGeo, skyMat);
        this.scene.add(this.skyDome);
        // === PLAYER ===
        this.player = new player_1.Player();
        this.scene.add(this.player.mesh);
        if (this.player.trailMesh)
            this.scene.add(this.player.trailMesh);
        // === SPEED PARTICLES ===
        this.speedParticles = new particles_1.SpeedParticles();
        this.scene.add(this.speedParticles.particles);
        // === CHUNKS ===
        this.chunkGen = new chunks_1.ChunkGenerator();
        for (let i = 0; i < 8; i++) {
            const chunk = this.chunkGen.generateChunk(-i * CHUNK_LENGTH - 40, 1);
            this.chunkGen.chunks.push(chunk);
            this.scene.add(chunk.group);
        }
        // === INIT ===
        this.clock = new THREE.Clock();
        window.addEventListener('resize', () => this.onResize());
        window.addEventListener('start', () => this.onStart());
        window.addEventListener('restart', () => this.onRestart());
        this.autoStart();
        this.animate();
    }
    onStart() {
        if (!state_1.state.started) {
            state_1.state.started = true;
            audio_1.audio.init();
        }
    }
    autoStart() {
        state_1.state.started = true;
        // Force-hide start overlay
        const startEl = document.getElementById('start-btn');
        if (startEl) {
            const parent = startEl.parentElement;
            if (parent)
                parent.style.display = 'none';
        }
        // Also hide via HUD reference
        hud_1.hud.hideStart();
        try {
            audio_1.audio.init();
        }
        catch (e) { }
    }
    onRestart() {
        this.chunkGen.chunks.forEach(c => {
            this.scene.remove(c.group);
            c.group.traverse((o) => {
                if (o instanceof THREE.Mesh) {
                    o.geometry.dispose();
                    if (o.material && !Array.isArray(o.material))
                        o.material.dispose();
                }
            });
        });
        this.chunkGen.chunks = [];
        (0, state_1.resetState)();
        this.player.pos.set(0, 2, 0);
        this.player.vel.set(0, 0, 0);
        this.player.bankAngle = 0;
        this.player.pitchAngle = 0;
        this.player.yawAngle = 0;
        this.camera.position.set(0, 5, 14);
        this.cameraTarget.set(0, 5, 14);
        this.cameraLookTarget.set(0, 1, -8);
        for (let i = 0; i < 8; i++) {
            const chunk = this.chunkGen.generateChunk(-i * CHUNK_LENGTH - 40, 1);
            this.chunkGen.chunks.push(chunk);
            this.scene.add(chunk.group);
        }
        hud_1.hud.hideGameOver();
    }
    onResize() {
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
    }
    updateChunks() {
        const pz = this.player.pos.z;
        const last = this.chunkGen.chunks[this.chunkGen.chunks.length - 1];
        const lz = last ? last.z : 0;
        if (pz < lz + CHUNK_LENGTH * 5) {
            const chunk = this.chunkGen.generateChunk(lz - CHUNK_LENGTH, state_1.state.difficulty);
            this.chunkGen.chunks.push(chunk);
            this.scene.add(chunk.group);
        }
        for (let i = 0; i < this.chunkGen.chunks.length; i++) {
            const c = this.chunkGen.chunks[i];
            if (c.z > pz + CHUNK_LENGTH * 2) {
                this.scene.remove(c.group);
                c.group.traverse((o) => {
                    if (o instanceof THREE.Mesh) {
                        o.geometry.dispose();
                        if (o.material && !Array.isArray(o.material))
                            o.material.dispose();
                    }
                });
                this.chunkGen.chunks.splice(i, 1);
                i--;
            }
        }
    }
    updateWorldPositions() {
        for (const chunk of this.chunkGen.chunks) {
            if (!chunk.active)
                continue;
            const gz = chunk.group.position.z;
            chunk.obstacles.forEach(o => {
                o.worldSphere.center.set(o.localSphere.center.x, o.localSphere.center.y, o.localSphere.center.z + gz);
            });
            chunk.orbs.forEach(o => {
                const wp = new THREE.Vector3(o.localPos.x, o.localPos.y, o.localPos.z + gz);
                o.mesh.position.copy(wp);
                o.glow.position.copy(wp);
            });
        }
    }
    updateCamera(dt) {
        const p = this.player.pos;
        const camX = p.x * 0.3;
        const camY = p.y + 5;
        const camZ = p.z + 12 + (state_1.state.boosting ? 4 : 0);
        let sx = 0, sy = 0;
        if (state_1.state.shakeIntensity > 0) {
            sx = (Math.random() - 0.5) * state_1.state.shakeIntensity * 0.4;
            sy = (Math.random() - 0.5) * state_1.state.shakeIntensity * 0.4;
        }
        this.cameraTarget.set(camX + sx, camY + sy, camZ);
        this.camera.position.lerp(this.cameraTarget, 4 * dt);
        this.cameraLookTarget.set(p.x * 0.5, p.y + 1, p.z - 8);
        this.camera.lookAt(this.cameraLookTarget);
        const tFov = 60 + (state_1.state.speed - 15) * 0.8 + (state_1.state.boosting ? 15 : 0);
        this.currentFov += (tFov - this.currentFov) * 3 * dt;
        this.camera.fov = this.currentFov;
        this.camera.updateProjectionMatrix();
    }
    updateLights() {
        const p = this.player.pos;
        this.dirLight.position.set(p.x - 15, p.y + 25, p.z + 15);
        this.dirLight.target.position.set(p.x, p.y - 5, p.z - 20);
        this.dirLight.target.updateMatrixWorld();
        this.sunLight.position.set(p.x, p.y + 12, p.z - 80);
        this.sunSphere.position.set(p.x, 12, p.z - 120);
        this.skyDome.position.set(p.x, 0, p.z);
    }
    updateGame(dt) {
        if (!state_1.state.started || (0, state_1.isGameOver)())
            return;
        const ed = Math.min(dt, 0.05);
        // Boost
        if (input_1.input.wantsBoost() && state_1.state.boost >= 10 && !state_1.state.boosting) {
            state_1.state.boosting = true;
            audio_1.audio.playBoost();
        }
        if (state_1.state.boosting) {
            state_1.state.boost -= 25 * ed;
            if (state_1.state.boost <= 0) {
                state_1.state.boost = 0;
                state_1.state.boosting = false;
            }
        }
        if (!state_1.state.boosting && state_1.state.speed > state_1.state.baseSpeed) {
            state_1.state.boost = Math.min(state_1.state.maxBoost, state_1.state.boost + 4 * ed);
        }
        // Speed
        const target = state_1.state.baseSpeed + state_1.state.difficulty * 1.5;
        state_1.state.speed += ((target * (state_1.state.boosting ? 1.6 : 1)) - state_1.state.speed) * 2.5 * ed;
        state_1.state.maxSpeed = Math.max(state_1.state.maxSpeed, state_1.state.speed);
        state_1.state.distance += state_1.state.speed * ed;
        this.player.pos.z -= state_1.state.speed * ed;
        // Energy
        state_1.state.energy -= (2.5 + state_1.state.difficulty * 0.5) * ed;
        if (state_1.state.boosting)
            state_1.state.energy -= 2 * ed;
        if (state_1.state.energy <= 0) {
            state_1.state.energy = 0;
            state_1.state.gameOver = true;
            audio_1.audio.playExplosion();
            hud_1.hud.showGameOver();
            return;
        }
        state_1.state.difficulty = 1 + state_1.state.score / 400;
        this.player.update(ed, state_1.state.speed, state_1.state.boosting);
        this.updateCamera(ed);
        this.updateLights();
        audio_1.audio.updateEngine(state_1.state.speed);
        this.updateChunks();
        this.updateWorldPositions();
        // Collision
        collision_1.collision.updatePlayer(this.player.pos);
        const obs = this.chunkGen.getActiveObstacles();
        const nm = collision_1.collision.checkNearMisses(obs);
        if (nm.triggered) {
            state_1.state.nearMissPulse = 1.0;
            state_1.state.multiplier = Math.min(10, state_1.state.multiplier + 0.3);
            state_1.state.score += 8 * state_1.state.multiplier * ed;
            if (Math.random() < 0.015)
                audio_1.audio.playNearMiss();
            state_1.state.shakeIntensity = Math.min(0.8, 2.0 / nm.closestDist);
        }
        const orbs = this.chunkGen.getActiveOrbs();
        const collected = collision_1.collision.checkOrbs(orbs, ed);
        for (const orb of collected) {
            state_1.state.energy = Math.min(state_1.state.maxEnergy, state_1.state.energy + 15);
            state_1.state.boost = Math.min(state_1.state.maxBoost, state_1.state.boost + 20);
            state_1.state.score += 5 * state_1.state.multiplier;
            orb.mesh.visible = false;
            orb.glow.intensity = 0;
            audio_1.audio.playCollect();
        }
        if (collision_1.collision.checkCollision(obs)) {
            state_1.state.gameOver = true;
            audio_1.audio.playExplosion();
            hud_1.hud.showGameOver();
            return;
        }
        state_1.state.nearMissPulse = Math.max(0, state_1.state.nearMissPulse - 4 * ed);
        state_1.state.shakeIntensity = Math.max(0, state_1.state.shakeIntensity - 5 * ed);
        state_1.state.multiplier = Math.max(1, state_1.state.multiplier - 0.5 * ed);
        state_1.state.score += state_1.state.speed * state_1.state.multiplier * 0.01 * ed;
        // Animate orbs
        const t = this.clock.getElapsedTime();
        for (const chunk of this.chunkGen.getActiveChunks()) {
            for (const orb of chunk.orbs) {
                if (orb.collected)
                    continue;
                orb.mesh.rotation.y += 3 * ed;
                orb.mesh.rotation.x += 1.5 * ed;
                const bob = Math.sin(t * 2.5 + orb.localPos.x * 0.5) * 0.15;
                orb.mesh.position.y += bob * 0.02;
                orb.glow.intensity = 1.0 + Math.sin(t * 4) * 0.3;
            }
        }
        this.speedParticles.update(ed, this.player.pos, state_1.state.speed);
        hud_1.hud.update();
    }
    animate() {
        requestAnimationFrame(() => this.animate());
        const dt = this.clock.getDelta();
        this.updateGame(dt);
        this.renderer.render(this.scene, this.camera);
    }
}
function start() { new Game(); }
start();
};

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createGameState = createGameState;
function createGameState() {
    return {
        score: 0,
        multiplier: 1,
        multiplierTimer: 0,
        energy: 100,
        maxEnergy: 100,
        speed: 15,
        baseSpeed: 15,
        boost: 0,
        maxBoost: 100,
        boosting: false,
        boostingTimer: 0,
        gameOver: false,
        started: false,
        distance: 0,
        maxSpeed: 0,
        difficulty: 1,
        nearMissPulse: 0,
        shakeIntensity: 0,
    };
}
};

// ── module: src/state.ts ──
__mods["src/state.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.state = void 0;
exports.resetState = resetState;
exports.setGameOver = setGameOver;
exports.isGameOver = isGameOver;
const types_1 = require("./types");
exports.state = (0, types_1.createGameState)();
function resetState() {
    exports.state = (0, types_1.createGameState)();
    exports.state.started = true;
}
function setGameOver() {
    exports.state.gameOver = true;
}
function isGameOver() {
    return exports.state.gameOver;
}
};

// ── module: src/audio.ts ──
__mods["src/audio.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.audio = void 0;
// Procedural audio using Web Audio API
class SoundEngine {
    constructor() {
        this.ctx = null;
        this.masterGain = null;
        this.engineOsc = null;
        this.engineGain = null;
        this.engineFilter = null;
    }
    init() {
        this.ctx = new AudioContext();
        this.masterGain = this.ctx.createGain();
        this.masterGain.gain.value = 0.3;
        this.masterGain.connect(this.ctx.destination);
        // Engine hum
        this.engineOsc = this.ctx.createOscillator();
        this.engineGain = this.ctx.createGain();
        this.engineFilter = this.ctx.createBiquadFilter();
        this.engineOsc.type = 'sawtooth';
        this.engineOsc.frequency.value = 60;
        this.engineFilter.type = 'lowpass';
        this.engineFilter.frequency.value = 200;
        this.engineFilter.Q.value = 2;
        this.engineGain.gain.value = 0.08;
        this.engineOsc.connect(this.engineFilter);
        this.engineFilter.connect(this.engineGain);
        this.engineGain.connect(this.masterGain);
        this.engineOsc.start();
    }
    updateEngine(speed) {
        if (!this.engineOsc || !this.engineGain || !this.engineFilter)
            return;
        const freq = 40 + speed * 4;
        this.engineOsc.frequency.setTargetAtTime(freq, this.ctx.currentTime, 0.1);
        this.engineFilter.frequency.setTargetAtTime(150 + speed * 15, this.ctx.currentTime, 0.1);
        this.engineGain.gain.setTargetAtTime(0.05 + speed * 0.003, this.ctx.currentTime, 0.1);
    }
    playCollect() {
        if (!this.ctx || !this.masterGain)
            return;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sine';
        osc.frequency.setValueAtTime(800, this.ctx.currentTime);
        osc.frequency.exponentialRampToValueAtTime(1600, this.ctx.currentTime + 0.1);
        osc.frequency.exponentialRampToValueAtTime(2400, this.ctx.currentTime + 0.15);
        gain.gain.setValueAtTime(0.15, this.ctx.currentTime);
        gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.25);
        osc.connect(gain);
        gain.connect(this.masterGain);
        osc.start();
        osc.stop(this.ctx.currentTime + 0.25);
    }
    playNearMiss() {
        if (!this.ctx || !this.masterGain)
            return;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sawtooth';
        osc.frequency.setValueAtTime(200, this.ctx.currentTime);
        osc.frequency.exponentialRampToValueAtTime(600, this.ctx.currentTime + 0.08);
        osc.frequency.exponentialRampToValueAtTime(100, this.ctx.currentTime + 0.15);
        gain.gain.setValueAtTime(0.12, this.ctx.currentTime);
        gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.2);
        osc.connect(gain);
        gain.connect(this.masterGain);
        osc.start();
        osc.stop(this.ctx.currentTime + 0.2);
    }
    playExplosion() {
        if (!this.ctx || !this.masterGain)
            return;
        const bufferSize = this.ctx.sampleRate * 0.5;
        const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
        const data = buffer.getChannelData(0);
        for (let i = 0; i < bufferSize; i++) {
            data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (bufferSize * 0.1));
        }
        const source = this.ctx.createBufferSource();
        source.buffer = buffer;
        const gain = this.ctx.createGain();
        gain.gain.setValueAtTime(0.3, this.ctx.currentTime);
        gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.5);
        const filter = this.ctx.createBiquadFilter();
        filter.type = 'lowpass';
        filter.frequency.setValueAtTime(2000, this.ctx.currentTime);
        filter.frequency.exponentialRampToValueAtTime(100, this.ctx.currentTime + 0.5);
        source.connect(filter);
        filter.connect(gain);
        gain.connect(this.masterGain);
        source.start();
    }
    playBoost() {
        if (!this.ctx || !this.masterGain)
            return;
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.type = 'sine';
        osc.frequency.setValueAtTime(300, this.ctx.currentTime);
        osc.frequency.exponentialRampToValueAtTime(1200, this.ctx.currentTime + 0.3);
        gain.gain.setValueAtTime(0.1, this.ctx.currentTime);
        gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + 0.4);
        osc.connect(gain);
        gain.connect(this.masterGain);
        osc.start();
        osc.stop(this.ctx.currentTime + 0.4);
    }
    destroy() {
        if (this.engineOsc) {
            this.engineOsc.stop();
            this.engineOsc.disconnect();
        }
        if (this.ctx) {
            this.ctx.close();
        }
    }
}
exports.audio = new SoundEngine();
};

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

// ── module: src/input.ts ──
__mods["src/input.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.input = exports.Input = void 0;
// Input handling
class Input {
    constructor() {
        this.keys = new Set();
        this.mouseX = 0;
        this.mouseY = 0;
        this.touchActive = false;
        window.addEventListener('keydown', (e) => {
            this.keys.add(e.code);
            if (['Space', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.code)) {
                e.preventDefault();
            }
        });
        window.addEventListener('keyup', (e) => {
            this.keys.delete(e.code);
        });
        window.addEventListener('mousemove', (e) => {
            this.mouseX = (e.clientX / window.innerWidth) * 2 - 1;
            this.mouseY = -(e.clientY / window.innerHeight) * 2 + 1;
        });
        window.addEventListener('touchstart', (e) => {
            this.touchActive = true;
            const t = e.touches[0];
            this.mouseX = (t.clientX / window.innerWidth) * 2 - 1;
            this.mouseY = -(t.clientY / window.innerHeight) * 2 + 1;
            e.preventDefault();
        }, { passive: false });
        window.addEventListener('touchmove', (e) => {
            const t = e.touches[0];
            this.mouseX = (t.clientX / window.innerWidth) * 2 - 1;
            this.mouseY = -(t.clientY / window.innerHeight) * 2 + 1;
            e.preventDefault();
        }, { passive: false });
        window.addEventListener('touchend', () => {
            this.touchActive = false;
        });
    }
    isDown(code) {
        return this.keys.has(code);
    }
    // Returns -1 to 1 for left/right
    getLateralInput() {
        let v = 0;
        if (this.isDown('ArrowLeft') || this.isDown('KeyA'))
            v -= 1;
        if (this.isDown('ArrowRight') || this.isDown('KeyD'))
            v += 1;
        return v;
    }
    // Returns -1 to 1 for up/down
    getVerticalInput() {
        let v = 0;
        if (this.isDown('ArrowUp') || this.isDown('KeyW'))
            v += 1;
        if (this.isDown('ArrowDown') || this.isDown('KeyS'))
            v -= 1;
        return v;
    }
    wantsBoost() {
        return this.isDown('Space') || this.isDown('ShiftLeft') || this.isDown('ShiftRight');
    }
}
exports.Input = Input;
exports.input = new Input();
};

// ── module: src/player.ts ──
__mods["src/player.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Player = void 0;
// Player flight-based physics model
const input_1 = require("./input");
class Player {
    constructor() {
        this.trailMesh = null;
        // Physics state
        this.pos = new THREE.Vector3(0, 2, 0);
        this.vel = new THREE.Vector3(0, 0, 0);
        this.bankAngle = 0; // roll
        this.pitchAngle = 0;
        this.yawAngle = 0;
        // Config
        this.lateralSpeed = 18;
        this.verticalSpeed = 14;
        this.lateralFriction = 8;
        this.verticalFriction = 8;
        this.bankFactor = 0.7;
        // Craft body — sleek low-poly geometric shape
        const group = new THREE.Group();
        // Main body — elongated diamond
        const bodyGeo = new THREE.ConeGeometry(0.3, 1.8, 6);
        bodyGeo.rotateX(Math.PI / 2);
        const bodyMat = new THREE.MeshStandardMaterial({
            color: 0xc0c0d0,
            flatShading: true,
            metalness: 0.8,
            roughness: 0.2,
        });
        const body = new THREE.Mesh(bodyGeo, bodyMat);
        group.add(body);
        // Cockpit
        const cockpitGeo = new THREE.OctahedronGeometry(0.2, 0);
        const cockpitMat = new THREE.MeshStandardMaterial({
            color: 0x4488ff,
            flatShading: true,
            emissive: 0x2244aa,
            emissiveIntensity: 0.5,
            metalness: 0.3,
            roughness: 0.1,
        });
        const cockpit = new THREE.Mesh(cockpitGeo, cockpitMat);
        cockpit.position.set(0, 0.15, 0.3);
        group.add(cockpit);
        // Wings
        const wingGeo = new THREE.BoxGeometry(3.2, 0.05, 0.8);
        const wingMat = new THREE.MeshStandardMaterial({
            color: 0xaa8866,
            flatShading: true,
            metalness: 0.6,
            roughness: 0.3,
        });
        const wings = new THREE.Mesh(wingGeo, wingMat);
        wings.position.set(0, -0.05, 0.1);
        group.add(wings);
        // Wing tips
        const tipGeo = new THREE.TetrahedronGeometry(0.15);
        const tipMat = new THREE.MeshStandardMaterial({
            color: 0xff6633,
            flatShading: true,
            emissive: 0xff4400,
            emissiveIntensity: 0.8,
        });
        [-1, 1].forEach(side => {
            const tip = new THREE.Mesh(tipGeo, tipMat);
            tip.position.set(side * 1.6, -0.05, 0.1);
            group.add(tip);
        });
        // Engine glow light
        this.engineGlow = new THREE.PointLight(0xff6633, 2, 6);
        this.engineGlow.position.set(0, 0, 1.2);
        group.add(this.engineGlow);
        // Engine nozzle
        const nozzleGeo = new THREE.CylinderGeometry(0.08, 0.2, 0.3, 6);
        nozzleGeo.rotateX(Math.PI / 2);
        const nozzleMat = new THREE.MeshStandardMaterial({
            color: 0xff8844,
            flatShading: true,
            emissive: 0xff4400,
            emissiveIntensity: 1.0,
        });
        const nozzle = new THREE.Mesh(nozzleGeo, nozzleMat);
        nozzle.position.set(0, 0, 1.0);
        group.add(nozzle);
        this.mesh = group;
        this.mesh.position.copy(this.pos);
        // Trail particles
        const trailCount = 80;
        this.trailGeometry = new THREE.BufferGeometry();
        const trailPos = new Float32Array(trailCount * 3);
        const trailSizes = new Float32Array(trailCount);
        for (let i = 0; i < trailCount; i++) {
            trailPos[i * 3] = 0;
            trailPos[i * 3 + 1] = 0;
            trailPos[i * 3 + 2] = 0;
            trailSizes[i] = 0;
        }
        this.trailGeometry.setAttribute('position', new THREE.BufferAttribute(trailPos, 3));
        this.trailGeometry.setAttribute('size', new THREE.BufferAttribute(trailSizes, 1));
        const trailMat = new THREE.ShaderMaterial({
            uniforms: {
                uColor: { value: new THREE.Color(0xff6633) },
            },
            vertexShader: `
        attribute float size;
        varying float vAlpha;
        void main() {
          vAlpha = size / 2.0;
          vec4 mvPos = modelViewMatrix * vec4(position, 1.0);
          gl_PointSize = size * 80.0 / -mvPos.z;
          gl_Position = projectionMatrix * mvPos;
        }
      `,
            fragmentShader: `
        uniform vec3 uColor;
        varying float vAlpha;
        void main() {
          float d = length(gl_PointCoord - 0.5) * 2.0;
          if (d > 1.0) discard;
          float a = smoothstep(1.0, 0.0, d) * vAlpha * 0.7;
          gl_FragColor = vec4(uColor, a);
        }
      `,
            transparent: true,
            blending: THREE.AdditiveBlending,
        });
        this.trailMesh = new THREE.Points(this.trailGeometry, trailMat);
        this.trailMesh.frustumCulled = false;
        this.trailMesh.visible = true;
        // Initialize trail with invisible particles behind player
        for (let i = 0; i < trailCount; i++) {
            trailPos[i * 3 + 2] = 2 + i * 0.3;
            trailSizes[i] = 0;
        }
        this.trailGeometry.attributes.position.needsUpdate = true;
        this.trailGeometry.attributes.size.needsUpdate = true;
    }
    update(dt, speed, boosting) {
        // Get input
        const lateral = input_1.input.getLateralInput();
        const vertical = input_1.input.getVerticalInput();
        // Apply acceleration
        this.vel.x += lateral * this.lateralSpeed * dt;
        this.vel.y += vertical * this.verticalSpeed * dt;
        // Apply friction (weighty feel)
        this.vel.x *= Math.max(0, 1 - this.lateralFriction * dt);
        this.vel.y *= Math.max(0, 1 - this.verticalFriction * dt);
        // Clamp velocity
        const maxV = 25;
        const vlen = Math.sqrt(this.vel.x * this.vel.x + this.vel.y * this.vel.y);
        if (vlen > maxV) {
            this.vel.x *= maxV / vlen;
            this.vel.y *= maxV / vlen;
        }
        // Integrate position
        this.pos.x += this.vel.x * dt;
        this.pos.y += this.vel.y * dt;
        // Clamp to playable area
        this.pos.x = Math.max(-18, Math.min(18, this.pos.x));
        this.pos.y = Math.max(-8, Math.min(14, this.pos.y));
        // Banking (roll) based on lateral velocity
        const targetBank = -this.vel.x * this.bankFactor * 0.15;
        this.bankAngle += (targetBank - this.bankAngle) * 8 * dt;
        // Pitch based on vertical velocity
        const targetPitch = -this.vel.y * 0.1;
        this.pitchAngle += (targetPitch - this.pitchAngle) * 6 * dt;
        // Yaw subtle drift with lateral movement
        this.yawAngle += (lateral * -0.15 - this.yawAngle) * 4 * dt;
        // Apply to mesh
        this.mesh.position.copy(this.pos);
        const euler = new THREE.Euler(this.pitchAngle, this.yawAngle, this.bankAngle, 'YXZ');
        this.mesh.quaternion.setFromEuler(euler);
        // Engine glow intensity
        const glowIntensity = 1.5 + (boosting ? 3 : 0) + Math.sin(Date.now() * 0.01) * 0.3;
        this.engineGlow.intensity = glowIntensity;
        // Update trail particles
        this.updateTrail(dt, speed);
    }
    updateTrail(dt, speed) {
        if (!this.trailMesh)
            return;
        const positions = this.trailGeometry.attributes.position.array;
        const sizes = this.trailGeometry.attributes.size.array;
        const count = sizes.length;
        // Shift trail backwards
        for (let i = count - 1; i > 0; i--) {
            positions[i * 3] = positions[(i - 1) * 3];
            positions[i * 3 + 1] = positions[(i - 1) * 3 + 1];
            positions[i * 3 + 2] = positions[(i - 1) * 3 + 2] + speed * dt * 0.5;
            sizes[i] = sizes[i - 1] * 0.95;
        }
        // Insert new particle at origin (player position)
        positions[0] = this.pos.x + (Math.random() - 0.5) * 0.2;
        positions[0 + 1] = this.pos.y - 0.1 + (Math.random() - 0.5) * 0.1;
        positions[0 + 2] = this.pos.z + 1.5;
        sizes[0] = 0.8 + Math.random() * 0.4;
        this.trailGeometry.attributes.position.needsUpdate = true;
        this.trailGeometry.attributes.size.needsUpdate = true;
    }
    getBounds() {
        return { radius: 0.6 };
    }
}
exports.Player = Player;
};

// ── module: src/chunks.ts ──
__mods["src/chunks.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.chunkGenerator = exports.ChunkGenerator = void 0;
// Color palette — vibrant sunset canyon
const WALL_COLORS = [0xFF9944, 0xFFAA55, 0xFF8833, 0xFFBB66, 0xFF7722, 0xFFAA55];
const GROUND_COLOR = 0x996644;
const MONOLITH_COLORS = [0x9944FF, 0x8833EE, 0xAA55FF, 0x7722DD];
const CHUNK_LENGTH = 80;
function seededRandom(seed) {
    let s = seed;
    return () => {
        s = (s * 16807 + 0) % 2147483647;
        return (s - 1) / 2147483646;
    };
}
// Helper: create a bright wall material with emissive glow
function makeWallMat(col, glow) {
    const c = new THREE.Color(col);
    const m = new THREE.MeshStandardMaterial({
        color: c,
        flatShading: true,
        roughness: 0.9,
        metalness: 0.05,
        emissive: c,
        emissiveIntensity: glow,
    });
    return m;
}
class ChunkGenerator {
    constructor() {
        this.chunks = [];
        this.seed = 42;
        this.difficulty = 1;
    }
    getActiveChunks() {
        return this.chunks.filter(c => c.active);
    }
    getActiveObstacles() {
        const all = [];
        this.chunks.filter(c => c.active).forEach(c => all.push(...c.obstacles));
        return all;
    }
    getActiveOrbs() {
        const all = [];
        this.chunks.filter(c => c.active).forEach(c => all.push(...c.orbs));
        return all;
    }
    generateChunk(z, difficulty) {
        const rng = seededRandom(this.seed + Math.floor(z / CHUNK_LENGTH) * 137);
        this.difficulty = difficulty;
        const group = new THREE.Group();
        group.position.z = z;
        const obstacles = [];
        const orbs = [];
        const typeRoll = rng();
        let type;
        if (typeRoll < 0.5)
            type = 'canyon';
        else if (typeRoll < 0.8)
            type = 'monolith';
        else
            type = 'squeeze';
        this.createFloor(group, rng, difficulty);
        if (type === 'canyon')
            this.createCanyonWalls(group, obstacles, rng, difficulty);
        else if (type === 'monolith')
            this.createMonoliths(group, obstacles, rng, difficulty);
        else
            this.createSqueeze(group, obstacles, rng, difficulty);
        this.createOrbs(group, orbs, rng);
        return { group, z, type, obstacles, orbs, active: true };
    }
    createFloor(group, rng, difficulty) {
        const fw = 34 + rng() * 8;
        const m = makeWallMat(GROUND_COLOR, 0.2);
        const floor = new THREE.Mesh(new THREE.BoxGeometry(fw, 2, CHUNK_LENGTH), m);
        floor.position.y = -10;
        floor.receiveShadow = true;
        group.add(floor);
        const bumps = Math.floor(rng() * 2 * difficulty);
        for (let i = 0; i < bumps; i++) {
            const sz = 1 + rng() * 2;
            const col = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
            const b = new THREE.Mesh(new THREE.TetrahedronGeometry(sz), makeWallMat(col, 0.3));
            b.position.set((rng() - 0.5) * fw * 0.7, -9 + sz * 0.3, (rng() - 0.5) * CHUNK_LENGTH * 0.8);
            b.rotation.set(rng() * Math.PI, rng() * Math.PI, rng() * Math.PI);
            group.add(b);
        }
    }
    createCanyonWalls(group, obstacles, rng, diff) {
        const wh = 25 + diff * 4;
        const gw = Math.max(10, 28 - diff * 2);
        const lw = this.createWallSegment(CHUNK_LENGTH, wh, rng);
        lw.position.set(-gw / 2 - 5, wh / 2 - 5, 0);
        group.add(lw);
        obstacles.push(this.groupToObstacle(lw, -gw / 2 - 2, wh / 2 - 5, 0, 5));
        const rw = this.createWallSegment(CHUNK_LENGTH, wh, rng);
        rw.position.set(gw / 2 + 5, wh / 2 - 5, 0);
        group.add(rw);
        obstacles.push(this.groupToObstacle(rw, gw / 2 + 2, wh / 2 - 5, 0, 5));
        if (rng() < 0.25 * diff) {
            const col = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
            const oh = new THREE.Mesh(new THREE.BoxGeometry(gw * 0.3, 2, 6), makeWallMat(col, 0.5));
            oh.position.set((rng() - 0.5) * gw * 0.3, 8 + rng() * 5, (rng() - 0.5) * CHUNK_LENGTH * 0.6);
            group.add(oh);
            obstacles.push(this.meshToObstacle(oh, 3));
        }
    }
    createWallSegment(length, height, rng) {
        const wg = new THREE.Group();
        const segs = Math.floor(3 + rng() * 4);
        const sw = length / segs;
        for (let i = 0; i < segs; i++) {
            const w = sw + (rng() - 0.5) * 4;
            const h = height * (0.5 + rng() * 0.7);
            const d = 3 + rng() * 5;
            const col = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
            const m = makeWallMat(col, 0.5);
            const seg = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), m);
            seg.position.set(0, h / 2 - 5, -length / 2 + i * sw + sw / 2);
            seg.castShadow = true;
            seg.receiveShadow = true;
            wg.add(seg);
        }
        return wg;
    }
    createMonoliths(group, obstacles, rng, diff) {
        const count = Math.floor(3 + rng() * 4 * diff);
        for (let i = 0; i < count; i++) {
            const h = 8 + rng() * 12, w = 2 + rng() * 3, d = 2 + rng() * 3;
            const x = (rng() - 0.5) * 22, y = -8 + h / 2, z = (rng() - 0.5) * CHUNK_LENGTH * 0.75;
            const col = MONOLITH_COLORS[Math.floor(rng() * MONOLITH_COLORS.length)];
            let mesh;
            if (rng() < 0.3) {
                const m = makeWallMat(col, 0.5);
                m.metalness = 0.6;
                m.roughness = 0.3;
                mesh = new THREE.Mesh(new THREE.OctahedronGeometry(w, 0), m);
                mesh.scale.y = h / w;
            }
            else {
                const m = makeWallMat(col, 0.3);
                m.metalness = 0.5;
                m.roughness = 0.4;
                mesh = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), m);
            }
            mesh.position.set(x, y, z);
            mesh.rotation.y = rng() * Math.PI;
            mesh.castShadow = true;
            group.add(mesh);
            const r = Math.max(w, d) * 0.7 + 1;
            const c = new THREE.Vector3(x, y, z);
            obstacles.push({ mesh, localSphere: new THREE.Sphere(c.clone(), r), worldSphere: new THREE.Sphere(c, r), nearMissRadius: r + 2.5 });
        }
        [-1, 1].forEach(side => {
            const col = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
            const wall = new THREE.Mesh(new THREE.BoxGeometry(4, 30, CHUNK_LENGTH), makeWallMat(col, 0.5));
            wall.position.set(side * 18, 7, 0);
            wall.receiveShadow = true;
            group.add(wall);
            obstacles.push(this.meshToObstacle(wall, 4));
        });
    }
    createSqueeze(group, obstacles, rng, diff) {
        const gs = Math.max(4, 10 - diff * 1.2);
        const gy = 2 + rng() * 5;
        const sz = (rng() - 0.5) * CHUNK_LENGTH * 0.4;
        const tx = (rng() - 0.5) * 4;
        const col = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
        const sm = makeWallMat(col, 0.5);
        const tw = new THREE.Mesh(new THREE.BoxGeometry(18, 3, 8 + diff * 2), sm);
        tw.position.set(tx, gy + gs / 2 + 1.5, sz);
        tw.castShadow = true;
        group.add(tw);
        obstacles.push(this.meshToObstacle(tw, 2));
        const bw = new THREE.Mesh(new THREE.BoxGeometry(18, 3, 8 + diff * 2), sm);
        bw.position.set(tx, gy - gs / 2 - 1.5, sz);
        bw.castShadow = true;
        group.add(bw);
        obstacles.push(this.meshToObstacle(bw, 2));
        const col2 = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
        const sm2 = makeWallMat(col2, 0.5);
        [-1, 1].forEach(side => {
            const sw = new THREE.Mesh(new THREE.BoxGeometry(2, gs + 4, 10), sm2);
            sw.position.set(side * 7, gy, sz);
            sw.castShadow = true;
            group.add(sw);
            obstacles.push(this.meshToObstacle(sw, 2));
        });
        [-1, 1].forEach(side => {
            const col3 = WALL_COLORS[Math.floor(rng() * WALL_COLORS.length)];
            const wall = new THREE.Mesh(new THREE.BoxGeometry(4, 30, CHUNK_LENGTH), makeWallMat(col3, 0.5));
            wall.position.set(side * 18, 7, 0);
            wall.receiveShadow = true;
            group.add(wall);
            obstacles.push(this.meshToObstacle(wall, 4));
        });
    }
    createOrbs(group, orbs, rng) {
        const count = 3 + Math.floor(rng() * 4);
        for (let i = 0; i < count; i++) {
            const om = new THREE.MeshStandardMaterial({
                color: 0xffcc00,
                flatShading: true,
            });
            om.emissive.set(0xffaa00);
            om.emissiveIntensity = 3;
            const mesh = new THREE.Mesh(new THREE.IcosahedronGeometry(0.5), om);
            const ox = (rng() - 0.5) * 16, oy = 1 + rng() * 7, oz = (rng() - 0.5) * CHUNK_LENGTH * 0.8;
            const lp = new THREE.Vector3(ox, oy, oz);
            mesh.position.copy(lp);
            const glow = new THREE.PointLight(0xffcc00, 2, 6);
            glow.position.copy(lp);
            group.add(glow);
            group.add(mesh);
            orbs.push({ mesh, collected: false, glow, localPos: lp });
        }
    }
    meshToObstacle(mesh, padding) {
        const box = new THREE.Box3().setFromObject(mesh);
        const center = new THREE.Vector3();
        box.getCenter(center);
        const sphere = new THREE.Sphere();
        box.getBoundingSphere(sphere);
        sphere.radius += padding;
        return { mesh, localSphere: new THREE.Sphere(sphere.center.clone(), sphere.radius), worldSphere: new THREE.Sphere(sphere.center.clone(), sphere.radius), nearMissRadius: sphere.radius + 3 };
    }
    groupToObstacle(group, cx, cy, cz, padding) {
        const box = new THREE.Box3().setFromObject(group);
        const center = new THREE.Vector3(cx, cy, cz);
        const radius = Math.max(Math.abs(box.max.x - box.min.x), Math.abs(box.max.y - box.min.y)) * 0.5 + padding;
        return { mesh: group, localSphere: new THREE.Sphere(center.clone(), radius), worldSphere: new THREE.Sphere(center, radius), nearMissRadius: radius + 3 };
    }
}
exports.ChunkGenerator = ChunkGenerator;
exports.chunkGenerator = new ChunkGenerator();
};

// ── module: src/particles.ts ──
__mods["src/particles.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpeedParticles = void 0;
// Speed lines / debris particles that fly past the player to emphasize speed
class SpeedParticles {
    constructor() {
        this.particles = null;
        this.count = 200;
        const geo = new THREE.BufferGeometry();
        this.positions = new Float32Array(this.count * 3);
        this.sizes = new Float32Array(this.count);
        for (let i = 0; i < this.count; i++) {
            this.resetParticle(i, true);
            this.sizes[i] = 0.5 + Math.random() * 1.5;
        }
        geo.setAttribute('position', new THREE.BufferAttribute(this.positions, 3));
        geo.setAttribute('size', new THREE.BufferAttribute(this.sizes, 1));
        const mat = new THREE.ShaderMaterial({
            uniforms: {
                uColor: { value: new THREE.Color(0xffeedd) },
            },
            vertexShader: `
        attribute float size;
        varying float vAlpha;
        void main() {
          vAlpha = size / 2.0;
          vec4 mvPos = modelViewMatrix * vec4(position, 1.0);
          gl_PointSize = size * 40.0 / -mvPos.z;
          gl_Position = projectionMatrix * mvPos;
        }
      `,
            fragmentShader: `
        uniform vec3 uColor;
        varying float vAlpha;
        void main() {
          float d = length(gl_PointCoord - 0.5) * 2.0;
          if (d > 1.0) discard;
          float a = smoothstep(1.0, 0.0, d) * vAlpha * 0.3;
          gl_FragColor = vec4(uColor, a);
        }
      `,
            transparent: true,
            blending: THREE.AdditiveBlending,
            depthWrite: false,
        });
        this.particles = new THREE.Points(geo, mat);
        this.particles.frustumCulled = false;
    }
    resetParticle(i, randomZ) {
        this.positions[i * 3] = (Math.random() - 0.5) * 40;
        this.positions[i * 3 + 1] = (Math.random() - 0.5) * 25 - 3;
        this.positions[i * 3 + 2] = randomZ ? -Math.random() * 80 : -80;
    }
    update(dt, playerPos, speed) {
        if (!this.particles)
            return;
        const s = speed * dt * 3;
        for (let i = 0; i < this.count; i++) {
            this.positions[i * 3 + 2] += s;
            if (this.positions[i * 3 + 2] > playerPos.z + 20) {
                this.resetParticle(i, false);
            }
        }
        this.particles.geometry.attributes.position.needsUpdate = true;
        this.particles.position.copy(playerPos);
    }
}
exports.SpeedParticles = SpeedParticles;
};

// ── module: src/collision.ts ──
__mods["src/collision.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.collision = exports.CollisionSystem = void 0;
class CollisionSystem {
    constructor() {
        this.playerPos = new THREE.Vector3();
        this.playerRadius = 0.5;
    }
    updatePlayer(pos) {
        this.playerPos.copy(pos);
    }
    checkCollision(obstacles) {
        const pSphere = new THREE.Sphere(this.playerPos, this.playerRadius);
        for (const obs of obstacles) {
            if (pSphere.intersectsSphere(obs.worldSphere)) {
                return true;
            }
        }
        return false;
    }
    checkNearMisses(obstacles) {
        let closestDist = Infinity;
        for (const obs of obstacles) {
            const dist = this.playerPos.distanceTo(obs.worldSphere.center);
            const collisionRadius = this.playerRadius + obs.worldSphere.radius - 0.5;
            if (dist < obs.nearMissRadius && dist > collisionRadius) {
                closestDist = Math.min(closestDist, dist);
            }
        }
        return {
            triggered: closestDist < Infinity,
            closestDist,
        };
    }
    checkOrbs(orbs, dt) {
        const collected = [];
        for (const orb of orbs) {
            if (orb.collected)
                continue;
            const dist = this.playerPos.distanceTo(orb.mesh.position);
            if (dist < this.playerRadius + 1.5) {
                orb.collected = true;
                collected.push(orb);
            }
        }
        return collected;
    }
}
exports.CollisionSystem = CollisionSystem;
exports.collision = new CollisionSystem();
};

// ── module: src/hud.ts ──
__mods["src/hud.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hud = exports.HUD = void 0;
// Minimalist HUD rendered as DOM overlay
const state_1 = require("./state");
class HUD {
    constructor() {
        this.container = document.createElement('div');
        this.container.id = 'hud';
        this.container.style.cssText = `
      position: fixed; top: 0; left: 0; width: 100%; height: 100%;
      pointer-events: none; font-family: 'Courier New', monospace;
      z-index: 100; color: #fff;
    `;
        // Speed bar (right side vertical)
        this.speedBar = document.createElement('div');
        this.speedBar.style.cssText = `
      position: absolute; right: 30px; top: 50%; transform: translateY(-50%);
      width: 4px; height: 200px; background: rgba(255,255,255,0.1);
      border-radius: 2px; overflow: hidden;
    `;
        const speedFill = document.createElement('div');
        speedFill.id = 'speed-fill';
        speedFill.style.cssText = `
      width: 100%; height: 50%; background: linear-gradient(to top, #ff6633, #ffcc33);
      border-radius: 2px; transition: height 0.1s ease-out; position: absolute; bottom: 0;
    `;
        this.speedBar.appendChild(speedFill);
        this.container.appendChild(this.speedBar);
        // Speed label
        const speedLabel = document.createElement('div');
        speedLabel.id = 'speed-label';
        speedLabel.style.cssText = `
      position: absolute; right: 20px; top: calc(50% + 115px);
      font-size: 11px; color: rgba(255,255,255,0.5); letter-spacing: 2px;
    `;
        speedLabel.textContent = 'SPD';
        this.container.appendChild(speedLabel);
        // Multiplier (center-top)
        this.multiplierEl = document.createElement('div');
        this.multiplierEl.style.cssText = `
      position: absolute; top: 30px; left: 50%; transform: translateX(-50%);
      font-size: 48px; font-weight: bold; color: #ffcc33;
      text-shadow: 0 0 20px rgba(255,204,51,0.5);
      transition: transform 0.1s, color 0.2s;
    `;
        this.multiplierEl.textContent = '×1';
        this.container.appendChild(this.multiplierEl);
        // Score
        this.scoreEl = document.createElement('div');
        this.scoreEl.style.cssText = `
      position: absolute; top: 85px; left: 50%; transform: translateX(-50%);
      font-size: 16px; color: rgba(255,255,255,0.7); letter-spacing: 3px;
    `;
        this.scoreEl.textContent = '0';
        this.container.appendChild(this.scoreEl);
        // Energy bar (bottom center)
        this.energyBar = document.createElement('div');
        this.energyBar.style.cssText = `
      position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%);
      width: 200px; height: 6px; background: rgba(255,255,255,0.1);
      border-radius: 3px; overflow: hidden;
    `;
        const energyFill = document.createElement('div');
        energyFill.id = 'energy-fill';
        energyFill.style.cssText = `
      width: 100%; height: 100%;
      background: linear-gradient(to right, #ff3333, #ffcc33, #33ff66);
      border-radius: 3px; transition: width 0.1s ease-out;
    `;
        this.energyBar.appendChild(energyFill);
        this.container.appendChild(this.energyBar);
        // Energy label
        const energyLabel = document.createElement('div');
        energyLabel.style.cssText = `
      position: absolute; bottom: 42px; left: 50%; transform: translateX(-50%);
      font-size: 10px; color: rgba(255,255,255,0.4); letter-spacing: 2px;
    `;
        energyLabel.textContent = 'ENERGY';
        this.container.appendChild(energyLabel);
        // Boost bar (below energy)
        this.boostBar = document.createElement('div');
        this.boostBar.style.cssText = `
      position: absolute; bottom: 55px; left: 50%; transform: translateX(-50%);
      width: 140px; height: 4px; background: rgba(255,255,255,0.08);
      border-radius: 2px; overflow: hidden;
    `;
        const boostFill = document.createElement('div');
        boostFill.id = 'boost-fill';
        boostFill.style.cssText = `
      width: 0%; height: 100%;
      background: linear-gradient(to right, #00ccff, #00ffaa);
      border-radius: 2px; transition: width 0.1s ease-out;
    `;
        this.boostBar.appendChild(boostFill);
        this.container.appendChild(this.boostBar);
        // Boost label
        const boostLabel = document.createElement('div');
        boostLabel.style.cssText = `
      position: absolute; bottom: 67px; left: 50%; transform: translateX(-50%);
      font-size: 10px; color: rgba(255,255,255,0.4); letter-spacing: 2px;
    `;
        boostLabel.textContent = 'TURBO';
        this.container.appendChild(boostLabel);
        // Near miss flash
        this.nearMissFlash = document.createElement('div');
        this.nearMissFlash.style.cssText = `
      position: absolute; inset: 0; background: rgba(255,204,51,0.15);
      opacity: 0; transition: opacity 0.05s; pointer-events: none;
    `;
        this.container.appendChild(this.nearMissFlash);
        // Game over overlay
        this.gameOverOverlay = document.createElement('div');
        this.gameOverOverlay.style.cssText = `
      position: absolute; inset: 0; background: rgba(0,0,0,0.85);
      display: none; flex-direction: column; align-items: center; justify-content: center;
      pointer-events: auto;
    `;
        const title = document.createElement('div');
        title.style.cssText = 'font-size: 48px; color: #ff6633; margin-bottom: 30px; letter-spacing: 6px;';
        title.textContent = 'WIPED OUT';
        this.gameOverOverlay.appendChild(title);
        this.gameOverScore = document.createElement('div');
        this.gameOverScore.style.cssText = 'font-size: 24px; color: #ffcc33; margin-bottom: 10px; letter-spacing: 2px;';
        this.gameOverOverlay.appendChild(this.gameOverScore);
        this.gameOverMaxSpeed = document.createElement('div');
        this.gameOverMaxSpeed.style.cssText = 'font-size: 16px; color: rgba(255,255,255,0.6); margin-bottom: 30px; letter-spacing: 2px;';
        this.gameOverOverlay.appendChild(this.gameOverMaxSpeed);
        const restartBtn = document.createElement('button');
        restartBtn.id = 'restart-btn';
        restartBtn.style.cssText = `
      font-family: 'Courier New', monospace; font-size: 18px; color: #fff;
      background: transparent; border: 2px solid #ff6633; padding: 12px 40px;
      cursor: pointer; letter-spacing: 3px; pointer-events: auto;
      transition: all 0.2s;
    `;
        restartBtn.textContent = 'RESTART';
        restartBtn.addEventListener('mouseenter', () => {
            restartBtn.style.background = 'rgba(255,102,51,0.3)';
        });
        restartBtn.addEventListener('mouseleave', () => {
            restartBtn.style.background = 'transparent';
        });
        restartBtn.addEventListener('click', () => {
            window.dispatchEvent(new CustomEvent('restart'));
        });
        this.gameOverOverlay.appendChild(restartBtn);
        this.container.appendChild(this.gameOverOverlay);
        // Start overlay
        this.startOverlay = document.createElement('div');
        this.startOverlay.style.cssText = `
      position: absolute; inset: 0; background: rgba(0,0,0,0.9);
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      pointer-events: auto;
    `;
        const startTitle = document.createElement('div');
        startTitle.style.cssText = 'font-size: 56px; color: #ff6633; margin-bottom: 10px; letter-spacing: 8px; text-shadow: 0 0 30px rgba(255,102,51,0.5);';
        startTitle.textContent = 'CANYON RACER';
        this.startOverlay.appendChild(startTitle);
        const startSub = document.createElement('div');
        startSub.style.cssText = 'font-size: 14px; color: rgba(255,255,255,0.5); margin-bottom: 40px; letter-spacing: 3px;';
        startSub.textContent = 'HIGH-SPEED PROCEDURAL RUN';
        this.startOverlay.appendChild(startSub);
        const startControls = document.createElement('div');
        startControls.style.cssText = 'font-size: 13px; color: rgba(255,255,255,0.4); margin-bottom: 30px; line-height: 2; text-align: center; letter-spacing: 1px;';
        startControls.innerHTML = 'WASD / ARROWS — MOVE<br>SPACE / SHIFT — BOOST<br>COLLECT ORBS — STAY FUELED';
        this.startOverlay.appendChild(startControls);
        const startBtn = document.createElement('button');
        startBtn.id = 'start-btn';
        startBtn.style.cssText = `
      font-family: 'Courier New', monospace; font-size: 20px; color: #ffcc33;
      background: transparent; border: 2px solid #ffcc33; padding: 14px 50px;
      cursor: pointer; letter-spacing: 4px; pointer-events: auto;
      transition: all 0.2s;
    `;
        startBtn.textContent = 'LAUNCH';
        startBtn.addEventListener('mouseenter', () => {
            startBtn.style.background = 'rgba(255,204,51,0.2)';
        });
        startBtn.addEventListener('mouseleave', () => {
            startBtn.style.background = 'transparent';
        });
        startBtn.addEventListener('click', () => {
            window.dispatchEvent(new CustomEvent('start'));
            this.startOverlay.style.display = 'none';
        });
        this.startOverlay.appendChild(startBtn);
        this.container.appendChild(this.startOverlay);
        document.body.appendChild(this.container);
    }
    update() {
        if (state_1.state.gameOver) {
            this.gameOverOverlay.style.display = 'flex';
            this.gameOverScore.textContent = `SCORE: ${Math.floor(state_1.state.score).toLocaleString()}`;
            this.gameOverMaxSpeed.textContent = `MAX SPEED: ${Math.floor(state_1.state.maxSpeed * 20)} KM/H`;
            return;
        }
        // Speed fill
        const speedFill = document.getElementById('speed-fill');
        if (speedFill) {
            const pct = Math.min(1, state_1.state.speed / 40);
            speedFill.style.height = `${pct * 100}%`;
        }
        // Multiplier
        this.multiplierEl.textContent = `×${state_1.state.multiplier}`;
        if (state_1.state.multiplier > 1) {
            const scale = 1 + Math.sin(state_1.state.nearMissPulse * 10) * 0.1;
            this.multiplierEl.style.transform = `translateX(-50%) scale(${scale})`;
            this.multiplierEl.style.color = state_1.state.multiplier > 3 ? '#ff6633' : '#ffcc33';
        }
        else {
            this.multiplierEl.style.transform = 'translateX(-50%) scale(1)';
            this.multiplierEl.style.color = '#ffcc33';
        }
        // Score
        this.scoreEl.textContent = `${Math.floor(state_1.state.score).toLocaleString()}`;
        // Energy
        const energyFill = document.getElementById('energy-fill');
        if (energyFill) {
            const pct = Math.max(0, state_1.state.energy / state_1.state.maxEnergy);
            energyFill.style.width = `${pct * 100}%`;
            if (pct < 0.2) {
                energyFill.style.background = '#ff3333';
            }
            else if (pct < 0.5) {
                energyFill.style.background = '#ffcc33';
            }
            else {
                energyFill.style.background = 'linear-gradient(to right, #ff3333, #ffcc33, #33ff66)';
            }
        }
        // Boost
        const boostFill = document.getElementById('boost-fill');
        if (boostFill) {
            const pct = Math.min(1, state_1.state.boost / state_1.state.maxBoost);
            boostFill.style.width = `${pct * 100}%`;
        }
        // Near miss flash
        if (state_1.state.nearMissPulse > 0) {
            this.nearMissFlash.style.opacity = `${state_1.state.nearMissPulse * 0.5}`;
        }
        else {
            this.nearMissFlash.style.opacity = '0';
        }
    }
    showGameOver() {
        this.gameOverOverlay.style.display = 'flex';
    }
    hideGameOver() {
        this.gameOverOverlay.style.display = 'none';
    }
    hideStart() {
        this.startOverlay.style.display = 'none';
    }
}
exports.HUD = HUD;
exports.hud = new HUD();
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"unsloth/Qwen3.6-27B-MTP-GGUF-UD-Q5_K_XL","provider":"brigid","persona":"typescript","sessionId":"1171bd06-7305-4452-8d98-31cd4cf2bb64","tokensIn":9734212,"tokensOut":90170,"tokensTotal":9824382,"cost":0,"turns":127,"toolCalls":174,"failedToolCalls":16,"timestamp":"2026-08-04T11:49:02.549Z"} -->