Mostrando entradas con la etiqueta Juego de tanques variado; echo solo por IA; en html;;. Mostrar todas las entradas
Mostrando entradas con la etiqueta Juego de tanques variado; echo solo por IA; en html;;. Mostrar todas las entradas

domingo, 8 de noviembre de 2026

Juego de tanques variado; echo solo por IA; en html;

https://sites.google.com/d/1E3e621ETvoodY9N6mItPT6zoCI8RKcdl/p/1Upm8xsiX_ZC6-gyxha2trqmQLjZVtUJm/edit 



<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>3D Tank Warzone</title>
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Three.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <!-- Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=Rajdhani:wght@600;700&display=swap" rel="stylesheet">

    <style>
        * {
            box-sizing: border-box;
            user-select: none;
            -webkit-user-select: none;
            touch-action: none;
        }
        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: #050811;
            font-family: 'Rajdhani', sans-serif;
        }
        .font-orbitron {
            font-family: 'Orbitron', sans-serif;
        }
        .glass-panel {
            background: rgba(10, 16, 28, 0.85);
            backdrop-filter: blur(8px);
            border: 1px solid rgba(0, 255, 204, 0.3);
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
        }
        .glow-text {
            text-shadow: 0 0 10px rgba(0, 255, 204, 0.7);
        }
        .joystick-base {
            position: relative;
            width: 120px;
            height: 120px;
            border-radius: 50%;
            background: rgba(0, 0, 0, 0.4);
            border: 2px solid rgba(0, 255, 204, 0.4);
        }
        .joystick-knob {
            position: absolute;
            top: 50%;
            left: 50%;
            width: 44px;
            height: 44px;
            margin-top: -22px;
            margin-left: -22px;
            border-radius: 50%;
            background: rgba(0, 255, 204, 0.8);
            box-shadow: 0 0 12px rgba(0, 255, 204, 0.8);
            pointer-events: none;
        }
    </style>
