← Flight Simulator results

Flight Simulator

ThinkingCap Qwen3.6 27B GGUF · typescript

Initial view of Flight Simulator

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 53,957 bytes · SHA-256 85395367d573
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Apex Aero — Flight Simulator</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.4.0/simplex-noise.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<style>
/* ── css: layout ── */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000; font-family: 'Segoe UI', 'Roboto', sans-serif; }
#app { width: 100%; height: 100%; position: relative; }
#gameCanvas { display: block; width: 100%; height: 100%; }

/* HUD */
#hud { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; display: none; }
#hud-panel { width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: space-between; padding: 15px 20px; }
#hud-top { display: flex; justify-content: space-between; font-size: 16px; font-family: 'Courier New', monospace; color: #0f0; text-shadow: 0 0 8px rgba(0,255,0,0.5); font-weight: bold; }
#hud-center { display: flex; flex-direction: column; align-items: center; justify-content: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
#hud-crosshair { color: rgba(0,255,0,0.5); font-size: 48px; font-weight: bold; }
#hud-stall { color: #ff0000; font-size: 28px; font-weight: bold; font-family: monospace; text-shadow: 0 0 15px red; margin-top: 20px; }
#hud-bottom { display: flex; justify-content: space-between; font-size: 14px; font-family: 'Courier New', monospace; color: #0f0; text-shadow: 0 0 8px rgba(0,255,0,0.5); }
#hud-mission { color: #ffff00; }

