class Game {
    constructor() {
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        this.renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('gameCanvas') });
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.shadowMap.enabled = true;
        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

        this.objects = [];
        this.mapSize = 20;
        this.cellSize = 5;
        this.map = this.generateMap();

        this.player = new Player(this.camera, this);

        this.setupLights();
        this.setupWorld();
        this.setupEventListeners();

        this.clock = new THREE.Clock();
        this.enemies = [];
        this.createEnemies();
        this.animate();
    }

    generateMap() {
        const map = [];
        for (let z = 0; z < this.mapSize; z++) {
            map[z] = [];
            for (let x = 0; x < this.mapSize; x++) {
                map[z][x] = Math.random() < 0.8 ? 0 : 1; // 0 for empty, 1 for wall
            }
        }
        return map;
    }

    setupLights() {
        const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
        this.scene.add(ambientLight);

        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
        directionalLight.position.set(50, 50, 50);
        directionalLight.castShadow = true;
        directionalLight.shadow.mapSize.width = 1024;
        directionalLight.shadow.mapSize.height = 1024;
        directionalLight.shadow.camera.near = 10;
        directionalLight.shadow.camera.far = 200;
        directionalLight.shadow.camera.left = -50;
        directionalLight.shadow.camera.right = 50;
        directionalLight.shadow.camera.top = 50;
        directionalLight.shadow.camera.bottom = -50;
        this.scene.add(directionalLight);

        const fillLight = new THREE.DirectionalLight(0xffffff, 0.3);
        fillLight.position.set(-50, 30, -50);
        this.scene.add(fillLight);

        const pointLight1 = new THREE.PointLight(0xffaa00, 0.5, 50);
        pointLight1.position.set(10, 20, 10);
        this.scene.add(pointLight1);

        const pointLight2 = new THREE.PointLight(0x00aaff, 0.5, 50);
        pointLight2.position.set(-10, 20, -10);
        this.scene.add(pointLight2);
    }

    setupWorld() {
        const floorGeometry = new THREE.PlaneGeometry(this.mapSize * this.cellSize, this.mapSize * this.cellSize);
        const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
        const floor = new THREE.Mesh(floorGeometry, floorMaterial);
        floor.rotation.x = -Math.PI / 2;
        floor.receiveShadow = true;
        this.scene.add(floor);

        const wallMaterial = new THREE.MeshStandardMaterial({ 
            color: 0x8B4513,
            roughness: 0.7,
            metalness: 0.1
        });

        const brickWidth = this.cellSize / 3;
        const brickHeight = this.cellSize / 10;
        const brickDepth = this.cellSize / 2;
        const mortarGap = 0.02;

        for (let z = 0; z < this.mapSize; z++) {
            for (let x = 0; x < this.mapSize; x++) {
                if (this.map[z][x] === 1) {
                    const wallGroup = new THREE.Group();
                    
                    for (let by = 0; by < 10; by++) {
                        for (let bx = 0; bx < 3; bx++) {
                            const brickGeometry = new THREE.BoxGeometry(brickWidth - mortarGap, brickHeight - mortarGap, brickDepth);
                            const brick = new THREE.Mesh(brickGeometry, wallMaterial);
                            
                            brick.position.set(
                                (bx - 1) * brickWidth,
                                (by + 0.5) * brickHeight - this.cellSize / 2,
                                0
                            );
                            
                            if (by % 2 === 1) {
                                brick.position.x += brickWidth / 2;
                            }
                            
                            wallGroup.add(brick);
                        }
                    }

                    wallGroup.position.set(
                        (x - this.mapSize / 2) * this.cellSize + this.cellSize / 2,
                        this.cellSize / 2,
                        (z - this.mapSize / 2) * this.cellSize + this.cellSize / 2
                    );
                    
                    wallGroup.traverse((object) => {
                        if (object instanceof THREE.Mesh) {
                            object.castShadow = true;
                            object.receiveShadow = true;
                        }
                    });

                    this.scene.add(wallGroup);
                    this.objects.push(wallGroup);
                }
            }
        }

        this.player.setPosition(0, this.cellSize / 2, 0);
    }

    setupEventListeners() {
        window.addEventListener('resize', () => this.onWindowResize(), false);
        document.getElementById('gameCanvas').addEventListener('keydown', (event) => this.player.handleKeyDown(event));
        document.getElementById('gameCanvas').addEventListener('keyup', (event) => this.player.handleKeyUp(event));
        document.getElementById('gameCanvas').addEventListener('click', () => this.player.lockPointer());
        document.addEventListener('pointerlockchange', () => this.player.onPointerLockChange());
        document.addEventListener('mousemove', (event) => this.player.handleMouseMove(event));
        document.getElementById('gameCanvas').addEventListener('mousedown', (event) => this.player.handleMouseDown(event));
    }

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

    animate() {
        requestAnimationFrame(() => this.animate());
        const delta = this.clock.getDelta();
        this.player.update(delta);
        this.updateEnemies(delta);
        this.renderer.render(this.scene, this.camera);
    }

    createEnemies() {
        const enemyYPosition = 1.1; // Base Y position for the enemies
        const bodyGeometry = new THREE.BoxGeometry(1, 2, 1); // Body dimensions
        const bodyMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 }); // Red color for the body

        const legGeometry = new THREE.BoxGeometry(0.3, 1, 0.3); // Increased leg dimensions for better proportion
        const armGeometry = new THREE.BoxGeometry(0.25, 1, 0.25); // Increased arm dimensions for better proportion
        const headGeometry = new THREE.SphereGeometry(0.5, 8, 8); // Sphere for the head

        for (let i = 0; i < 5; i++) {
            const enemy = new THREE.Group();

            // Create body
            const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
            body.position.set(0, 1, 0); // Position the body 1 unit above the ground
            enemy.add(body);

            // Create legs
            const leftLeg = new THREE.Mesh(legGeometry, bodyMaterial);
            leftLeg.position.set(-0.15, 0, 0); // Adjusted position for left leg
            enemy.add(leftLeg);

            const rightLeg = new THREE.Mesh(legGeometry, bodyMaterial);
            rightLeg.position.set(0.15, 0, 0); // Adjusted position for right leg
            enemy.add(rightLeg);

            // Create arms
            const leftArm = new THREE.Mesh(armGeometry, bodyMaterial);
            leftArm.position.set(-0.55, 1.5, 0); // Adjusted position for left arm
            enemy.add(leftArm);

            const rightArm = new THREE.Mesh(armGeometry, bodyMaterial);
            rightArm.position.set(0.55, 1.5, 0); // Adjusted position for right arm
            enemy.add(rightArm);

            // Create head
            const head = new THREE.Mesh(headGeometry, bodyMaterial);
            head.position.set(0, 2.5, 0); // Position the head
            enemy.add(head);

            // Set the position of the entire enemy group
            const angle = Math.random() * Math.PI * 2;
            const distance = Math.random() * 10 + 5;
            enemy.position.set(
                Math.cos(angle) * distance,
                enemyYPosition,
                Math.sin(angle) * distance
            );

            // Initialize userData for animation and interaction
            enemy.userData = {
                walkCycle: 0,
                speed: Math.random() * 2 + 2,
                leftLeg: leftLeg,
                rightLeg: rightLeg
            };

            this.scene.add(enemy);
            this.enemies.push(enemy);
        }
    }

    updateEnemies(delta) {
        const groundLevelY = 1; // Adjust this to your ground level
        for (let i = 0; i < this.enemies.length; i++) {
            const enemy = this.enemies[i];
            const userData = enemy.userData;

            userData.walkCycle += delta * userData.speed;

            const walkCycleSin = Math.sin(userData.walkCycle);
            userData.leftLeg.rotation.x = walkCycleSin * 0.5;
            userData.rightLeg.rotation.x = -walkCycleSin * 0.5;

            const directionToPlayer = this.player.position.clone().sub(enemy.position).normalize();
            directionToPlayer.y = 0; // Ensure no vertical movement component
            enemy.position.addScaledVector(directionToPlayer, delta * userData.speed);
            enemy.lookAt(new THREE.Vector3(this.player.position.x, enemy.position.y, this.player.position.z)); // Adjust lookAt to ignore vertical differences

            enemy.position.y = groundLevelY; // Ensure enemies are on the ground
        }
    }
}

