Uncaught ReferenceError ReferenceError: deltaTime is not defined
    at animate (c:\Users\123\Desktop\2.html:525:19)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    at <anonymous> (c:\Users\123\Desktop\2.html:605:5)
2.html:525
Uncaught ReferenceError ReferenceError: deltaTime is not defined
    at animate (c:\Users\123\Desktop\2.html:525:19)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    --- requestAnimationFrame ---
    at animate (c:\Users\123\Desktop\2.html:523:9)
    at <anonymous> (c:\Users\123\Desktop\2.html:605:5)
2.html:525<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>First-Person Shooter Game with House and AK-74 Gun</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000;
        }
        canvas {
            display: block;
        }
        #instructions {
            position: absolute;
            top: 50%;
            width: 100%;
            text-align: center;
            color: white;
            font-family: Arial, sans-serif;
            font-size: 24px;
            user-select: none;
            cursor: pointer;
            transform: translateY(-50%);
        }
    </style>
    <!-- Import Map to resolve 'three' and 'tween.js' modules -->
    <script type="importmap">
    {
        "imports": {
            "three": "https://cdn.jsdelivr.net/npm/three@0.152.0/build/three.module.js",
            "tween.js": "https://unpkg.com/@tweenjs/tween.js@18.6.4/dist/tween.esm.js"
        }
    }
    </script>
</head>
<body>

<div id="instructions">
    Click to Play<br>
    W/A/S/D to Move | Mouse to Look | Click to Shoot<br>
    Spacebar to Jump | Ctrl to Crouch | Shift to Run
</div>