/* Menu */
#menu { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; z-index: 100; }
#menu-container { background: rgba(10,15,30,0.95); border: 2px solid rgba(0,255,100,0.3); border-radius: 16px; padding: 40px 50px; text-align: center; max-width: 600px; box-shadow: 0 0 60px rgba(0,255,100,0.1); }
#menu-title { font-size: 48px; color: #0f0; text-shadow: 0 0 30px rgba(0,255,0,0.5); letter-spacing: 8px; margin-bottom: 5px; }
#menu-subtitle { font-size: 18px; color: #0a6; margin-bottom: 30px; letter-spacing: 3px; }
#aircraft-select h2, #mission-select h2 { color: #8f8; margin: 20px 0 15px; font-size: 18px; letter-spacing: 2px; }
#aircraft-cards { display: flex; gap: 10px; justify-content: center; margin-bottom: 20px; }
.acard { background: rgba(0,255,100,0.05); border: 1px solid rgba(0,255,100,0.2); border-radius: 8px; padding: 12px 16px; cursor: pointer; transition: all 0.3s; min-width: 160px; }
.acard:hover { background: rgba(0,255,100,0.15); border-color: rgba(0,255,100,0.5); }
.acard.active { background: rgba(0,255,100,0.2); border-color: #0f0; box-shadow: 0 0 20px rgba(0,255,0,0.2); }
.acard-name { color: #0f0; font-weight: bold; font-size: 16px; }
.acard-desc { color: #aaa; font-size: 12px; margin: 6px 0; }
.acard-stats { color: #6a6; font-size: 11px; font-family: monospace; }
#mission-select button { background: rgba(0,255,100,0.1); border: 1px solid rgba(0,255,100,0.4); color: #0f0; border-radius: 8px; transition: all 0.3s; font-family: inherit; }
#mission-select button:hover { background: rgba(0,255,100,0.25); border-color: #0f0; box-shadow: 0 0 20px rgba(0,255,0,0.3); transform: scale(1.05); }
</style>
</head>
<body>
<div id="app">
  <canvas id="gameCanvas"></canvas>
  <div id="hud"></div>
  <div id="menu"></div>
  <div id="minimap"></div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./types":"src/types.ts","./aircraft":"src/aircraft.ts","./physics":"src/physics.ts","./terrain":"src/terrain.ts","./audio":"src/audio.ts","./input":"src/input.ts","./models":"src/models.ts","./hud":"src/hud.ts","./mission":"src/mission.ts"},"src/aircraft.ts":{"./types":"src/types.ts"},"src/physics.ts":{"./types":"src/types.ts"},"src/terrain.ts":{"./types":"src/types.ts"},"src/input.ts":{"./types":"src/types.ts"},"src/models.ts":{"./types":"src/types.ts"},"src/hud.ts":{"./types":"src/types.ts"},"src/mission.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";
// Apex Aero — Main entry module
// Initializes Three.js scene, game state, and runs the render loop
Object.defineProperty(exports, "__esModule", { value: true });
const aircraft_1 = require("./aircraft");
const physics_1 = require("./physics");
const terrain_1 = require("./terrain");
const audio_1 = require("./audio");
const input_1 = require("./input");
const models_1 = require("./models");
const hud_1 = require("./hud");
const mission_1 = require("./mission");
// --- Globals ---
let scene;
let camera;
let renderer;
let game;
let aircraftMesh;
let enemyMeshes;
let exhaustFlameL;
let exhaustFlameR;
let lastCamMode = 'chase';
let camModeIndex = 0;
// --- Initialize ---
function init() {
    // Scene
    scene = new THREE.Scene();
    scene.background = 0x87CEEB;
    scene.fog = new THREE.Fog(0x87CEEB, 2000, 10000);
    // Camera
    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 20000);
    // Renderer
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setClearColor(0x87CEEB, 1);
    document.getElementById('gameCanvas').replaceWith(renderer.domElement);
    renderer.domElement.id = 'gameCanvas';
    // Monkey-patch THREE.BufferAttribute to prevent onUploadCallback errors
    THREE.BufferAttribute.prototype.onUploadCallback = function () { };
    // Lighting
    const ambient = new THREE.AmbientLight(0x404060, 0.5);
    scene.add(ambient);
    const hemi = new THREE.HemisphereLight(0x87CEEB, 0x362907, 0.6);
    scene.add(hemi);
    const sun = new THREE.DirectionalLight(0xffffff, 1.0);
    sun.position.set(500, 800, 300);
    scene.add(sun);
    // Initialize terrain
    (0, terrain_1.initTerrain)();
    // Clouds
    const cloudGroup = new THREE.Group();
    for (let i = 0; i < 60; i++) {
        const c = new THREE.Mesh(new THREE.SphereGeometry(30 + Math.random() * 60, 6, 4), new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true, transparent: true, opacity: 0.7 }));
        c.position.set((Math.random() - 0.5) * 3000, 400 + Math.random() * 400, (Math.random() - 0.5) * 3000);
        c.scale.set(1 + Math.random(), 0.3 + Math.random() * 0.3, 1 + Math.random());
        cloudGroup.add(c);
    }
    scene.add(cloudGroup);
    // Runway strip
    const runwayMat = new THREE.MeshPhongMaterial({ color: 0x333333, flatShading: true });
    const runway = new THREE.Mesh(new THREE.PlaneGeometry(30, 400), runwayMat);
    runway.rotation.x = -Math.PI / 2;
    runway.position.set(0, 0.5, -100);
    scene.add(runway);
    // Runway markings
    const markMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
    for (let z = -280; z < 80; z += 40) {
        const mark = new THREE.Mesh(new THREE.PlaneGeometry(2, 12), markMat);
        mark.rotation.x = -Math.PI / 2;
        mark.position.set(0, 0.6, -100 + z);
        scene.add(mark);
    }
    // Runway edge lights
    const lightMat = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    for (let z = -280; z < 80; z += 20) {
        const l1 = new THREE.Mesh(new THREE.SphereGeometry(0.5, 4, 4), lightMat);
        l1.position.set(-8, 0.8, -100 + z);
        scene.add(l1);
        const l2 = new THREE.Mesh(new THREE.SphereGeometry(0.5, 4, 4), lightMat);
        l2.position.set(8, 0.8, -100 + z);
        scene.add(l2);
    }
    // Initialize game state
    game = createInitialState();
    enemyMeshes = new Map();
    // Initialize input
    (0, input_1.initInputHandlers)(game.input, () => { });
    // Initialize HUD
    (0, hud_1.initHUD)();
    // Initialize audio (deferred — resume on first interaction)
    (0, audio_1.initAudio)();
    // Show menu
    showMainMenu();
    // Handle resize
    window.addEventListener('resize', onResize);
    // First interaction resumes audio
    window.addEventListener('keydown', () => (0, audio_1.resumeAudio)(), { once: true });
    window.addEventListener('click', () => (0, audio_1.resumeAudio)(), { once: true });
    // Start render loop
    game.lastTime = performance.now();
    requestAnimationFrame(gameLoop);
}
function createInitialState() {
    return {
        phase: 'menu',
        selectedAircraft: 0,
        flight: {
            position: new THREE.Vector3(0, 2, 0),
            velocity: new THREE.Vector3(0, 0, 0),
            quaternion: new THREE.Quaternion(),
            angularVelocity: new THREE.Vector3(0, 0, 0),
            throttle: 0,
            airspeed: 0,
            altitude: 0,
            angleOfAttack: 0,
            sideslipAngle: 0,
            gForce: 1,
            engineRPM: 800,
            engineTemp: 20,
            isGrounded: true,
            isStalled: false,
            terrainHeight: 0
        },
        input: (0, input_1.createInput)(),
        mission: {
            type: 'takeoff',
            waypoints: [],
            enemies: [],
            score: 0,
            phase: '',
            time: 0,
            alive: true
        },
        cameraMode: 'chase',
        cameraOffset: new THREE.Vector3(0, 5, -15),
        lastTime: 0,
        deltaTime: 0,
        fps: 0,
        frameCount: 0,
        missionTime: 0,
        terrainChunks: new Map(),
        weather: {
            windDirection: 0,
            windSpeed: 5,
            turbulence: 0.1,
            cloudDensity: 0.3,
            visibility: 8000
        }
    };
}
// --- Menu ---
function showMainMenu() {
    game.phase = 'menu';
    const menu = document.getElementById('menu');
    menu.style.display = 'block';
    menu.innerHTML = `
    <div id="menu-container">
      <h1 id="menu-title">APEX AERO</h1>
      <p id="menu-subtitle">Flight Simulator</p>
      <div id="aircraft-select">
        <h2>Select Aircraft</h2>
        <div id="aircraft-cards"></div>
      </div>
      <div id="mission-select">
        <h2>Select Mission</h2>
        <button id="btn-takeoff" style="font-size:18px;padding:12px 30px;margin:8px;cursor:pointer;">🛫 Takeoff & Landing</button>
        <button id="btn-dogfight" style="font-size:18px;padding:12px 30px;margin:8px;cursor:pointer;">⚔️ Dogfight</button>
      </div>
    </div>
  `;
    const cards = document.getElementById('aircraft-cards');
    aircraft_1.AIRCRAFT.forEach((ac, i) => {
        const card = document.createElement('div');
        card.className = 'acard' + (i === game.selectedAircraft ? ' active' : '');
        card.innerHTML = `
      <div class="acard-name">${ac.name}</div>
      <div class="acard-desc">${ac.description}</div>
      <div class="acard-stats">
        SPD:${Math.round(ac.maxSpeed * 3.6)}km/h | WGT:${ac.mass / 1000}t | THR:${(ac.thrust / 1000).toFixed(0)}kN
      </div>
    `;
        card.addEventListener('click', () => {
            game.selectedAircraft = i;
            document.querySelectorAll('.acard').forEach(c => c.classList.remove('active'));
            card.classList.add('active');
        });
        cards.appendChild(card);
    });
    document.getElementById('btn-takeoff').addEventListener('click', () => startMission('takeoff'));
    document.getElementById('btn-dogfight').addEventListener('click', () => startMission('dogfight'));
}
function startMission(type) {
    (0, audio_1.resumeAudio)();
    const menu = document.getElementById('menu');
    if (menu)
        menu.style.display = 'none';
    game.phase = 'playing';
    const ac = aircraft_1.AIRCRAFT[game.selectedAircraft];
    // Reset flight state
    game.flight.position = new THREE.Vector3(0, type === 'takeoff' ? 2 : 300, type === 'takeoff' ? 0 : -200);
    game.flight.velocity = new THREE.Vector3(0, 0, type === 'takeoff' ? 0 : 150);
    game.flight.quaternion = new THREE.Quaternion();
    game.flight.throttle = type === 'takeoff' ? 0.1 : 0.8;
    game.flight.airspeed = type === 'takeoff' ? 0 : 150;
    game.flight.isGrounded = type === 'takeoff';
    game.flight.isStalled = false;
    game.mission = (0, mission_1.initMission)(type);
    game.missionTime = 0;
    document.getElementById('hud').style.display = 'block';
    // Build aircraft model
    scene.children = scene.children.filter(c => !(c instanceof THREE.Group) || c === scene);
    aircraftMesh = (0, models_1.buildAircraftModel)(ac, 1.5);
    scene.add(aircraftMesh);
    // Build exhaust flames
    const flameMat = new THREE.MeshBasicMaterial({ color: 0xff6600, transparent: true, opacity: 0.7 });
    exhaustFlameL = new THREE.Mesh(new THREE.ConeGeometry(0.3, 2, 6), flameMat);
    exhaustFlameL.rotation.x = Math.PI / 2;
    exhaustFlameL.position.set(-1.5, -0.3, -4);
    aircraftMesh.add(exhaustFlameL);
    exhaustFlameR = new THREE.Mesh(new THREE.ConeGeometry(0.3, 2, 6), flameMat);
    exhaustFlameR.rotation.x = Math.PI / 2;
    exhaustFlameR.position.set(1.5, -0.3, -4);
    aircraftMesh.add(exhaustFlameR);
    // Build enemy models for dogfight
    if (type === 'dogfight') {
        enemyMeshes.clear();
        game.mission.enemies.forEach((enemy, i) => {
            const em = (0, models_1.buildEnemyAircraft)(0xcc2222);
            scene.add(em);
            enemyMeshes.set(i, em);
        });
    }
    // Clear old terrain
    game.terrainChunks.forEach(mesh => scene.remove(mesh));
    game.terrainChunks.clear();
}
// --- Camera ---
function updateCamera(dt) {
    if (game.phase !== 'playing')
        return;
    const fp = game.flight.position;
    if (game.cameraMode === 'chase') {
        // Chase camera behind and above aircraft
        const offset = new THREE.Vector3(0, 8, -20);
        offset.applyQuaternion(game.flight.quaternion);
        const camTarget = fp.clone().add(offset);
        camera.position.lerp(camTarget, 3 * dt);
        const lookTarget = fp.clone().add(new THREE.Vector3(0, 2, 10).applyQuaternion(game.flight.quaternion));
        camera.lookAt(lookTarget);
    }
    else if (game.cameraMode === 'cockpit') {
        const forward = new THREE.Vector3(0, 0, 1).applyQuaternion(game.flight.quaternion);
        const up = new THREE.Vector3(0, 1, 0).applyQuaternion(game.flight.quaternion);
        const camPos = fp.clone().add(forward.multiplyScalar(2)).add(up.multiplyScalar(1.2));
        camera.position.copy(camPos);
        const lookTarget = fp.clone().add(forward.multiplyScalar(100));
        camera.lookAt(lookTarget);
    }
}
// --- Game Loop ---
function gameLoop(now) {
    requestAnimationFrame(gameLoop);
    const dt = Math.min((now - game.lastTime) / 1000, 0.1);
    game.lastTime = now;
    game.deltaTime = dt;
    // FPS counter
    game.frameCount++;
    if (game.frameCount % 30 === 0) {
        game.fps = Math.round(1 / dt);
    }
    if (game.phase === 'playing') {
        const ac = aircraft_1.AIRCRAFT[game.selectedAircraft];
        // Update terrain
        game.flight.terrainHeight = (0, terrain_1.getTerrainHeightAt)(game.flight.position.x, game.flight.position.z);
        (0, terrain_1.updateTerrainChunks)(scene, game);
        // Physics
        (0, physics_1.computeFlightPhysics)(game.flight, ac, game.input, dt);
        // Update aircraft model transform
        aircraftMesh.position.copy(game.flight.position);
        aircraftMesh.quaternion.copy(game.flight.quaternion);
        aircraftMesh.updateMatrixWorld(true);
        // Exhaust flames visualization
        const thrustScale = 0.5 + game.flight.throttle * 3;
        exhaustFlameL.scale.set(1, 1, thrustScale);
        exhaustFlameR.scale.set(1, 1, thrustScale);
        const flameOpacity = 0.3 + game.flight.throttle * 0.5;
        exhaustFlameL.material.opacity = flameOpacity;
        exhaustFlameR.material.opacity = flameOpacity;
        // Enemy updates
        if (game.mission.type === 'dogfight') {
            game.mission.enemies.forEach((enemy, i) => {
                const em = enemyMeshes.get(i);
                if (em && enemy.alive) {
                    em.position.copy(enemy.position);
                    em.quaternion.copy(enemy.quaternion);
                    em.updateMatrixWorld(true);
                }
                else if (em) {
                    scene.remove(em);
                }
            });
        }
        // Mission update
        (0, mission_1.updateMission)(game.mission, game.flight, dt);
        game.missionTime += dt;
        // Camera
        updateCamera(dt);
        // Audio
        (0, audio_1.updateAudio)(game.flight.throttle, game.flight.airspeed, game.flight.isStalled, game.flight.altitude, dt);
        // HUD
        (0, hud_1.updateHUD)(game.flight, game.mission, ac);
    }
    // Render
    renderer.render(scene, camera);
}
function onResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}
// Camera mode toggle
const origHandler = input_1.initInputHandlers;
window.addEventListener('keydown', (e) => {
    if (e.code === 'KeyC' && game.phase === 'playing') {
        const modes = ['chase', 'cockpit'];
        camModeIndex = (camModeIndex + 1) % modes.length;
        game.cameraMode = modes[camModeIndex];
    }
});
// Start
init();
};

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