class Revolver {
    constructor(scene) {
        this.group = new THREE.Group();
        this.loader = new THREE.GLTFLoader();

        this.loader.load(
            'models/revolver.glb',
            (gltf) => {
                this.revolverModel = gltf.scene;
                this.group.add(this.revolverModel);

                this.revolverModel.scale.set(0.4, 0.4, 0.4);

                this.revolverModel.position.set(0, 0, 0);

                this.revolverModel.rotation.set(0, 3, 0);

                this.revolverModel.traverse((object) => {
                    if (object instanceof THREE.Mesh) {
                        object.castShadow = true;
                        object.receiveShadow = true;
                    }
                });
            },
            undefined,
            (error) => {
                console.error('An error occurred while loading the revolver model:', error);
            }
        );

        scene.add(this.group);

        this.bullets = [];
        this.maxBullets = 12;
        this.currentBullets = this.maxBullets;
        this.recoilAmount = 0.05;
        this.recoilDuration = 0.1;
        this.recoveryDuration = 0.3;
        this.recoilState = {
            active: false,
            timer: 0,
            startRotation: new THREE.Euler(),
            startPosition: new THREE.Vector3()
        };
    }

    shoot(scene, camera) {
        if (this.currentBullets > 0) {
            const bulletGeometry = new THREE.SphereGeometry(0.02, 8, 8);
            const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
            const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);

            const offsetRight = new THREE.Vector3(0.6, 0, 0);
            const bulletPosition = this.group.position.clone().add(offsetRight);
            bullet.position.copy(bulletPosition);

            bullet.quaternion.copy(camera.quaternion);

            const velocity = new THREE.Vector3(0, 0, -1);
            velocity.applyQuaternion(camera.quaternion);
            bullet.userData.velocity = velocity.multiplyScalar(50);

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

            this.currentBullets--;
            this.applyRecoil();
        }
    }

    applyRecoil() {
        this.recoilState.active = true;
        this.recoilState.timer = 0;
        this.recoilState.startRotation.copy(this.group.rotation);
        this.recoilState.startPosition.copy(this.group.position);
    }

    updateBullets(delta) {
        for (let i = this.bullets.length - 1; i >= 0; i--) {
            const bullet = this.bullets[i];
            bullet.position.add(bullet.userData.velocity.clone().multiplyScalar(delta));

            if (bullet.position.length() > 1000) {
                this.bullets.splice(i, 1);
                bullet.geometry.dispose();
                bullet.material.dispose();
                bullet.parent.remove(bullet);
            }
        }
    }

    updatePosition(camera, delta) {
        const vector = new THREE.Vector3(0.3, -0.2, -0.5);
        vector.applyQuaternion(camera.quaternion);
        this.group.position.copy(camera.position).add(vector);
        this.group.rotation.copy(camera.rotation);

        if (this.recoilState.active) {
            this.recoilState.timer += delta;
            
            if (this.recoilState.timer <= this.recoilDuration) {
                const progress = this.recoilState.timer / this.recoilDuration;
                const easeOutQuad = 1 - (1 - progress) * (1 - progress);
                
                this.group.rotation.x = this.recoilState.startRotation.x - this.recoilAmount * easeOutQuad;
                this.group.position.y = this.recoilState.startPosition.y + this.recoilAmount * easeOutQuad;
            } else if (this.recoilState.timer <= this.recoilDuration + this.recoveryDuration) {
                const progress = (this.recoilState.timer - this.recoilDuration) / this.recoveryDuration;
                const easeInOutQuad = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;
                
                this.group.rotation.x = this.recoilState.startRotation.x - this.recoilAmount * (1 - easeInOutQuad);
                this.group.position.y = this.recoilState.startPosition.y + this.recoilAmount * (1 - easeInOutQuad);
            } else {
                this.recoilState.active = false;
                this.group.rotation.x = this.recoilState.startRotation.x;
                this.group.position.y = this.recoilState.startPosition.y;
            }
        }
    }
}