<script type="module">
    // Import Three.js, PointerLockControls, and TWEEN.js as ES6 modules using import map
    import * as THREE from 'three';
    import { PointerLockControls } from 'https://cdn.jsdelivr.net/npm/three@0.152.0/examples/jsm/controls/PointerLockControls.js';
    import { Tween, Easing } from 'tween.js';

    // Constants for player's height
    const standingHeight = 10.0;
    const crouchHeight = 7.0;

    // Setup Scene, Camera, and Renderer
    const scene = new THREE.Scene();
    
    // Set the sky to a solid blue color
    scene.background = new THREE.Color(0x87ceeb); // Sky Blue
    
    const camera = new THREE.PerspectiveCamera(
        75, 
        window.innerWidth / window.innerHeight, 
        0.1, 
        1000
    );

    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.shadowMap.enabled = true; // Enable shadows
    document.body.appendChild(renderer.domElement);

    // Add Lighting
    const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1.0);
    hemiLight.position.set(0, 200, 0);
    scene.add(hemiLight);

    const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
    dirLight.position.set(0, 200, 100);
    dirLight.castShadow = true;
    dirLight.shadow.camera.top = 180;
    dirLight.shadow.camera.bottom = -100;
    dirLight.shadow.camera.left = -120;
    dirLight.shadow.camera.right = 120;
    scene.add(dirLight);

    // ---------- Add Green Ground ----------
    const groundMaterial = new THREE.MeshPhongMaterial({ 
        color: 0x00ff00 // Green color
    });

    const groundGeometry = new THREE.PlaneGeometry(1000, 1000);
    const ground = new THREE.Mesh(groundGeometry, groundMaterial);
    ground.rotation.x = - Math.PI / 2; // Rotate to make it horizontal
    ground.receiveShadow = true; // Allow ground to receive shadows
    scene.add(ground);

    // ---------- Add Some Red Cubes as Targets ----------
    const boxGeometry = new THREE.BoxGeometry(20, 20, 20);
    const boxMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 }); // Red color
    const targets = [];
    for (let i = 0; i < 20; i++) {
        const box = new THREE.Mesh(boxGeometry, boxMaterial);
        box.position.set(
            Math.random() * 800 - 400,
            10,
            Math.random() * 800 - 400
        );
        box.castShadow = true;
        box.receiveShadow = true;
        scene.add(box);
        targets.push(box);
    }

    // ---------- Add a House with Windows and Doors ----------
    function createHouse() {
        const house = new THREE.Group();

        // ----- Walls -----
        const wallsMaterial = new THREE.MeshPhongMaterial({ color: 0x8B4513 }); // Brown color for walls
        const wallsGeometry = new THREE.BoxGeometry(100, 60, 100);
        const walls = new THREE.Mesh(wallsGeometry, wallsMaterial);
        walls.castShadow = true;
        walls.receiveShadow = true;
        walls.position.y = 30; // Raise walls to sit on the ground
        house.add(walls);

        // ----- Roof -----
        const roofMaterial = new THREE.MeshPhongMaterial({ color: 0xB22222 }); // Firebrick color for roof
        const roofGeometry = new THREE.ConeGeometry(70, 30, 4); // Square base
        const roof = new THREE.Mesh(roofGeometry, roofMaterial);
        roof.rotation.y = Math.PI / 4; // Rotate to align with walls
        roof.position.y = 60; // Position atop the walls
        roof.castShadow = true;
        roof.receiveShadow = true;
        house.add(roof);

        // ----- Door -----
        const doorMaterial = new THREE.MeshPhongMaterial({ color: 0x654321 }); // Dark brown for door
        const doorGeometry = new THREE.BoxGeometry(20, 35, 5);
        const door = new THREE.Mesh(doorGeometry, doorMaterial);
        door.position.set(0, 17.5, 52.51); // Position on the front face of walls
        door.castShadow = true;
        door.receiveShadow = true;
        house.add(door);

        // ----- Windows -----
        const windowMaterial = new THREE.MeshPhongMaterial({ 
            color: 0xADD8E6, // Light blue for windows
            transparent: true,
            opacity: 0.6
        });
        const windowGeometry = new THREE.BoxGeometry(15, 15, 2);

        // Front Window
        const frontWindow = new THREE.Mesh(windowGeometry, windowMaterial);
        frontWindow.position.set(-30, 30, 52.51); // Left front window
        house.add(frontWindow);

        const frontWindow2 = new THREE.Mesh(windowGeometry, windowMaterial);
        frontWindow2.position.set(30, 30, 52.51); // Right front window
        house.add(frontWindow2);

        // Back Window
        const backWindow = new THREE.Mesh(windowGeometry, windowMaterial);
        backWindow.position.set(-30, 30, -52.51); // Left back window
        house.add(backWindow);

        const backWindow2 = new THREE.Mesh(windowGeometry, windowMaterial);
        backWindow2.position.set(30, 30, -52.51); // Right back window
        house.add(backWindow2);

        // ----- Side Windows -----
        const sideWindow = new THREE.Mesh(windowGeometry, windowMaterial);
        sideWindow.position.set(52.51, 30, 30); // Right side window
        sideWindow.rotation.y = Math.PI / 2;
        house.add(sideWindow);

        const sideWindow2 = new THREE.Mesh(windowGeometry, windowMaterial);
        sideWindow2.position.set(52.51, 30, -30); // Right side window
        sideWindow2.rotation.y = Math.PI / 2;
        house.add(sideWindow2);

        const sideWindow3 = new THREE.Mesh(windowGeometry, windowMaterial);
        sideWindow3.position.set(-52.51, 30, 30); // Left side window
        sideWindow3.rotation.y = Math.PI / 2;
        house.add(sideWindow3);

        const sideWindow4 = new THREE.Mesh(windowGeometry, windowMaterial);
        sideWindow4.position.set(-52.51, 30, -30); // Left side window
        sideWindow4.rotation.y = Math.PI / 2;
        house.add(sideWindow4);

        return house;
    }

    // Create and position the house
    const house = createHouse();
    house.position.set(0, 0, -200); // Position the house 200 units behind the starting point
    scene.add(house);

    // ---------- Pointer Lock Controls ----------
    const controls = new PointerLockControls(camera, document.body);

    const instructions = document.getElementById('instructions');
    instructions.addEventListener('click', () => {
        controls.lock();
    }, false);

    controls.addEventListener('lock', () => {
        instructions.style.display = 'none';
    });

    controls.addEventListener('unlock', () => {
        instructions.style.display = '';
    });

    scene.add(controls.getObject());

    // ---------- Initialize Player's Y Position ----------
    controls.getObject().position.y = standingHeight;

    // ---------- Movement Variables ----------
    const move = {
        forward: false,
        backward: false,
        left: false,
        right: false,
        run: false
    };

    let velocity = new THREE.Vector3();
    let direction = new THREE.Vector3();

    const walkSpeed = 400.0; // Units per second
    const runSpeed = 700.0; // Units per second
    let currentSpeed = walkSpeed;

    // ---------- Jumping and Gravity Variables ----------
    const gravity = 2000.0; // Units per second squared
    let isOnGround = true;
    let verticalVelocity = 0.0;

    // ---------- Crouch Variables ----------
    let isCrouching = false;

    // ---------- Event Listeners for Key Presses ----------
    const onKeyDown = function (event) {
        switch (event.code) {
            case 'KeyW':
            case 'ArrowUp':
                move.forward = true;
                break;
            case 'KeyA':
            case 'ArrowLeft':
                move.left = true;
                break;
            case 'KeyS':
            case 'ArrowDown':
                move.backward = true;
                break;
            case 'KeyD':
            case 'ArrowRight':
                move.right = true;
                break;
            case 'Space':
                if (isOnGround && !isCrouching) {
                    verticalVelocity = 600; // Initial jump velocity (positive for upward)
                    isOnGround = false;
                }
                break;
            case 'ShiftLeft':
            case 'ShiftRight':
                move.run = true;
                break;
            case 'ControlLeft':
            case 'ControlRight':
                toggleCrouch();
                break;
        }
    };

    const onKeyUp = function (event) {
        switch (event.code) {
            case 'KeyW':
            case 'ArrowUp':
                move.forward = false;
                break;
            case 'KeyA':
            case 'ArrowLeft':
                move.left = false;
                break;
            case 'KeyS':
            case 'ArrowDown':
                move.backward = false;
                break;
            case 'KeyD':
            case 'ArrowRight':
                move.right = false;
                break;
            case 'ShiftLeft':
            case 'ShiftRight':
                move.run = false;
                break;
            // Note: Removed handling for 'Control' key on keyup since we're using toggle
        }
    };

    window.addEventListener('keydown', onKeyDown, false);
    window.addEventListener('keyup', onKeyUp, false);

    // ---------- Crouch Toggle Function ----------
    function toggleCrouch() {
        if (isCrouching) {
            // Stand up
            isCrouching = false;
            // Smoothly interpolate camera and gun positions back to standing
            new Tween(controls.getObject().position)
                .to({ y: standingHeight }, 200)
                .easing(Easing.Linear.None)
                .start();
            new Tween(gun.position)
                .to({ y: -1 }, 200)
                .easing(Easing.Linear.None)
                .start();
        } else {
            // Crouch down
            isCrouching = true;
            // Smoothly interpolate camera and gun positions to crouch
            new Tween(controls.getObject().position)
                .to({ y: crouchHeight }, 200)
                .easing(Easing.Linear.None)
                .start();
            new Tween(gun.position)
                .to({ y: -1.5 }, 200)
                .easing(Easing.Linear.None)
                .start();
        }
    }

    // ---------- Bullet Array ----------
    const bullets = [];
    const bulletSpeed = 1000; // Units per second
    const bulletGeometry = new THREE.SphereGeometry(0.5, 8, 8);
    const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 }); // Yellow bullets

    // ---------- Shooting Mechanism ----------
    const shoot = () => {
        const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
        bullet.position.copy(controls.getObject().position);

        // Calculate direction
        const vector = new THREE.Vector3(0, 0, -1);
        vector.applyQuaternion(controls.getObject().quaternion);
        bullet.velocity = vector.clone().multiplyScalar(bulletSpeed * deltaTime);

        scene.add(bullet);
        bullets.push(bullet);

        // Trigger recoil animation
        recoil();
    };

    window.addEventListener('mousedown', (event) => {
        if (event.button === 0 && controls.isLocked === true) { // Left mouse button
            shoot();
        }
    }, false);

    // ---------- Handle Window Resize ----------
    window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);
    }, false);

    // ---------- Gun Setup (AK-74 Model) ----------
    // Create a more detailed AK-74 model using basic geometries
    const gun = new THREE.Group();

    // ----- Receiver -----
    const receiverGeometry = new THREE.BoxGeometry(1, 0.5, 1.5);
    const receiverMaterial = new THREE.MeshPhongMaterial({ color: 0x444444 });
    const receiver = new THREE.Mesh(receiverGeometry, receiverMaterial);
    receiver.position.set(0, 0, -0.75);
    receiver.castShadow = true;
    receiver.receiveShadow = true;
    gun.add(receiver);

    // ----- Barrel -----
    const barrelGeometry = new THREE.CylinderGeometry(0.1, 0.1, 3, 8);
    const barrelMaterial = new THREE.MeshPhongMaterial({ color: 0x555555 });
    const barrel = new THREE.Mesh(barrelGeometry, barrelMaterial);
    barrel.rotation.x = Math.PI / 2;
    barrel.position.set(0, 0, -2);
    barrel.castShadow = true;
    barrel.receiveShadow = true;
    gun.add(barrel);

    // ----- Handguard -----
    const handguardGeometry = new THREE.BoxGeometry(0.3, 0.3, 1.5);
    const handguardMaterial = new THREE.MeshPhongMaterial({ color: 0x666666 });
    const handguard = new THREE.Mesh(handguardGeometry, handguardMaterial);
    handguard.position.set(0, 0.5, -1.5);
    handguard.castShadow = true;
    handguard.receiveShadow = true;
    gun.add(handguard);

    // ----- Magazine -----
    const magazineGeometry = new THREE.BoxGeometry(0.5, 1, 0.5);
    const magazineMaterial = new THREE.MeshPhongMaterial({ color: 0x000000 });
    const magazine = new THREE.Mesh(magazineGeometry, magazineMaterial);
    magazine.position.set(0, -0.75, -0.75);
    magazine.castShadow = true;
    magazine.receiveShadow = true;
    gun.add(magazine);

    // ----- Stock -----
    const stockGeometry = new THREE.BoxGeometry(0.2, 1, 0.2);
    const stockMaterial = new THREE.MeshPhongMaterial({ color: 0x444444 });
    const stock = new THREE.Mesh(stockGeometry, stockMaterial);
    stock.position.set(-0.6, -0.4, 0.3);
    stock.castShadow = true;
    stock.receiveShadow = true;
    gun.add(stock);

    // ----- Front Sight -----
    const frontSightGeometry = new THREE.BoxGeometry(0.05, 0.05, 0.2);
    const frontSightMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 });
    const frontSight = new THREE.Mesh(frontSightGeometry, frontSightMaterial);
    frontSight.position.set(0, 0.25, -3);
    frontSight.castShadow = true;
    frontSight.receiveShadow = true;
    gun.add(frontSight);

    // ----- Rear Sight -----
    const rearSightGeometry = new THREE.BoxGeometry(0.1, 0.1, 0.2);
    const rearSightMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 });
    const rearSight = new THREE.Mesh(rearSightGeometry, rearSightMaterial);
    rearSight.position.set(-0.3, 0.25, -0.5);
    rearSight.castShadow = true;
    rearSight.receiveShadow = true;
    gun.add(rearSight);

    // ----- Pistol Grip -----
    const gripGeometry = new THREE.BoxGeometry(0.3, 0.5, 0.2);
    const gripMaterial = new THREE.MeshPhongMaterial({ color: 0x222222 });
    const grip = new THREE.Mesh(gripGeometry, gripMaterial);
    grip.position.set(0.3, -0.3, -0.5);
    grip.castShadow = true;
    grip.receiveShadow = true;
    gun.add(grip);

    // Position the gun in front of the camera
    gun.position.set(0.5, -1, -2); // Initial standing position
    camera.add(gun); // Add the gun to the camera's hierarchy

    // Enable shadows for gun parts
    receiver.castShadow = true;
    receiver.receiveShadow = true;
    barrel.castShadow = true;
    barrel.receiveShadow = true;
    handguard.castShadow = true;
    handguard.receiveShadow = true;
    magazine.castShadow = true;
    magazine.receiveShadow = true;
    stock.castShadow = true;
    stock.receiveShadow = true;
    frontSight.castShadow = true;
    frontSight.receiveShadow = true;
    rearSight.castShadow = true;
    rearSight.receiveShadow = true;
    grip.castShadow = true;
    grip.receiveShadow = true;

    // ---------- Recoil Animation ----------
    let isRecoiling = false;
    const recoilDistance = 0.1; // How much the gun moves back
    const recoilSpeed = 10.0; // Speed of recoil
    const originalGunPosition = new THREE.Vector3(0.5, -1, -2);
    const originalGunRotation = new THREE.Euler(0, 0, 0);

    const recoil = () => {
        if (!isRecoiling) {
            isRecoiling = true;
            // Start recoil tween
            new Tween(gun.position)
                .to({ z: originalGunPosition.z - recoilDistance, y: originalGunPosition.y + 0.5 }, 100)
                .easing(Easing.Linear.None)
                .onComplete(() => {
                    // Start recovery tween
                    new Tween(gun.position)
                        .to({ z: originalGunPosition.z, y: originalGunPosition.y }, 100)
                        .easing(Easing.Linear.None)
                        .onComplete(() => {
                            isRecoiling = false;
                        })
                        .start();
                })
                .start();
            
            // Optional: Add rotation recoil
            new Tween(gun.rotation)
                .to({ x: originalGunRotation.x + 0.05 }, 100)
                .easing(Easing.Linear.None)
                .onComplete(() => {
                    new Tween(gun.rotation)
                        .to({ x: originalGunRotation.x }, 100)
                        .easing(Easing.Linear.None)
                        .start();
                })
                .start();
        }
    };

    // ---------- Animation Loop ----------
    const clock = new THREE.Clock();

    function animate(time) {
        requestAnimationFrame(animate);

        deltaTime = clock.getDelta();

        // Update TWEEN animations with the current time
        Tween.update(time);

        // ---------- Handle Running ----------
        if (move.run && !isCrouching) {
            currentSpeed = runSpeed;
        } else {
            currentSpeed = walkSpeed;
        }

        // ---------- Update Velocity Based on Movement ----------
        velocity.x -= velocity.x * 10.0 * deltaTime;
        velocity.z -= velocity.z * 10.0 * deltaTime;

        direction.z = Number(move.forward) - Number(move.backward);
        direction.x = Number(move.right) - Number(move.left);
        direction.normalize(); // this ensures consistent movements in all directions

        if (move.forward || move.backward) velocity.z -= direction.z * currentSpeed * deltaTime;
        if (move.left || move.right) velocity.x -= direction.x * currentSpeed * deltaTime;

        controls.moveRight(- velocity.x * deltaTime);
        controls.moveForward(- velocity.z * deltaTime);

        // ---------- Handle Jumping and Gravity ----------
        if (!isOnGround) {
            verticalVelocity -= gravity * deltaTime; // Apply gravity correctly (subtracting)
            controls.getObject().position.y += verticalVelocity * deltaTime; // Update position

            // Check if player has landed
            if (controls.getObject().position.y >= standingHeight) {
                controls.getObject().position.y = standingHeight;
                isOnGround = true;
                verticalVelocity = 0;
            }
        }

        // Prevent camera from going below crouch height if crouching
        if (isCrouching && controls.getObject().position.y < crouchHeight) {
            controls.getObject().position.y = crouchHeight;
        }

        // Update Bullets
        for (let i = 0; i < bullets.length; i++) {
            const bullet = bullets[i];
            bullet.position.add(bullet.velocity);

            // Collision Detection with Targets
            for (let j = 0; j < targets.length; j++) {
                const target = targets[j];
                const distance = bullet.position.distanceTo(target.position);
                if (distance < 10) { // Assuming target radius ~10
                    // Remove target and bullet
                    scene.remove(target);
                    targets.splice(j, 1);

                    scene.remove(bullet);
                    bullets.splice(i, 1);
                    i--;
                    break;
                }
            }

            // Remove bullets that are too far away
            const distanceFromCamera = bullet.position.distanceTo(controls.getObject().position);
            if (distanceFromCamera > 1000) {
                scene.remove(bullet);
                bullets.splice(i, 1);
                i--;
            }
        }

        // Handle Recoil Animation
        // (Recoil is now handled via tweens in the recoil() function)

        renderer.render(scene, camera);
    }

    animate(performance.now());
</script>
</body>
</html>