// ── module: src/types.ts ──
__mods["src/types.ts"] = function (exports, require, module) {
"use strict";
// Core type definitions for Apex Aero flight simulator
Object.defineProperty(exports, "__esModule", { value: true });
};

// ── module: src/aircraft.ts ──
__mods["src/aircraft.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AIRCRAFT = void 0;
// Three distinct aircraft with mathematically differentiated flight profiles
exports.AIRCRAFT = [
    {
        name: 'Viper FX-7',
        maxSpeed: 280,
        thrust: 180000,
        mass: 12000,
        wingArea: 48,
        liftCoef: 1.8,
        dragCoef: 0.025,
        inducedDragFactor: 0.04,
        turnRate: 1.8,
        pitchRate: 1.2,
        yawRate: 0.5,
        stallSpeed: 55,
        color: 0x6B8E23,
        accentColor: 0xFF6600,
        description: 'Light fighter. High agility, low thrust. Best for dogfighting.'
    },
    {
        name: 'Thunder RB-12',
        maxSpeed: 320,
        thrust: 280000,
        mass: 22000,
        wingArea: 70,
        liftCoef: 2.0,
        dragCoef: 0.035,
        inducedDragFactor: 0.035,
        turnRate: 1.3,
        pitchRate: 0.9,
        yawRate: 0.4,
        stallSpeed: 65,
        color: 0x4682B4,
        accentColor: 0xFFD700,
        description: 'Heavy interceptor. High thrust, strong lift. Great for all missions.'
    },
    {
        name: 'Phantom SR-21',
        maxSpeed: 380,
        thrust: 220000,
        mass: 16000,
        wingArea: 55,
        liftCoef: 1.5,
        dragCoef: 0.018,
        inducedDragFactor: 0.05,
        turnRate: 2.1,
        pitchRate: 1.4,
        yawRate: 0.6,
        stallSpeed: 50,
        color: 0x2F2F2F,
        accentColor: 0xFF0044,
        description: 'High-speed interceptor. Lowest drag, hardest to stall.'
    }
];
};