class Player {
    constructor(camera, game) {
        this.camera = camera;
        this.game = game;
        this.moveSpeed = 10;
        this.turnSpeed = 0.002;
        this.velocity = new THREE.Vector3();
        this.moveForward = false;
        this.moveBackward = false;
        this.moveLeft = false;
        this.moveRight = false;
        this.position = new THREE.Vector3();
        this.rotation = new THREE.Euler(0, 0, 0, 'YXZ');
        this.isPointerLocked = false;

        this.camera.position.y = 1.6;

        this.revolver = new Revolver(game.scene);

        this.shootCooldown = 0;
        this.shootCooldownTime = 0.5;
    }

    setPosition(x, y, z) {
        this.position.set(x, y, z);
        this.camera.position.copy(this.position);
    }

    handleKeyDown(event) {
        switch (event.code) {
            case 'KeyW': this.moveForward = true; break;
            case 'KeyS': this.moveBackward = true; break;
            case 'KeyA': this.moveLeft = true; break;
            case 'KeyD': this.moveRight = true; break;
        }
    }

    handleKeyUp(event) {
        switch (event.code) {
            case 'KeyW': this.moveForward = false; break;
            case 'KeyS': this.moveBackward = false; break;
            case 'KeyA': this.moveLeft = false; break;
            case 'KeyD': this.moveRight = false; break;
        }
    }