</head>
<body class="w-full h-full relative select-none">

    <!-- 3D Canvas -->
    <div id="game-canvas-container" class="w-full h-full absolute inset-0 z-0"></div>

    <!-- HUD Overlay -->
    <div id="hud-layer" class="absolute inset-0 z-10 pointer-events-none flex flex-col justify-between p-4">
        <!-- Top HUD -->
        <div class="flex justify-between items-start w-full">
            <!-- Health Card -->
            <div class="glass-panel p-3 rounded-xl w-60 md:w-72 pointer-events-auto">
                <div class="flex justify-between items-center mb-1">
                    <span class="font-orbitron text-xs font-bold text-cyan-400">BLINDAJE</span>
                    <span id="health-val" class="font-orbitron text-xs font-bold text-emerald-400">100 HP</span>
                </div>
                <div class="w-full bg-gray-900 h-3 rounded-full overflow-hidden border border-gray-700">
                    <div id="health-bar" class="bg-gradient-to-r from-emerald-500 to-cyan-400 h-full w-full transition-all duration-150"></div>
                </div>
                <div class="flex justify-between items-center mt-2 mb-0.5">
                    <span class="font-orbitron text-[10px] text-gray-400">RECARGA CAÑÓN</span>
                    <span id="reload-val" class="font-orbitron text-[10px] text-emerald-400">LISTO</span>
                </div>
                <div class="w-full bg-gray-900 h-1.5 rounded-full overflow-hidden">
                    <div id="reload-bar" class="bg-cyan-400 h-full w-full transition-all duration-75"></div>
                </div>
            </div>

            <!-- Stats -->
            <div class="glass-panel px-4 py-2 rounded-xl flex items-center space-x-4 text-center pointer-events-auto">
                <div>
                    <div class="text-[10px] text-gray-400 font-orbitron">OLEADA</div>
                    <div id="hud-wave" class="text-xl font-black font-orbitron text-cyan-400 glow-text">1</div>
                </div>
                <div class="h-6 w-px bg-cyan-500/30"></div>
                <div>
                    <div class="text-[10px] text-gray-400 font-orbitron">BAJAS</div>
                    <div id="hud-kills" class="text-xl font-black font-orbitron text-amber-400">0</div>
                </div>
                <div class="h-6 w-px bg-cyan-500/30"></div>
                <div>
                    <div class="text-[10px] text-gray-400 font-orbitron">PUNTOS</div>
                    <div id="hud-score" class="text-xl font-black font-orbitron text-white">0</div>
                </div>
            </div>

            <!-- Speed Control Sliders Panel -->
            <div class="glass-panel p-3 rounded-xl w-52 md:w-64 pointer-events-auto flex flex-col space-y-2">
                <div class="text-xs font-bold font-orbitron text-cyan-400 mb-1">AJUSTES DE VELOCIDAD</div>
               
                <div>
                    <div class="flex justify-between items-center text-[10px] text-gray-300 font-orbitron mb-1">
                        <span>MI VELOCIDAD</span>
                        <span id="player-speed-val" class="text-emerald-400 font-bold">100%</span>
                    </div>
                    <input id="slider-player-speed" type="range" min="0.5" max="2.5" step="0.1" value="1.0"
                        class="w-full h-1.5 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-emerald-400">
                </div>

                <div>
                    <div class="flex justify-between items-center text-[10px] text-gray-300 font-orbitron mb-1">
                        <span>VELOCIDAD ENEMIGOS</span>
                        <span id="enemy-speed-val" class="text-amber-400 font-bold">40%</span>
                    </div>
                    <input id="slider-enemy-speed" type="range" min="0.1" max="1.5" step="0.05" value="0.4"
                        class="w-full h-1.5 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-amber-400">
                </div>
            </div>

            <!-- Radar Minimap -->
            <div class="glass-panel p-1 rounded-xl w-28 h-28 md:w-32 md:h-32 pointer-events-auto relative">
                <canvas id="radar-canvas" width="120" height="120" class="w-full h-full rounded-lg"></canvas>
            </div>
        </div>

        <!-- Touch Controls -->
        <div id="touch-controls" class="w-full flex justify-between items-end pb-2 pointer-events-auto">
            <!-- Left Stick: Steering -->
            <div id="stick-move" class="joystick-base">
                <div id="knob-move" class="joystick-knob"></div>
            </div>

            <!-- Fire Button -->
            <button id="btn-fire" class="w-20 h-20 rounded-full bg-gradient-to-tr from-red-600 to-amber-500 border-2 border-red-300 font-orbitron font-black text-white text-sm active:scale-95 shadow-lg shadow-red-600/50 flex items-center justify-center pointer-events-auto">
                DISPARAR
            </button>
        </div>
    </div>

    <!-- Start Overlay -->
    <div id="screen-start" class="absolute inset-0 z-20 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
        <div class="glass-panel p-8 rounded-2xl max-w-md w-full text-center border-cyan-500/50">
            <h1 class="font-orbitron text-4xl font-black text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-emerald-400 mb-2 glow-text">
                3D TANK WARZONE
            </h1>
            <p class="text-gray-300 text-sm mb-6">Usa WASD/Flechas para conducir, ratón para apuntar la torreta y Clic/Espacio para disparar.</p>
            <button id="btn-start-game" class="w-full py-4 rounded-xl bg-gradient-to-r from-cyan-500 to-emerald-500 hover:from-cyan-400 hover:to-emerald-400 font-orbitron font-black text-lg text-slate-950 shadow-lg shadow-cyan-500/40 transition-all transform hover:scale-105 active:scale-95">
                ¡JUGAR AHORA!
            </button>
        </div>
    </div>

    <!-- Game Over Overlay -->
    <div id="screen-gameover" class="absolute inset-0 z-20 flex items-center justify-center bg-black/85 backdrop-blur-md p-4 hidden">
        <div class="glass-panel p-8 rounded-2xl max-w-md w-full text-center border-red-500/50">
            <h2 class="font-orbitron text-3xl font-black text-red-500 mb-2 glow-text">TANQUE DESTRUIDO</h2>
            <p class="text-gray-400 text-sm mb-6">Tu blindaje ha sucumbido al fuego enemigo.</p>
            <div class="bg-gray-900/80 rounded-xl p-4 mb-6 border border-gray-800 space-y-2 text-sm font-orbitron">
                <div class="flex justify-between">
                    <span class="text-gray-400">PUNTUACIÓN:</span>
                    <span id="final-score" class="text-cyan-400 font-bold">0</span>
                </div>
                <div class="flex justify-between">
                    <span class="text-gray-400">BAJAS:</span>
                    <span id="final-kills" class="text-amber-400 font-bold">0</span>
                </div>
            </div>
            <button id="btn-restart-game" class="w-full py-4 rounded-xl bg-gradient-to-r from-red-600 to-amber-500 font-orbitron font-black text-lg text-white shadow-lg shadow-red-600/40 transition-all transform hover:scale-105 active:scale-95">
                REPLEGAR TANQUE
            </button>
        </div>
    </div>

    <script>
        // --- Web Audio Synthesizer ---
        class SoundEngine {
            constructor() { this.ctx = null; }
            init() {
                if (!this.ctx) {
                    this.ctx = new (window.AudioContext || window.webkitAudioContext)();
                }
            }
            shoot() {
                if (!this.ctx) return;
                const now = this.ctx.currentTime;
                const osc = this.ctx.createOscillator();
                const gain = this.ctx.createGain();
                osc.type = 'sawtooth';
                osc.frequency.setValueAtTime(140, now);
                osc.frequency.exponentialRampToValueAtTime(30, now + 0.3);
                gain.gain.setValueAtTime(0.5, now);
                gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3);
                osc.connect(gain); gain.connect(this.ctx.destination);
                osc.start(now); osc.stop(now + 0.3);
            }
            explode() {
                if (!this.ctx) return;
                const now = this.ctx.currentTime;
                const bufSize = this.ctx.sampleRate * 0.4;
                const buf = this.ctx.createBuffer(1, bufSize, this.ctx.sampleRate);
                const data = buf.getChannelData(0);
                for (let i = 0; i < bufSize; i++) data[i] = Math.random() * 2 - 1;
                const src = this.ctx.createBufferSource();
                src.buffer = buf;
                const filter = this.ctx.createBiquadFilter();
                filter.type = 'lowpass';
                filter.frequency.setValueAtTime(600, now);
                filter.frequency.exponentialRampToValueAtTime(40, now + 0.4);
                const gain = this.ctx.createGain();
                gain.gain.setValueAtTime(0.8, now);
                gain.gain.exponentialRampToValueAtTime(0.01, now + 0.4);
                src.connect(filter); filter.connect(gain); gain.connect(this.ctx.destination);
                src.start(now);
            }
        }
        const sfx = new SoundEngine();

        // --- Game Logic ---
        let scene, camera, renderer;
        let playerTank, playerBody, playerTurret, playerBarrel;
        let obstacles = [], enemies = [], projectiles = [], particles = [], wrecks = [];
       
        let isGameRunning = false;
        let health = 100, score = 0, kills = 0, wave = 1;
        let canShoot = true, reloadProgress = 1;

        // Speed Multipliers from Sliders
        let playerSpeedMult = 1.0;
        let enemySpeedMult = 0.4;

        const keys = {};
        const mouse = { x: 0, y: 0, worldPos: new THREE.Vector3() };
        const raycaster = new THREE.Raycaster();
        const groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);

        function init() {
            const container = document.getElementById('game-canvas-container');

            // 1. Scene & Camera
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x0a0f1d);
            scene.fog = new THREE.FogExp2(0x0a0f1d, 0.012);

            camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000);

            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFSoftShadowMap;
            container.appendChild(renderer.domElement);

            // 2. Lights
            const ambient = new THREE.AmbientLight(0xffffff, 0.5);
            scene.add(ambient);

            const sun = new THREE.DirectionalLight(0xffffff, 0.8);
            sun.position.set(40, 80, 40);
            sun.castShadow = true;
            sun.shadow.mapSize.width = 2048;
            sun.shadow.mapSize.height = 2048;
            sun.shadow.camera.left = -70; sun.shadow.camera.right = 70;
            sun.shadow.camera.top = 70; sun.shadow.camera.bottom = -70;
            scene.add(sun);

            // 3. Ground (Military Green 0x2e7d32 with Light Green Grid 0x81c784)
            const groundGeo = new THREE.PlaneGeometry(240, 240);
            const groundMat = new THREE.MeshStandardMaterial({ color: 0x2e7d32, roughness: 0.85, metalness: 0.1 });
            const ground = new THREE.Mesh(groundGeo, groundMat);
            ground.rotation.x = -Math.PI / 2;
            ground.receiveShadow = true;
            scene.add(ground);

            const grid = new THREE.GridHelper(240, 48, 0x81c784, 0x388e3c);
            grid.position.y = 0.02;
            scene.add(grid);

            // 4. Map Obstacles
            buildMap();

            // 5. Create Player Tank
            playerTank = createTankMesh(0x00ffcc, 0x008877);
            playerBody = playerTank.children[0];
            playerTurret = playerTank.children[1];
            playerBarrel = playerTurret.children[1];
            scene.add(playerTank);

            // 6. Listeners
            window.addEventListener('resize', onWindowResize);
            window.addEventListener('keydown', e => { keys[e.code] = true; });
            window.addEventListener('keyup', e => { keys[e.code] = false; });
            window.addEventListener('mousemove', onMouseMove);
            window.addEventListener('mousedown', e => {
                if (e.button === 0 && isGameRunning && canShoot) shootCannon(playerTank, true);
            });

            setupTouchControls();

            // 7. Start Render Loop
            requestAnimationFrame(animate);
        }

        function createTankMesh(chassisColor, turretColor) {
            const tank = new THREE.Group();

            // Body / Chassis
            const bodyGeo = new THREE.BoxGeometry(3.6, 1.2, 5.0);
            const bodyMat = new THREE.MeshStandardMaterial({ color: chassisColor, roughness: 0.4 });
            const body = new THREE.Mesh(bodyGeo, bodyMat);
            body.position.y = 0.8;
            body.castShadow = true;
            body.receiveShadow = true;
            tank.add(body);

            // Turret Group
            const turretGroup = new THREE.Group();
            turretGroup.position.set(0, 1.6, 0);

            const turretGeo = new THREE.CylinderGeometry(1.3, 1.5, 0.9, 8);
            const turretMat = new THREE.MeshStandardMaterial({ color: turretColor, roughness: 0.3 });
            const turret = new THREE.Mesh(turretGeo, turretMat);
            turret.castShadow = true;
            turretGroup.add(turret);

            // Barrel
            const barrelGeo = new THREE.CylinderGeometry(0.2, 0.25, 3.2, 12);
            const barrelMat = new THREE.MeshStandardMaterial({ color: 0x222222, metalness: 0.8 });
            const barrel = new THREE.Mesh(barrelGeo, barrelMat);
            barrel.rotation.x = Math.PI / 2;
            barrel.position.set(0, 0.1, 1.6);
            barrel.castShadow = true;
            turretGroup.add(barrel);

            tank.add(turretGroup);
            return tank;
        }

        function buildMap() {
            const wallMat = new THREE.MeshStandardMaterial({ color: 0x1e293b, roughness: 0.6 });
           
            // Outer Walls
            const hWall = new THREE.BoxGeometry(240, 5, 3);
            const vWall = new THREE.BoxGeometry(3, 5, 240);

            const n = new THREE.Mesh(hWall, wallMat); n.position.set(0, 2.5, -120); scene.add(n); obstacles.push(n);
            const s = new THREE.Mesh(hWall, wallMat); s.position.set(0, 2.5, 120); scene.add(s); obstacles.push(s);
            const e = new THREE.Mesh(vWall, wallMat); e.position.set(120, 2.5, 0); scene.add(e); obstacles.push(e);
            const w = new THREE.Mesh(vWall, wallMat); w.position.set(-120, 2.5, 0); scene.add(w); obstacles.push(w);

            // Inner Obstacles
            const boxGeo = new THREE.BoxGeometry(1, 1, 1);
            const positions = [
                {x: -30, z: -30, w: 10, h: 5, d: 10},
                {x: 30, z: -30, w: 12, h: 5, d: 8},
                {x: -30, z: 30, w: 8, h: 5, d: 12},
                {x: 30, z: 30, w: 14, h: 5, d: 10},
                {x: 0, z: -60, w: 16, h: 6, d: 8},
                {x: 0, z: 60, w: 16, h: 6, d: 8}
            ];

            positions.forEach(p => {
                const obs = new THREE.Mesh(boxGeo, wallMat);
                obs.scale.set(p.w, p.h, p.d);
                obs.position.set(p.x, p.h / 2, p.z);
                obs.castShadow = true;
                obs.receiveShadow = true;
                scene.add(obs);
                obs.radius = Math.max(p.w, p.d) / 2;
                obstacles.push(obs);
            });
        }

        function spawnWave() {
            const count = 2 + wave * 2;
            for (let i = 0; i < count; i++) {
                let rx, rz;
                do {
                    rx = (Math.random() - 0.5) * 180;
                    rz = (Math.random() - 0.5) * 180;
                } while (Math.hypot(rx - playerTank.position.x, rz - playerTank.position.z) < 35);

                const enemy = createTankMesh(0xff3366, 0x990033);
                enemy.position.set(rx, 0, rz);
                enemy.userData = {
                    health: 40 + wave * 10,
                    maxHealth: 40 + wave * 10,
                    lastShoot: 0,
                    shootInterval: Math.max(1400, 2600 - wave * 150),
                    speed: 0.08 + Math.min(0.06, wave * 0.008)
                };
                scene.add(enemy);
                enemies.push(enemy);
            }
        }

        function onMouseMove(event) {
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            raycaster.setFromCamera(mouse, camera);
            raycaster.ray.intersectPlane(groundPlane, mouse.worldPos);
        }

        function shootCannon(tank, isPlayer = false) {
            if (isPlayer && !canShoot) return;

            const turret = tank.children[1];
            const barrel = turret.children[1];

            const muzzlePos = new THREE.Vector3();
            barrel.getWorldPosition(muzzlePos);

            const dir = new THREE.Vector3();
            turret.getWorldDirection(dir);
            dir.y = 0;
            dir.normalize();

            // Shell
            const projGeo = new THREE.SphereGeometry(0.35, 8, 8);
            const projMat = new THREE.MeshBasicMaterial({ color: isPlayer ? 0x00ffcc : 0xffaa00 });
            const proj = new THREE.Mesh(projGeo, projMat);
            proj.position.copy(muzzlePos);
            proj.userData = { dir: dir, speed: 1.1, isPlayer: isPlayer, life: 100 };
            scene.add(proj);
            projectiles.push(proj);

            sfx.shoot();

            if (isPlayer) {
                canShoot = false;
                reloadProgress = 0;
                document.getElementById('reload-val').innerText = 'RECARGANDO';
                document.getElementById('reload-val').className = 'font-orbitron text-[10px] text-amber-400';
            }
        }

        function createExplosion(pos, color = 0xffaa00, count = 20) {
            sfx.explode();
            for (let i = 0; i < count; i++) {
                const pGeo = new THREE.BoxGeometry(0.3, 0.3, 0.3);
                const pMat = new THREE.MeshBasicMaterial({ color: color });
                const p = new THREE.Mesh(pGeo, pMat);
                p.position.copy(pos);
                p.userData = {
                    vel: new THREE.Vector3((Math.random() - 0.5) * 0.6, Math.random() * 0.5 + 0.2, (Math.random() - 0.5) * 0.6),
                    life: 25 + Math.random() * 15
                };
                scene.add(p);
                particles.push(p);
            }
        }

        function setupTouchControls() {
            const moveZone = document.getElementById('stick-move');
            const moveKnob = document.getElementById('knob-move');
            const fireBtn = document.getElementById('btn-fire');

            // Sliders setup
            const sliderPlayer = document.getElementById('slider-player-speed');
            const sliderEnemy = document.getElementById('slider-enemy-speed');

            sliderPlayer.addEventListener('input', (e) => {
                playerSpeedMult = parseFloat(e.target.value);
                document.getElementById('player-speed-val').innerText = Math.round(playerSpeedMult * 100) + '%';
            });

            sliderEnemy.addEventListener('input', (e) => {
                enemySpeedMult = parseFloat(e.target.value);
                document.getElementById('enemy-speed-val').innerText = Math.round(enemySpeedMult * 100) + '%';
            });

            let activeId = null;
            let startX = 0, startY = 0;

            moveZone.addEventListener('touchstart', e => {
                const t = e.changedTouches[0];
                activeId = t.identifier;
                startX = t.clientX; startY = t.clientY;
            });

            moveZone.addEventListener('touchmove', e => {
                for (let t of e.changedTouches) {
                    if (t.identifier === activeId) {
                        const dx = t.clientX - startX;
                        const dy = t.clientY - startY;
                        const dist = Math.min(40, Math.hypot(dx, dy));
                        const angle = Math.atan2(dy, dx);

                        moveKnob.style.transform = `translate(${Math.cos(angle)*dist}px, ${Math.sin(angle)*dist}px)`;

                        keys['KeyW'] = dy < -10;
                        keys['KeyS'] = dy > 10;
                        keys['KeyA'] = dx < -10;
                        keys['KeyD'] = dx > 10;
                    }
                }
            });

            const resetTouch = () => {
                moveKnob.style.transform = `translate(0px, 0px)`;
                keys['KeyW'] = false; keys['KeyS'] = false;
                keys['KeyA'] = false; keys['KeyD'] = false;
            };

            moveZone.addEventListener('touchend', resetTouch);
            moveZone.addEventListener('touchcancel', resetTouch);

            fireBtn.addEventListener('click', () => {
                if (isGameRunning && canShoot) shootCannon(playerTank, true);
            });
        }

        function updateRadar() {
            const canvas = document.getElementById('radar-canvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, 120, 120);

            ctx.fillStyle = 'rgba(5, 15, 25, 0.8)';
            ctx.fillRect(0, 0, 120, 120);

            const c = 60;
            const scale = 0.5;

            // Player
            ctx.fillStyle = '#00ffcc';
            ctx.beginPath();
            ctx.arc(c, c, 3.5, 0, Math.PI * 2);
            ctx.fill();

            // Enemies
            ctx.fillStyle = '#ff3366';
            enemies.forEach(e => {
                const dx = (e.position.x - playerTank.position.x) * scale;
                const dz = (e.position.z - playerTank.position.z) * scale;
                if (Math.hypot(dx, dz) < 55) {
                    ctx.beginPath();
                    ctx.arc(c + dx, c + dz, 3, 0, Math.PI * 2);
                    ctx.fill();
                }
            });
        }

        function updateGameLogic() {
            if (!isGameRunning) return;

            // 1. Reload Bar
            if (!canShoot) {
                reloadProgress += 0.025;
                document.getElementById('reload-bar').style.width = (reloadProgress * 100) + '%';
                if (reloadProgress >= 1) {
                    canShoot = true;
                    document.getElementById('reload-val').innerText = 'LISTO';
                    document.getElementById('reload-val').className = 'font-orbitron text-[10px] text-emerald-400';
                }
            }

            // 2. Player Steering (Scaled by Speed Slider)
            const moveSpeed = 0.18 * playerSpeedMult;
            const rotSpeed = 0.04 * Math.sqrt(playerSpeedMult);

            if (keys['KeyW'] || keys['ArrowUp']) playerTank.translateZ(moveSpeed);
            if (keys['KeyS'] || keys['ArrowDown']) playerTank.translateZ(-moveSpeed);
            if (keys['KeyA'] || keys['ArrowLeft']) playerTank.rotation.y += rotSpeed;
            if (keys['KeyD'] || keys['ArrowRight']) playerTank.rotation.y -= rotSpeed;

            playerTank.position.x = Math.max(-115, Math.min(115, playerTank.position.x));
            playerTank.position.z = Math.max(-115, Math.min(115, playerTank.position.z));

            // Turret Mouse Aiming
            playerTurret.lookAt(mouse.worldPos);

            // Camera Tracking
            camera.position.x = playerTank.position.x;
            camera.position.z = playerTank.position.z + 24;
            camera.position.y = playerTank.position.y + 18;
            camera.lookAt(playerTank.position);

            // 3. Enemies AI (Scaled by Enemy Speed Slider)
            const now = Date.now();
            enemies.forEach(enemy => {
                const dist = enemy.position.distanceTo(playerTank.position);
                enemy.lookAt(playerTank.position);
                if (dist > 14) {
                    enemy.translateZ(enemy.userData.speed * enemySpeedMult);
                }

                if (dist < 50 && now - enemy.userData.lastShoot > enemy.userData.shootInterval) {
                    shootCannon(enemy, false);
                    enemy.userData.lastShoot = now;
                }
            });

            // 4. Projectiles Update
            for (let i = projectiles.length - 1; i >= 0; i--) {
                const p = projectiles[i];
                p.position.addScaledVector(p.userData.dir, p.userData.speed);
                p.userData.life--;

                let hit = false;

                // Hit Obstacles
                obstacles.forEach(obs => {
                    const box = new THREE.Box3().setFromObject(obs);
                    if (box.containsPoint(p.position)) {
                        createExplosion(p.position, 0xaaaaaa, 8);
                        hit = true;
                    }
                });

                // Hit Enemies
                if (!hit && p.userData.isPlayer) {
                    enemies.forEach((enemy, eIdx) => {
                        if (p.position.distanceTo(enemy.position) < 2.5) {
                            enemy.userData.health -= 25;
                            createExplosion(p.position, 0xff9900, 14);
                            hit = true;

                            if (enemy.userData.health <= 0) {
                                createExplosion(enemy.position, 0xff3300, 30);

                                // Wreckage
                                const wreck = new THREE.Mesh(
                                    new THREE.BoxGeometry(3.6, 1, 4.8),
                                    new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.9 })
                                );
                                wreck.position.copy(enemy.position);
                                scene.add(wreck);
                                wrecks.push(wreck);

                                scene.remove(enemy);
                                enemies.splice(eIdx, 1);
                                kills++;
                                score += 150;

                                document.getElementById('hud-kills').innerText = kills;
                                document.getElementById('hud-score').innerText = score;

                                if (enemies.length === 0) {
                                    wave++;
                                    document.getElementById('hud-wave').innerText = wave;
                                    health = Math.min(100, health + 25);
                                    updateHealthUI();
                                    spawnWave();
                                }
                            }
                        }
                    });
                }

                // Hit Player
                if (!hit && !p.userData.isPlayer) {
                    if (p.position.distanceTo(playerTank.position) < 2.2) {
                        health -= 15;
                        updateHealthUI();
                        createExplosion(p.position, 0xff0033, 18);
                        hit = true;

                        if (health <= 0) {
                            isGameRunning = false;
                            createExplosion(playerTank.position, 0xff0000, 40);
                            document.getElementById('final-score').innerText = score;
                            document.getElementById('final-kills').innerText = kills;
                            document.getElementById('screen-gameover').classList.remove('hidden');
                        }
                    }
                }

                if (hit || p.userData.life <= 0) {
                    scene.remove(p);
                    projectiles.splice(i, 1);
                }
            }

            // 5. Particles Update
            for (let i = particles.length - 1; i >= 0; i--) {
                const p = particles[i];
                p.position.add(p.userData.vel);
                p.userData.vel.y -= 0.015;
                p.userData.life--;
                if (p.userData.life <= 0) {
                    scene.remove(p);
                    particles.splice(i, 1);
                }
            }

            updateRadar();
        }

        function updateHealthUI() {
            document.getElementById('health-val').innerText = health + ' HP';
            document.getElementById('health-bar').style.width = health + '%';
        }

        function resetAndStartGame() {
            enemies.forEach(e => scene.remove(e));
            projectiles.forEach(p => scene.remove(p));
            particles.forEach(p => scene.remove(p));
            wrecks.forEach(w => scene.remove(w));

            enemies = []; projectiles = []; particles = []; wrecks = [];
            health = 100; score = 0; kills = 0; wave = 1;
           
            playerTank.position.set(0, 0, 0);
            updateHealthUI();

            document.getElementById('hud-wave').innerText = '1';
            document.getElementById('hud-kills').innerText = '0';
            document.getElementById('hud-score').innerText = '0';

            spawnWave();
            isGameRunning = true;
        }

        function animate() {
            requestAnimationFrame(animate);
            updateGameLogic();
            renderer.render(scene, camera);
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // Start & Restart Listeners
        document.getElementById('btn-start-game').addEventListener('click', () => {
            sfx.init();
            document.getElementById('screen-start').classList.add('hidden');
            resetAndStartGame();
        });

        document.getElementById('btn-restart-game').addEventListener('click', () => {
            sfx.init();
            document.getElementById('screen-gameover').classList.add('hidden');
            resetAndStartGame();
        });

        window.onload = init;
    </script>
</body>
</html>