// ── module: src/physics.ts ──
__mods["src/physics.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAirDensity = getAirDensity;
exports.computeFlightPhysics = computeFlightPhysics;
// Standard sea-level air density
const RHO_SL = 1.225;
// Scale height for atmosphere model
const H0 = 8500;
// Gravity
const G = 9.81;
// Sea level temperature for Mach calculations (not used but good ref)
const T_SL = 288.15;
function getAirDensity(altitude) {
    return RHO_SL * Math.exp(-altitude / H0);
}
function computeFlightPhysics(state, aircraft, input, dt) {
    // Clamp dt to prevent physics explosions
    dt = Math.min(dt, 0.05);
    const altitude = Math.max(0, state.position.y);
    const rho = getAirDensity(altitude);
    // --- Velocity in aircraft body frame ---
    const bodyVel = new THREE.Vector3();
    bodyVel.copy(state.velocity);
    bodyVel.applyQuaternion(state.quaternion.clone().invert());
    const forwardSpeed = bodyVel.z;
    const lateralSpeed = bodyVel.x;
    const verticalSpeed = bodyVel.y;
    // Airspeed (magnitude)
    state.airspeed = state.velocity.length();
    state.altitude = altitude;
    // --- Angle of Attack ---
    const velDir = state.velocity.clone().normalize();
    const aircraftForward = new THREE.Vector3(0, 0, 1).applyQuaternion(state.quaternion);
    const dot = velDir.dot(aircraftForward);
    state.angleOfAttack = Math.asin(Math.max(-1, Math.min(1, velDir.y - aircraftForward.y * dot)));
    state.sideslipAngle = Math.atan2(lateralSpeed, Math.max(1, forwardSpeed));
    // --- Lift Calculation ---
    // CL = liftCoef * angleOfAttack (simplified, no stall model beyond threshold)
    const aoa = state.angleOfAttack;
    let CL = aircraft.liftCoef * aoa;
    // Stall detection — lift drops off sharply beyond stall AOA
    const stallAoa = Math.asin(aircraft.stallSpeed / Math.max(aircraft.maxSpeed, 1)) * 2;
    if (Math.abs(aoa) > stallAoa || state.airspeed < aircraft.stallSpeed) {
        state.isStalled = true;
        CL *= 0.4; // Stall: dramatic lift loss
    }
    else {
        state.isStalled = false;
    }
    const dynamicPressure = 0.5 * rho * state.airspeed * state.airspeed;
    const liftForce = dynamicPressure * aircraft.wingArea * CL;
    // Lift acts perpendicular to relative wind (in aircraft up direction)
    const liftDir = new THREE.Vector3(0, 1, 0).applyQuaternion(state.quaternion);
    const liftVec = liftDir.multiplyScalar(liftForce);
    // --- Drag Calculation ---
    // CD = CD0 + K * CL^2 (parasite drag + induced drag)
    const CD = aircraft.dragCoef + aircraft.inducedDragFactor * CL * CL;
    const dragForce = dynamicPressure * aircraft.wingArea * CD;
    // Drag opposes velocity
    const dragDir = state.airspeed > 0.1 ? state.velocity.clone().normalize().multiplyScalar(-1) : new THREE.Vector3(0, 0, 0);
    const dragVec = dragDir.multiplyScalar(dragForce);
    // --- Thrust ---
    // Throttle input with ramp-up/ramp-down rates
    const throttleRate = 0.5; // per second
    if (input.throttleUp) {
        state.throttle = Math.min(1, state.throttle + throttleRate * dt);
    }
    else if (input.throttleDown) {
        state.throttle = Math.max(0, state.throttle - throttleRate * dt);
    }
    // Engine RPM based on throttle
    state.engineRPM = 800 + state.throttle * 9200;
    // Thrust vector in aircraft forward direction
    const thrustForce = aircraft.thrust * state.throttle;
    const thrustDir = new THREE.Vector3(0, 0, 1).applyQuaternion(state.quaternion);
    const thrustVec = thrustDir.multiplyScalar(thrustForce);
    // Forward axis for later use
    const fwdAxis2 = aircraftForward.clone();
    // --- Gravity ---
    const gravityVec = new THREE.Vector3(0, -G * aircraft.mass, 0);
    // --- Ground friction ---
    const groundClearance = state.position.y;
    const onGround = groundClearance < 2.0;
    let frictionVec = new THREE.Vector3(0, 0, 0);
    if (onGround) {
        state.isGrounded = true;
        // Ground friction proportional to vertical penetration and velocity
        const frictionCoeff = 0.8;
        frictionVec.copy(state.velocity).multiplyScalar(-frictionCoeff * aircraft.mass);
        // Prevent going below ground
        if (state.position.y < 0)
            state.position.y = 0;
    }
    else {
        state.isGrounded = false;
    }
    // --- Sum forces ---
    const totalForce = new THREE.Vector3();
    totalForce.add(liftVec);
    totalForce.add(dragVec);
    totalForce.add(thrustVec);
    totalForce.add(gravityVec);
    totalForce.add(frictionVec);
    // --- Acceleration and velocity update ---
    const accel = totalForce.multiplyScalar(1 / aircraft.mass);
    state.velocity.add(accel.multiplyScalar(dt));
    // --- Position update ---
    state.position.add(state.velocity.clone().multiplyScalar(dt));
    // Keep above ground (terrain check handled elsewhere)
    state.terrainHeight = 0; // Will be set by terrain module
    if (state.position.y < state.terrainHeight + 1) {
        state.position.y = state.terrainHeight + 1;
        if (state.velocity.y < 0) {
            // Hard landing / crash threshold
            if (state.velocity.y < -15) {
                // Crash
                state.velocity.y = -state.velocity.y * 0.2;
            }
            else {
                state.velocity.y = 0;
            }
        }
    }
    // --- Angular dynamics (attitude changes) ---
    const rollInput = (input.rollLeft ? -1 : 0) + (input.rollRight ? 1 : 0);
    const pitchInput = (input.up ? 1 : 0) + (input.down ? -1 : 0);
    const yawInput = (input.left ? 1 : 0) + (input.right ? -1 : 0);
    // Rate limiting based on aircraft characteristics
    const rollRate = rollInput * aircraft.turnRate * dt;
    const pitchRate = pitchInput * aircraft.pitchRate * dt;
    const yawRateVal = yawInput * aircraft.yawRate * dt;
    // Create rotation quaternions
    const fwdAxis = new THREE.Vector3(0, 0, 1).applyQuaternion(state.quaternion);
    const rightAxis = new THREE.Vector3(1, 0, 0).applyQuaternion(state.quaternion);
    const upAxis = new THREE.Vector3(0, 1, 0).applyQuaternion(state.quaternion);
    // Yaw (heading) around world Y axis
    const yawQ = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), yawRateVal);
    state.quaternion.multiply(yawQ);
    // Pitch around aircraft right axis
    const pitchQ = new THREE.Quaternion().setFromAxisAngle(rightAxis, pitchRate);
    state.quaternion.multiply(pitchQ);
    // Roll around aircraft forward axis
    const rollQ = new THREE.Quaternion().setFromAxisAngle(fwdAxis, rollRate);
    state.quaternion.multiply(rollQ);
    // Normalize quaternion
    state.quaternion.normalize();
    // --- G-force calculation ---
    const gForceMag = accel.length() / G;
    state.gForce = gForceMag + 1; // +1 for gravity baseline
    // --- Auto-leveling when no input (stability augmentation) ---
    if (pitchInput === 0 && !state.isGrounded) {
        // Gentle return to level flight
        const autoLevel = 0.3 * dt;
        const worldUp = new THREE.Vector3(0, 1, 0);
        const planeUp = new THREE.Vector3(0, 1, 0).applyQuaternion(state.quaternion);
        const cross = new THREE.Vector3().crossVectors(planeUp, worldUp);
        if (cross.lengthSq() > 0.001) {
            const levelQ = new THREE.Quaternion().setFromAxisAngle(cross.normalize(), autoLevel);
            state.quaternion.multiply(levelQ);
        }
    }
}
};