    lockPointer() {
        document.getElementById('gameCanvas').requestPointerLock();
    }

    onPointerLockChange() {
        this.isPointerLocked = document.pointerLockElement === document.getElementById('gameCanvas');
    }

    handleMouseMove(event) {
        if (!this.isPointerLocked) return;

        const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
        const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

        this.rotation.y -= movementX * this.turnSpeed;
        this.rotation.x -= movementY * this.turnSpeed;

        this.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.rotation.x));

        this.camera.rotation.copy(this.rotation);
    }

    handleMouseDown(event) {
        if (event.button === 0) {
            this.shoot();
        }
    }

    shoot() {
        if (this.shootCooldown <= 0) {
            this.revolver.shoot(this.game.scene, this.camera);
            this.shootCooldown = this.shootCooldownTime;
        }
    }

    update(delta) {
        const moveDirection = new THREE.Vector3();
        if (this.moveForward) moveDirection.z -= 1;
        if (this.moveBackward) moveDirection.z += 1;
        if (this.moveLeft) moveDirection.x -= 1;
        if (this.moveRight) moveDirection.x += 1;
        moveDirection.normalize();

        moveDirection.applyEuler(new THREE.Euler(0, this.rotation.y, 0));

        const newPosition = this.position.clone();
        newPosition.addScaledVector(moveDirection, this.moveSpeed * delta);

        if (this.canMoveTo(newPosition)) {
            this.position.copy(newPosition);
            this.camera.position.copy(this.position);
        }

        this.revolver.updatePosition(this.camera, delta);

        if (this.shootCooldown > 0) {
            this.shootCooldown -= delta;
        }

        this.revolver.updateBullets(delta);
    }

    canMoveTo(position) {
        const mapX = Math.floor((position.x + this.game.mapSize * this.game.cellSize / 2) / this.game.cellSize);
        const mapZ = Math.floor((position.z + this.game.mapSize * this.game.cellSize / 2) / this.game.cellSize);

        if (mapX < 0 || mapX >= this.game.mapSize || mapZ < 0 || mapZ >= this.game.mapSize) {
            return false;
        }

        return this.game.map[mapZ][mapX] === 0;
    }
}

const game = new Game(); Take this code and make enemies fall down and disappear when they get a shot