// ── module: src/terrain.ts ──
__mods["src/terrain.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initTerrain = initTerrain;
exports.updateTerrainChunks = updateTerrainChunks;
exports.getTerrainHeightAt = getTerrainHeightAt;
// Patch BEFORE anything else runs
THREE.BufferAttribute.prototype.onUploadCallback = function () { };
const CHUNK_SIZE = 500;
const CHUNK_RES = 24;
const RENDER_DIST = 1200;
const TERRAIN_SCALE = 0.003;
const TERRAIN_HEIGHT = 300;
let simplex;
function initTerrain() {
    simplex = new SimplexNoise(Math.random() * 65536 | 0);
}
function getHeight(x, z) {
    if (!simplex)
        return 0;
    let h = 0, amp = 1, freq = 1;
    for (let i = 0; i < 5; i++) {
        h += simplex.noise2D(x * TERRAIN_SCALE * freq, z * TERRAIN_SCALE * freq) * amp;
        amp *= 0.5;
        freq *= 2;
    }
    const d = Math.sqrt(x * x + z * z);
    if (d < 400) {
        h *= (d / 400) * (d / 400);
    }
    return Math.max(0, h * TERRAIN_HEIGHT);
}
function createTerrainChunk(cx, cz) {
    const w = CHUNK_SIZE, res = CHUNK_RES, hw = w / 2;
    // Non-indexed: each quad = 6 vertices (2 triangles)
    const vertsPerQuad = 6;
    const totalVerts = res * res * vertsPerQuad;
    const pos = new Float32Array(totalVerts * 3);
    const col = new Float32Array(totalVerts * 3);
    let vi = 0;
    for (let iz = 0; iz < res; iz++) {
        for (let ix = 0; ix < res; ix++) {
            const x0 = (ix / res) * w - hw;
            const x1 = ((ix + 1) / res) * w - hw;
            const z0 = (iz / res) * w - hw;
            const z1 = ((iz + 1) / res) * w - hw;
            const h00 = getHeight(x0 + cx, z0 + cz);
            const h10 = getHeight(x1 + cx, z0 + cz);
            const h01 = getHeight(x0 + cx, z1 + cz);
            const h11 = getHeight(x1 + cx, z1 + cz);
            const verts = [
                [x0, h00, z0], [x1, h10, z0], [x0, h00, z1],
                [x1, h10, z0], [x1, h11, z1], [x0, h00, z1]
            ];
            for (let v = 0; v < 6; v++) {
                pos[vi * 3] = verts[v][0];
                pos[vi * 3 + 1] = verts[v][1];
                pos[vi * 3 + 2] = verts[v][2];
                const nh = Math.min(1, verts[v][1] / TERRAIN_HEIGHT);
                if (nh < 0.15) {
                    col[vi * 3] = 0.76;
                    col[vi * 3 + 1] = 0.7;
                    col[vi * 3 + 2] = 0.5;
                }
                else if (nh < 0.4) {
                    col[vi * 3] = 0.3 + nh * 0.5;
                    col[vi * 3 + 1] = 0.5 + nh * 0.3;
                    col[vi * 3 + 2] = 0.2;
                }
                else if (nh < 0.7) {
                    col[vi * 3] = 0.5 - nh * 0.2;
                    col[vi * 3 + 1] = 0.45 - nh * 0.15;
                    col[vi * 3 + 2] = 0.4 - nh * 0.15;
                }
                else {
                    const s = (nh - 0.7) / 0.3;
                    col[vi * 3] = 0.4 + s * 0.55;
                    col[vi * 3 + 1] = 0.35 + s * 0.55;
                    col[vi * 3 + 2] = 0.35 + s * 0.55;
                }
                vi++;
            }
        }
    }
    const geo = new THREE.BufferGeometry();
    const pa = new THREE.BufferAttribute(pos, 3);
    pa.onUploadCallback = function () { };
    const ca = new THREE.BufferAttribute(col, 3);
    ca.onUploadCallback = function () { };
    // Compute per-triangle normals (flat shading = duplicate normals per triangle)
    const nrm = new Float32Array(totalVerts * 3);
    vi = 0;
    for (let iz = 0; iz < res; iz++) {
        for (let ix = 0; ix < res; ix++) {
            const x0 = (ix / res) * w - hw;
            const x1 = ((ix + 1) / res) * w - hw;
            const z0 = (iz / res) * w - hw;
            const z1 = ((iz + 1) / res) * w - hw;
            const h00 = getHeight(x0 + cx, z0 + cz);
            const h10 = getHeight(x1 + cx, z0 + cz);
            const h01 = getHeight(x0 + cx, z1 + cz);
            const h11 = getHeight(x1 + cx, z1 + cz);
            // Triangle 1 normal
            const ax = x1 - x0, ay = h10 - h00, az = z0 - z0;
            const bx = x0 - x0, by = h01 - h00, bz = z1 - z0;
            let nx = ay * bz - az * by, ny = az * bx - ax * bz, nz = ax * by - ay * bx;
            const ln = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
            nx /= ln;
            ny /= ln;
            nz /= ln;
            nrm[vi * 3] = nx;
            nrm[vi * 3 + 1] = ny;
            nrm[vi * 3 + 2] = nz;
            vi++;
            nrm[vi * 3] = nx;
            nrm[vi * 3 + 1] = ny;
            nrm[vi * 3 + 2] = nz;
            vi++;
            nrm[vi * 3] = nx;
            nrm[vi * 3 + 1] = ny;
            nrm[vi * 3 + 2] = nz;
            vi++;
            // Triangle 2 normal
            const cx2 = x1 - x0, cy2 = h11 - h10, cz2 = z1 - z0;
            const dx2 = x0 - x1, dy2 = h01 - h10, dz2 = z1 - z0;
            nx = cy2 * dz2 - cz2 * dy2;
            ny = cz2 * dx2 - cx2 * dz2;
            nz = cx2 * dy2 - cy2 * dx2;
            const ln2 = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
            nx /= ln2;
            ny /= ln2;
            nz /= ln2;
            nrm[vi * 3] = nx;
            nrm[vi * 3 + 1] = ny;
            nrm[vi * 3 + 2] = nz;
            vi++;
            nrm[vi * 3] = nx;
            nrm[vi * 3 + 1] = ny;
            nrm[vi * 3 + 2] = nz;
            vi++;
            nrm[vi * 3] = nx;
            nrm[vi * 3 + 1] = ny;
            nrm[vi * 3 + 2] = nz;
            vi++;
        }
    }
    const na = new THREE.BufferAttribute(nrm, 3);
    na.onUploadCallback = function () { };
    geo.setAttribute('position', pa);
    geo.setAttribute('color', ca);
    geo.setAttribute('normal', na);
    const mat = new THREE.MeshPhongMaterial({
        vertexColors: true, flatShading: true, side: THREE.DoubleSide
    });
    const mesh = new THREE.Mesh(geo, mat);
    mesh.position.set(cx, 0, cz);
    return mesh;
}
function updateTerrainChunks(scene, state) {
    const px = Math.floor(state.flight.position.x / CHUNK_SIZE) * CHUNK_SIZE;
    const pz = Math.floor(state.flight.position.z / CHUNK_SIZE) * CHUNK_SIZE;
    const chunks = [];
    const vc = Math.ceil(RENDER_DIST / CHUNK_SIZE) + 1;
    for (let dx = -vc; dx <= vc; dx++) {
        for (let dz = -vc; dz <= vc; dz++) {
            const cx = px + dx * CHUNK_SIZE, cz = pz + dz * CHUNK_SIZE;
            if (Math.sqrt(dx * dx + dz * dz) * CHUNK_SIZE < RENDER_DIST)
                chunks.push([`${cx}_${cz}`, cx, cz]);
        }
    }
    const keys = new Set(chunks.map(c => c[0]));
    const remove = [];
    state.terrainChunks.forEach((m, k) => { if (!keys.has(k))
        remove.push(m); });
    for (const m of remove) {
        scene.remove(m);
        state.terrainChunks.delete(`${m.position.x}_${m.position.z}`);
    }
    for (const [k, cx, cz] of chunks) {
        if (!state.terrainChunks.has(k)) {
            scene.add(createTerrainChunk(cx, cz));
            state.terrainChunks.set(k, scene.children[scene.children.length - 1]);
        }
    }
}
function getTerrainHeightAt(x, z) {
    return getHeight(x, z);
}
};

// ── module: src/audio.ts ──
__mods["src/audio.ts"] = function (exports, require, module) {
"use strict";
// Dynamic audio system tied to flight physics
// Uses Web Audio API for real-time synthesized sounds
Object.defineProperty(exports, "__esModule", { value: true });
exports.initAudio = initAudio;
exports.resumeAudio = resumeAudio;
exports.updateAudio = updateAudio;
let audioCtx = null;
let engineGain = null;
let engineOsc = null;
let windGain = null;
let windNoise = null;
let stallWarn = null;
let stallGain = null;
let masterGain = null;
function initAudio() {
    try {
        audioCtx = new AudioContext();
        masterGain = audioCtx.createGain();
        masterGain.gain.value = 0.4;
        masterGain.connect(audioCtx.destination);
        // Engine oscillator (sawtooth for jet sound)
        engineOsc = audioCtx.createOscillator();
        engineOsc.type = 'sawtooth';
        engineOsc.frequency.value = 80;
        engineGain = audioCtx.createGain();
        engineGain.gain.value = 0;
        engineOsc.connect(engineGain);
        engineGain.connect(masterGain);
        engineOsc.start();
        // Wind noise (filtered white noise)
        const bufferSize = audioCtx.sampleRate * 2;
        const noiseBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
        const data = noiseBuffer.getChannelData(0);
        for (let i = 0; i < bufferSize; i++) {
            data[i] = Math.random() * 2 - 1;
        }
        windNoise = audioCtx.createBufferSource();
        windNoise.buffer = noiseBuffer;
        windNoise.loop = true;
        const windFilter = audioCtx.createBiquadFilter();
        windFilter.type = 'lowpass';
        windFilter.frequency.value = 400;
        windFilter.Q.value = 0.5;
        windGain = audioCtx.createGain();
        windGain.gain.value = 0;
        windNoise.connect(windFilter);
        windFilter.connect(windGain);
        windGain.connect(masterGain);
        windNoise.start();
        // Stall warning oscillator
        stallWarn = audioCtx.createOscillator();
        stallWarn.type = 'square';
        stallWarn.frequency.value = 800;
        stallGain = audioCtx.createGain();
        stallGain.gain.value = 0;
        stallWarn.connect(stallGain);
        stallGain.connect(masterGain);
        stallWarn.start();
    }
    catch (e) {
        // Audio may not initialize until user interaction
        console.warn('Audio init deferred:', e);
    }
}
function resumeAudio() {
    if (audioCtx && audioCtx.state === 'suspended') {
        audioCtx.resume();
    }
}
function updateAudio(throttle, airspeed, isStalled, altitude, dt) {
    if (!audioCtx || !engineOsc || !engineGain || !windGain || !stallGain)
        return;
    // Engine frequency based on throttle and altitude (less dense air = higher pitch perception)
    const altFactor = Math.exp(-altitude / 5000);
    const engineFreq = 60 + throttle * 200 * (1 + altFactor * 0.2);
    engineOsc.frequency.setTargetAtTime(engineFreq, audioCtx.currentTime, dt);
    // Engine volume based on throttle
    const engineVol = throttle * 0.3 + 0.01;
    engineGain.gain.setTargetAtTime(engineVol, audioCtx.currentTime, dt);
    // Wind noise based on airspeed
    const windVol = Math.min(1, airspeed / 250) * 0.2;
    windGain.gain.setTargetAtTime(windVol, audioCtx.currentTime, dt);
    // Stall warning
    const stallVol = isStalled ? 0.15 : 0;
    stallGain.gain.setTargetAtTime(stallVol, audioCtx.currentTime, dt);
}
};

// ── module: src/input.ts ──
__mods["src/input.ts"] = function (exports, require, module) {
"use strict";
// Keyboard and mouse input handling
Object.defineProperty(exports, "__esModule", { value: true });
exports.createInput = createInput;
exports.initInputHandlers = initInputHandlers;
function createInput() {
    return {
        up: false, down: false, left: false, right: false,
        rollLeft: false, rollRight: false,
        throttleUp: false, throttleDown: false,
        brake: false, gear: false
    };
}
function initInputHandlers(input, onChange) {
    const keyMap = {
        'ArrowUp': 'up', 'KeyW': 'up',
        'ArrowDown': 'down', 'KeyS': 'down',
        'ArrowLeft': 'left', 'KeyA': 'left',
        'ArrowRight': 'right', 'KeyD': 'right',
        'KeyQ': 'rollLeft', 'KeyE': 'rollRight',
        'ShiftLeft': 'throttleUp', 'ShiftRight': 'throttleUp',
        'ControlLeft': 'throttleDown', 'ControlRight': 'throttleDown',
        'Space': 'brake'
    };
    window.addEventListener('keydown', (e) => {
        const key = keyMap[e.code];
        if (key) {
            input[key] = true;
            e.preventDefault();
            onChange();
        }
        // Camera mode toggle
        if (e.code === 'KeyC') {
            e.preventDefault();
            onChange();
        }
    });
    window.addEventListener('keyup', (e) => {
        const key = keyMap[e.code];
        if (key) {
            input[key] = false;
            e.preventDefault();
            onChange();
        }
    });
}
};

// ── module: src/models.ts ──
__mods["src/models.ts"] = function (exports, require, module) {
"use strict";
// 3D aircraft model generation — builds distinct models per aircraft profile
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAircraftModel = buildAircraftModel;
exports.buildEnemyAircraft = buildEnemyAircraft;
function buildAircraftModel(aircraft, scale = 1) {
    const group = new THREE.Group();
    const bodyMat = new THREE.MeshPhongMaterial({
        color: aircraft.color,
        specular: 0x444444,
        shininess: 80,
        flatShading: true
    });
    const accentMat = new THREE.MeshPhongMaterial({
        color: aircraft.accentColor,
        specular: 0x666666,
        shininess: 100
    });
    const darkMat = new THREE.MeshPhongMaterial({
        color: 0x222222,
        specular: 0x333333,
        shininess: 60
    });
    const glassMat = new THREE.MeshPhongMaterial({
        color: 0x88ccff,
        specular: 0xffffff,
        shininess: 200,
        transparent: true
    });
    // Fuselage
    const fuselageLen = 10 * scale;
    const fuselageRad = 0.6 * scale;
    const fuselage = new THREE.CylinderGeometry(fuselageRad, fuselageRad * 0.4, fuselageLen, 8);
    const fuselageMesh = new THREE.Mesh(fuselage, bodyMat);
    fuselageMesh.rotation.x = Math.PI / 2;
    group.add(fuselageMesh);
    // Nose cone
    const noseGeo = new THREE.ConeGeometry(fuselageRad, 3 * scale, 8);
    const nose = new THREE.Mesh(noseGeo, bodyMat);
    nose.rotation.x = Math.PI / 2;
    nose.position.z = fuselageLen / 2 + 1.2 * scale;
    group.add(nose);
    // Cockpit
    const cockpitGeo = new THREE.SphereGeometry(0.45 * scale, 8, 6);
    const cockpit = new THREE.Mesh(cockpitGeo, glassMat);
    cockpit.position.set(0, 0.35 * scale, 2 * scale);
    cockpit.scale.set(1, 0.6, 1.5);
    group.add(cockpit);
    // Main wings
    const wingSpan = (aircraft.wingArea / fuselageLen) * scale;
    const wingGeo = new THREE.BoxGeometry(wingSpan * 2, 0.08 * scale, 2 * scale);
    const wings = new THREE.Mesh(wingGeo, bodyMat);
    wings.position.set(0, 0, 0.5 * scale);
    group.add(wings);
    // Wing leading edge strips (accent)
    const stripGeo = new THREE.BoxGeometry(wingSpan * 2, 0.04 * scale, 0.15 * scale);
    const stripL = new THREE.Mesh(stripGeo, accentMat);
    stripL.position.set(0, 0, 0.5 * scale + scale);
    group.add(stripL);
    // Wing tip lights
    const tipGeo = new THREE.SphereGeometry(0.08 * scale, 4, 4);
    const tipL = new THREE.Mesh(tipGeo, accentMat);
    tipL.position.set(-wingSpan, 0, 0.5 * scale);
    group.add(tipL);
    const tipR = new THREE.Mesh(tipGeo, accentMat);
    tipR.position.set(wingSpan, 0, 0.5 * scale);
    group.add(tipR);
    // Tail - vertical stabilizer
    const vTailGeo = new THREE.BoxGeometry(0.08 * scale, 2 * scale, 1.2 * scale);
    const vTail = new THREE.Mesh(vTailGeo, bodyMat);
    vTail.position.set(0, 1 * scale, -3.5 * scale);
    group.add(vTail);
    // Tail - horizontal stabilizer
    const hTailGeo = new THREE.BoxGeometry(wingSpan, 0.06 * scale, 1.2 * scale);
    const hTail = new THREE.Mesh(hTailGeo, bodyMat);
    hTail.position.set(0, 0, -3.5 * scale);
    group.add(hTail);
    // Engine nacelles
    const nacelleGeo = new THREE.CylinderGeometry(0.35 * scale, 0.3 * scale, 2.5 * scale, 8);
    const nacelleL = new THREE.Mesh(nacelleGeo, darkMat);
    nacelleL.rotation.x = Math.PI / 2;
    nacelleL.position.set(-1 * scale, -0.2 * scale, 0);
    group.add(nacelleL);
    const nacelleR = new THREE.Mesh(nacelleGeo, darkMat);
    nacelleR.rotation.x = Math.PI / 2;
    nacelleR.position.set(1 * scale, -0.2 * scale, 0);
    group.add(nacelleR);
    // Exhaust nozzles
    const nozGeo = new THREE.CylinderGeometry(0.2 * scale, 0.15 * scale, 0.5 * scale, 8);
    const nozL = new THREE.Mesh(nozGeo, accentMat);
    nozL.rotation.x = Math.PI / 2;
    nozL.position.set(-1 * scale, -0.2 * scale, -2.5 * scale);
    group.add(nozL);
    const nozR = new THREE.Mesh(nozGeo, accentMat);
    nozR.rotation.x = Math.PI / 2;
    nozR.position.set(1 * scale, -0.2 * scale, -2.5 * scale);
    group.add(nozR);
    return group;
}
function buildEnemyAircraft(color) {
    // Simplified enemy model (red/orange)
    const group = new THREE.Group();
    const mat = new THREE.MeshPhongMaterial({
        color: color,
        specular: 0x333333,
        shininess: 60,
        flatShading: true
    });
    const fuselageMesh = new THREE.Mesh(new THREE.ConeGeometry(0.3, 4, 6), mat);
    fuselageMesh.rotation.x = Math.PI / 2;
    group.add(fuselageMesh);
    const wingsMesh = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.06, 1), mat);
    group.add(wingsMesh);
    const vTailMesh = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.8, 0.5), mat);
    vTailMesh.position.set(0, 0.4, -1.5);
    group.add(vTailMesh);
    return group;
}
};

// ── module: src/hud.ts ──
__mods["src/hud.ts"] = function (exports, require, module) {
"use strict";
// Heads-Up Display rendering
// Updates flight data overlay every frame
Object.defineProperty(exports, "__esModule", { value: true });
exports.initHUD = initHUD;
exports.updateHUD = updateHUD;
let hudEl;
function initHUD() {
    hudEl = document.getElementById('hud');
    hudEl.innerHTML = `
    <div id="hud-panel">
      <div id="hud-top">
        <div id="hud-speed"></div>
        <div id="hud-pitch"></div>
        <div id="hud-altitude"></div>
      </div>
      <div id="hud-center">
        <div id="hud-crosshair">+</div>
        <div id="hud-stall" style="display:none">⚠ STALL</div>
      </div>
      <div id="hud-bottom">
        <div id="hud-throttle"></div>
        <div id="hud-gforce"></div>
        <div id="hud-mission"></div>
      </div>
      <div id="hud-controls" style="position:fixed;bottom:10px;left:10px;color:#aaa;font-size:11px;font-family:monospace;background:rgba(0,0,0,0.6);padding:8px;border-radius:4px;">
        <b>Controls:</b> W/S Pitch | Q/E Roll | A/D Yaw | Shift Ctrl Throttle | C Camera
      </div>
    </div>
  `;
}
function updateHUD(state, mission, aircraft) {
    if (!hudEl)
        return;
    const speed = document.getElementById('hud-speed');
    const pitch = document.getElementById('hud-pitch');
    const alt = document.getElementById('hud-altitude');
    const throttle = document.getElementById('hud-throttle');
    const gforce = document.getElementById('hud-gforce');
    const stall = document.getElementById('hud-stall');
    const missionEl = document.getElementById('hud-mission');
    if (speed)
        speed.textContent = `SPD ${Math.round(state.airspeed * 3.6)} km/h`;
    if (alt)
        alt.textContent = `ALT ${Math.round(state.altitude)} m`;
    if (pitch)
        pitch.textContent = `PIT ${(state.angleOfAttack * 180 / Math.PI).toFixed(1)}°`;
    if (throttle)
        throttle.textContent = `THR ${(state.throttle * 100).toFixed(0)}% RPM ${Math.round(state.engineRPM)}`;
    if (gforce) {
        const gText = `G ${state.gForce.toFixed(1)}`;
        gforce.textContent = gText;
        gforce.style.color = state.gForce > 3 ? '#ff4444' : state.gForce > 2 ? '#ffaa00' : '#44ff44';
    }
    if (stall)
        stall.style.display = state.isStalled ? 'block' : 'none';
    if (missionEl) {
        missionEl.textContent = `SCORE ${mission.score} | ${mission.type === 'takeoff' ? 'TAKEOFF/LANDING' : 'DOGFIGHT'}`;
    }
}
};

// ── module: src/mission.ts ──
__mods["src/mission.ts"] = function (exports, require, module) {
"use strict";
// Mission system: Takeoff/Landing and Dogfight scenarios
Object.defineProperty(exports, "__esModule", { value: true });
exports.initMission = initMission;
exports.updateMission = updateMission;
exports.getMissionPhaseText = getMissionPhaseText;
function createEnemy(pos) {
    return {
        position: new THREE.Vector3(pos.x, pos.y, pos.z),
        velocity: new THREE.Vector3(0, 0, 0),
        quaternion: new THREE.Quaternion(),
        alive: true,
        health: 100,
        lastShot: 0,
        targetPos: new THREE.Vector3()
    };
}
function initMission(missionType) {
    if (missionType === 'takeoff') {
        return {
            type: 'takeoff',
            waypoints: [
                new THREE.Vector3(0, 5, 0), // runway start
                new THREE.Vector3(0, 200, -500), // climb waypoint
                new THREE.Vector3(0, 200, -2000), // cruise waypoint
                new THREE.Vector3(0, 5, -3000), // landing approach
            ],
            enemies: [],
            score: 0,
            phase: 'on_runway',
            time: 0,
            alive: true
        };
    }
    else {
        return {
            type: 'dogfight',
            waypoints: [],
            enemies: [
                createEnemy(new THREE.Vector3(500, 500, -500)),
                createEnemy(new THREE.Vector3(-400, 600, -800)),
                createEnemy(new THREE.Vector3(300, 400, -1200)),
                createEnemy(new THREE.Vector3(-600, 550, -300)),
                createEnemy(new THREE.Vector3(700, 450, -1000)),
            ],
            score: 0,
            phase: 'combat',
            time: 0,
            alive: true
        };
    }
}
function updateMission(mission, flight, dt) {
    if (!mission.alive)
        return;
    mission.time += dt;
    if (mission.type === 'takeoff') {
        updateTakeoffMission(mission, flight, dt);
    }
    else {
        updateDogfightMission(mission, flight, dt);
    }
}
function updateTakeoffMission(mission, flight, dt) {
    const idx = getWaypointIndex(mission, flight);
    if (idx === 0) {
        // On runway - need to accelerate and lift off
        if (flight.altitude > 10 && flight.airspeed > 60) {
            mission.phase = 'climbing';
            mission.score += 100;
        }
    }
    else if (idx === 1) {
        // Climbing
        if (flight.altitude > 150) {
            mission.phase = 'cruising';
            mission.score += 200;
        }
    }
    else if (idx === 2) {
        // Cruising
        mission.score += dt * 10;
    }
    else if (idx === 3) {
        // Landing approach
        if (flight.altitude < 50 && flight.altitude > 0) {
            mission.phase = 'landing';
            if (flight.altitude < 10 && flight.airspeed < 80) {
                mission.phase = 'landed';
                mission.score += 500;
            }
        }
    }
    // Crash check
    if (flight.airspeed > 30 && flight.velocity.y < -20 && flight.altitude < 5) {
        mission.alive = false;
        mission.phase = 'crashed';
    }
}
function getWaypointIndex(mission, flight) {
    for (let i = 0; i < mission.waypoints.length; i++) {
        const wp = mission.waypoints[i];
        const dist = Math.sqrt((flight.position.x - wp.x) ** 2 +
            (flight.position.y - wp.y) ** 2 +
            (flight.position.z - wp.z) ** 2);
        if (dist < 500)
            return i;
    }
    return mission.waypoints.length - 1;
}
function updateDogfightMission(mission, flight, dt) {
    for (const enemy of mission.enemies) {
        if (!enemy.alive)
            continue;
        // Simple AI: fly toward player
        const dx = flight.position.x - enemy.position.x;
        const dy = flight.position.y - enemy.position.y;
        const dz = flight.position.z - enemy.position.z;
        const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
        // Maintain altitude, steer toward player
        const speed = 150;
        enemy.velocity.x += (dx / dist) * 2 * dt;
        enemy.velocity.y += (dy / dist) * 0.5 * dt;
        enemy.velocity.z += (dz / dist) * 2 * dt;
        // Clamp speed
        const vLen = Math.sqrt(enemy.velocity.x ** 2 + enemy.velocity.y ** 2 + enemy.velocity.z ** 2);
        if (vLen > speed) {
            const scale = speed / vLen;
            enemy.velocity.x *= scale;
            enemy.velocity.y *= scale;
            enemy.velocity.z *= scale;
        }
        // Keep altitude above terrain
        if (enemy.position.y < 100) {
            enemy.velocity.y += 10 * dt;
        }
        enemy.position.x += enemy.velocity.x * dt;
        enemy.position.y += enemy.velocity.y * dt;
        enemy.position.z += enemy.velocity.z * dt;
        // Update enemy quaternion to face movement direction
        const dir = new THREE.Vector3(enemy.velocity.x, enemy.velocity.y, enemy.velocity.z);
        if (dir.lengthSq() > 0.1) {
            dir.normalize();
            // Simple look-at: point forward axis toward velocity
            enemy.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.atan2(dir.x, dir.z));
        }
        // Shooting cooldown and collision
        const shootDist = 500;
        if (dist < shootDist && mission.time - enemy.lastShot > 2) {
            enemy.lastShot = mission.time;
            // Damage player
            if (dist < 30) {
                mission.score -= 50;
            }
        }
        // Player collision with enemy
        if (dist < 20 && flight.airspeed > 100) {
            enemy.health -= 50 * dt;
            if (enemy.health <= 0) {
                enemy.alive = false;
                mission.score += 300;
            }
        }
    }
    // Check if all enemies destroyed
    const aliveCount = mission.enemies.filter(e => e.alive).length;
    if (aliveCount === 0) {
        mission.phase = 'victory';
        mission.score += 1000;
    }
    // Player crash
    if (flight.airspeed > 30 && flight.velocity.y < -20 && flight.altitude < 5) {
        mission.alive = false;
        mission.phase = 'crashed';
    }
}
function getMissionPhaseText(mission) {
    switch (mission.phase) {
        case 'on_runway': return 'Taxi to takeoff speed';
        case 'climbing': return 'Climb to altitude';
        case 'cruising': return 'Cruise to landing zone';
        case 'landing': return 'Approach for landing';
        case 'landed': return '✓ Landed safely!';
        case 'combat': return 'Destroy all enemies!';
        case 'victory': return '✓ Mission complete!';
        case 'crashed': return '✗ Crashed!';
        default: return '';
    }
}
};

// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"ThinkingCap-Qwen3.6-27B-GGUF","provider":"brigid","persona":"typescript","sessionId":"d527410e-6f7a-4529-804e-857754ff1cc8","tokensIn":4217471,"tokensOut":38675,"tokensTotal":4256146,"cost":0,"turns":89,"toolCalls":90,"failedToolCalls":4,"timestamp":"2026-08-03T22:25:19.434Z"} -->