Make the goblins drop coins that can be picked up and add them to your gold. Display gold in the top left under health. Make it golden text and draw a coin next to it.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>3D Environment with Labyrinth and Enhanced Enemies</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }

        /* HUD Container */
        #hud {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            font-family: Arial, sans-serif;
            font-size: 20px;
            z-index: 1;
        }

        /* Health Bar Container */
        #healthBarContainer {
            width: 200px;
            height: 25px;
            background-color: #555; /* Dark gray background */
            border: 2px solid #fff; /* White border */
            border-radius: 5px;
            overflow: hidden;
            margin-bottom: 10px; /* Spacing below the health bar */
        }

        /* Health Bar (Foreground) */
        #healthBar {
            width: 100%; /* Full width initially */
            height: 100%;
            background-color: green; /* Initial color based on health */
            transition: width 0.2s ease, background-color 0.2s ease; /* Smooth transitions */
        }

        /* Time Display */
        #timeDisplay {
            margin-top: 10px; /* Spacing above the time display */
        }

        /* Game Over screen */
        #gameOver {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: red;
            font-family: Arial, sans-serif;
            font-size: 50px;
            display: none;
            z-index: 2;
        }

        /* YOU WIN screen */
        #youWin {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: #000033; /* Dark blue background */
            display: none;
            z-index: 3;
            overflow: hidden;
        }

        #winText {
            position: absolute;
            top: 20%;
            width: 100%;
            text-align: center;
            color: #FF00FF; /* Magenta */
            font-family: 'Comic Sans MS', cursive, sans-serif;
            font-size: 80px;
            text-shadow: 2px 2px #00FFFF; /* Cyan shadow */
        }
    </style>
</head>
<body>
    <!-- HUD Elements -->
    <div id="hud">
        <!-- Health Bar -->
        <div id="healthBarContainer">
            <div id="healthBar"></div>
        </div>
        <!-- Time Display -->
        <div id="timeDisplay">Time: 06:00 AM</div>
    </div>
    <!-- Game Over Screen -->
    <div id="gameOver">Game Over</div>
    <!-- YOU WIN Screen -->
    <div id="youWin">
        <div id="winText">YOU WIN!</div>
        <canvas id="winCanvas"></canvas>
    </div>
    <!-- Three.js and Controls Libraries -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/objects/Sky.js"></script>
    <script>
        // Set up scene, camera, and renderer
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 10, 10);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        const mazeWallsBoxes = []; // Array to hold bounding boxes for collision
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // HUD Elements
        const healthBar = document.getElementById('healthBar');
        const timeDisplay = document.getElementById('timeDisplay');
        const gameOverScreen = document.getElementById('gameOver');
        const youWinScreen = document.getElementById('youWin');

        // Player Health
        let playerHealth = 100;

        // Add ambient light and directional light
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
        scene.add(ambientLight);
        const sunLight = new THREE.DirectionalLight(0xffffff, 1);
        sunLight.position.set(100, 100, 100);
        scene.add(sunLight);

        // Add Sky Shader
        const sky = new THREE.Sky();
        sky.scale.setScalar(450000); // Scale the sky to cover a large area
        scene.add(sky);

        const sun = new THREE.Vector3();

        // Skybox parameters
        const skyUniforms = sky.material.uniforms;
        skyUniforms['turbidity'].value = 10;
        skyUniforms['rayleigh'].value = 2;
        skyUniforms['mieCoefficient'].value = 0.005;
        skyUniforms['mieDirectionalG'].value = 0.8;

        // Set the initial sun position
        const thetaInitial = Math.PI * (0.25); // Elevation angle in radians for midday
        const phiInitial = 2 * Math.PI * (0.25); // Azimuthal angle in radians
        sun.setFromSphericalCoords(1, thetaInitial, phiInitial);
        sky.material.uniforms['sunPosition'].value.copy(sun);
        sunLight.position.copy(sun);

        // Add ground with rolling hills
        const terrainGeometry = new THREE.PlaneBufferGeometry(1000, 1000, 128, 128);
        const terrainMaterial = new THREE.MeshLambertMaterial({ color: 0x228B22 });
        const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
        terrain.rotation.x = -Math.PI / 2;

        const positionAttr = terrainGeometry.attributes.position;
        for (let i = 0; i < positionAttr.count; i++) {
            const x = positionAttr.getX(i);
            const y = positionAttr.getY(i);
            const z = Math.sin(x / 20) * 5 + Math.cos(y / 20) * 5;
            positionAttr.setZ(i, z);
        }
        terrainGeometry.computeVertexNormals();
        scene.add(terrain);

        // Function to get the height of the terrain at a given (x, z) position
        function getTerrainHeightAt(x, z) {
            const terrainPosition = terrainGeometry.attributes.position;
            const terrainWidth = terrainGeometry.parameters.width;
            const terrainSegments = terrainGeometry.parameters.widthSegments;

            const xNormalized = (x + terrainWidth / 2) / terrainWidth;
            const zNormalized = (z + terrainWidth / 2) / terrainWidth;

            const xIndex = xNormalized * terrainSegments;
            const zIndex = zNormalized * terrainSegments;

            const x0 = Math.floor(xIndex);
            const x1 = Math.min(x0 + 1, terrainSegments);
            const z0 = Math.floor(zIndex);
            const z1 = Math.min(z0 + 1, terrainSegments);

            const s1 = xIndex - x0;
            const s0 = 1 - s1;
            const t1 = zIndex - z0;
            const t0 = 1 - t1;

            const index00 = z0 * (terrainSegments + 1) + x0;
            const index01 = z1 * (terrainSegments + 1) + x0;
            const index10 = z0 * (terrainSegments + 1) + x1;
            const index11 = z1 * (terrainSegments + 1) + x1;

            const height00 = terrainPosition.getZ(index00);
            const height01 = terrainPosition.getZ(index01);
            const height10 = terrainPosition.getZ(index10);
            const height11 = terrainPosition.getZ(index11);

            const height = (s0 * t0 * height00) + (s1 * t0 * height10) + (s0 * t1 * height01) + (s1 * t1 * height11);
            return height;
        }

        // Add trees
        for (let i = 0; i < 100; i++) {
            // Generate random position for the tree
            const treeX = Math.random() * 500 - 250;
            const treeZ = Math.random() * 500 - 250;
            const treeY = getTerrainHeightAt(treeX, treeZ);

            // Create tree trunk
            const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.5, 10, 8);
            const trunkMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
            const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
            trunk.position.set(treeX, treeY + 5, treeZ);

            // Create evergreen leaves as stacked cones
            const leavesMaterial = new THREE.MeshLambertMaterial({ color: 0x228B22 });
            for (let j = 0; j < 3; j++) {
                const leavesGeometry = new THREE.ConeGeometry(3 + j * 1.5, 8, 16); // Biggest on bottom, smallest on top
                const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial);
                leaves.position.set(0, 10 - j * 3, 0);
                trunk.add(leaves);
            }

            scene.add(trunk);
        }

        // Create a sword with blade, crossbar, and hilt
        const swordGroup = new THREE.Group();

        // Blade
        const bladeGeometry = new THREE.ConeGeometry(0.1, 1.5, 4);
        const bladeMaterial = new THREE.MeshLambertMaterial({ color: 0xC0C0C0 });
        const blade = new THREE.Mesh(bladeGeometry, bladeMaterial);
        blade.position.set(0, 0.75, 0);
        swordGroup.add(blade);

        // Crossbar
        const crossbarGeometry = new THREE.BoxGeometry(0.4, 0.05, 0.1);
        const crossbarMaterial = new THREE.MeshLambertMaterial({ color: 0x808080 });
        const crossbar = new THREE.Mesh(crossbarGeometry, crossbarMaterial);
        crossbar.position.set(0, 0, 0);
        swordGroup.add(crossbar);

        // Extra crossbar (vertical and perpendicular to the current one)
        const extraCrossbarGeometry = new THREE.BoxGeometry(0.4, 0.05, 0.1);
        const extraCrossbarMaterial = new THREE.MeshLambertMaterial({ color: 0x808080 });
        const extraCrossbar = new THREE.Mesh(extraCrossbarGeometry, extraCrossbarMaterial);
        extraCrossbar.rotation.y = Math.PI / 2;
        extraCrossbar.position.set(0, 0, 0);
        swordGroup.add(extraCrossbar);

        // Hilt
        const hiltGeometry = new THREE.CylinderGeometry(0.05, 0.05, 0.5, 8);
        const hiltMaterial = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
        const hilt = new THREE.Mesh(hiltGeometry, hiltMaterial);
        hilt.position.set(0, -0.25, 0);
        swordGroup.add(hilt);

        // Position the sword relative to the camera
        swordGroup.position.set(0.5, -0.5, -1);
        camera.add(swordGroup);
        scene.add(camera);

        // Pointer Lock Controls for first-person movement
        const controls = new THREE.PointerLockControls(camera, document.body);
        document.body.addEventListener('click', () => {
            controls.lock();
        });

        // WASD movement controls for the player
        const movement = { forward: false, backward: false, left: false, right: false, jump: false, running: false };
        document.addEventListener('keydown', (event) => {
            switch (event.code) {
                case 'KeyW': movement.forward = true; break;
                case 'KeyS': movement.backward = true; break;
                case 'KeyA': movement.left = true; break;
                case 'KeyD': movement.right = true; break;
                case 'Space': if (!movement.jump) { velocity.y = 0.6; movement.jump = true; } break;
                case 'ShiftLeft': movement.running = true; break;
                case 'Digit1': incrementTimeByOneHour(); break; // Handle "1" key press
            }
        });
        document.addEventListener('keyup', (event) => {
            switch (event.code) {
                case 'KeyW': movement.forward = false; break;
                case 'KeyS': movement.backward = false; break;
                case 'KeyA': movement.left = false; break;
                case 'KeyD': movement.right = false; break;
                case 'Space': movement.jump = false; break;
                case 'ShiftLeft': movement.running = false; break;
            }
        });

        // Gravity and velocity setup
        let velocity = new THREE.Vector3();
        const gravity = -0.05;

        // Array to hold all goblins
        const goblins = [];

        // Sword swing variables
        let isSwinging = false;
        let isBlocking = false; // Variable to track blocking state
        const swordDamage = 25; // Damage per swing

        // Sword rotation targets
        let swordRotationTargetZ = 0; // Target rotation for Z axis (roll)
        let swordRotationTargetX = 0; // Target rotation for X axis (swing)

        // Prevent context menu on right-click
        document.addEventListener('contextmenu', (event) => {
            event.preventDefault();
        });

        document.addEventListener('mousedown', (event) => {
            if (event.button === 0 && !isSwinging && !isBlocking) { // Left mouse button click
                isSwinging = true;
                const swingDuration = 200; // Swing duration in ms
                const swingSteps = swingDuration / 10;
                let currentStep = 0;
                const maxSwingAngle = Math.PI / 2; // 90 degrees

                const swingInterval = setInterval(() => {
                    currentStep++;
                    const swingProgress = currentStep / swingSteps;
                    const swingAngle = swingProgress * maxSwingAngle;

                    // Rotate the sword for swinging effect
                    swordGroup.rotation.x = -swingAngle;

                    // Detect hits during the swing
                    if (swingProgress > 0.3 && swingProgress < 0.7) { // Active swing window
                        const swordWorldPosition = new THREE.Vector3();
                        swordGroup.getWorldPosition(swordWorldPosition);

                        // Check for collisions with goblins
                        goblins.forEach(goblin => {
                            if (!goblin.isHit) { // Prevent multiple hits per swing
                                const distance = swordWorldPosition.distanceTo(goblin.mesh.position);
                                if (distance < 3) { // Increased collision distance for easier hits
                                    goblin.takeDamage(swordDamage);
                                    goblin.isHit = true;
                                }
                            }
                        });
                    }

                    if (currentStep >= swingSteps) { // End of swing
                        clearInterval(swingInterval);
                        swordGroup.rotation.x = 0;
                        isSwinging = false;

                        // Reset goblins' hit status after swing
                        goblins.forEach(goblin => goblin.isHit = false);
                    }
                }, 10);
            } else if (event.button === 2) { // Right mouse button click for blocking
                isBlocking = true;
                swordRotationTargetZ = Math.PI / 4; // Set target rotation for blocking
            }
        });

        document.addEventListener('mouseup', (event) => {
            if (event.button === 2) { // Right mouse button released
                isBlocking = false;
                swordRotationTargetZ = 0; // Reset target rotation
            }
        });

        // Bilinear interpolation for smoother terrain height detection
        function getInterpolatedHeightAt(x, z) {
            const terrainPosition = terrainGeometry.attributes.position;
            const terrainWidth = terrainGeometry.parameters.width;
            const terrainSegments = terrainGeometry.parameters.widthSegments;

            const xNormalized = (x + terrainWidth / 2) / terrainWidth;
            const zNormalized = (z + terrainWidth / 2) / terrainWidth;

            const xIndex = xNormalized * terrainSegments;
            const zIndex = zNormalized * terrainSegments;

            const x0 = Math.floor(xIndex);
            const x1 = Math.min(x0 + 1, terrainSegments);
            const z0 = Math.floor(zIndex);
            const z1 = Math.min(z0 + 1, terrainSegments);

            const s1 = xIndex - x0;
            const s0 = 1 - s1;
            const t1 = zIndex - z0;
            const t0 = 1 - t1;

            const index00 = z0 * (terrainSegments + 1) + x0;
            const index01 = z1 * (terrainSegments + 1) + x0;
            const index10 = z0 * (terrainSegments + 1) + x1;
            const index11 = z1 * (terrainSegments + 1) + x1;

            const height00 = terrainPosition.getZ(index00);
            const height01 = terrainPosition.getZ(index01);
            const height10 = terrainPosition.getZ(index10);
            const height11 = terrainPosition.getZ(index11);

            const height = (s0 * t0 * height00) + (s1 * t0 * height10) + (s0 * t1 * height01) + (s1 * t1 * height11);
            return height;
        }

        // Maze Walls Collision Group and Bounding Boxes
        let mazeWallsGroup = new THREE.Group();

        // Maze Class
        class Maze {
            constructor(width, height) {
                this.width = width;
                this.height = height;

                // Initialize maze grid
                this.grid = [];
                for (let x = 0; x < width; x++) {
                    this.grid[x] = [];
                    for (let y = 0; y < height; y++) {
                        this.grid[x][y] = {
                            visited: false,
                            walls: { top: true, right: true, bottom: true, left: true }
                        };
                    }
                }

                // Generate maze
                this.generateMaze(0, 0);
            }

            // Recursive Backtracker algorithm
            generateMaze(x, y) {
                const stack = [];
                stack.push({ x: x, y: y });
                this.grid[x][y].visited = true;

                while (stack.length > 0) {
                    const current = stack.pop();
                    const neighbors = this.getUnvisitedNeighbors(current.x, current.y);

                    if (neighbors.length > 0) {
                        stack.push(current);

                        // Choose a random neighbor
                        const next = neighbors[Math.floor(Math.random() * neighbors.length)];

                        // Remove walls between current and next
                        if (next.x > current.x) {
                            this.grid[current.x][current.y].walls.right = false;
                            this.grid[next.x][next.y].walls.left = false;
                        } else if (next.x < current.x) {
                            this.grid[current.x][current.y].walls.left = false;
                            this.grid[next.x][next.y].walls.right = false;
                        } else if (next.y > current.y) {
                            this.grid[current.x][current.y].walls.bottom = false;
                            this.grid[next.x][next.y].walls.top = false;
                        } else if (next.y < current.y) {
                            this.grid[current.x][current.y].walls.top = false;
                            this.grid[next.x][next.y].walls.bottom = false;
                        }

                        this.grid[next.x][next.y].visited = true;
                        stack.push(next);
                    }
                }
            }

            getUnvisitedNeighbors(x, y) {
                const neighbors = [];

                if (x > 0 && !this.grid[x - 1][y].visited) {
                    neighbors.push({ x: x - 1, y: y });
                }
                if (x < this.width - 1 && !this.grid[x + 1][y].visited) {
                    neighbors.push({ x: x + 1, y: y });
                }
                if (y > 0 && !this.grid[x][y - 1].visited) {
                    neighbors.push({ x: x, y: y - 1 });
                }
                if (y < this.height - 1 && !this.grid[x][y + 1].visited) {
                    neighbors.push({ x: x, y: y + 1 });
                }

                return neighbors;
            }
        }

        // Function to create the maze mesh
        function createMazeMesh(maze, positionX, positionZ) {
            const cellSize = mazeCellSize;
            const wallHeight = 5;
            const wallThickness = 0.3; // Reduced thickness for wider passageways
            const wallMaterial = new THREE.MeshLambertMaterial({ color: 0x888888 });
            const mazeGroup = new THREE.Group();

            // Group to hold all walls for collision
            mazeWallsGroup = new THREE.Group();

            for (let x = 0; x < maze.width; x++) {
                for (let y = 0; y < maze.height; y++) {
                    const cell = maze.grid[x][y];
                    const cellX = positionX + x * cellSize;
                    const cellZ = positionZ + y * cellSize;
                    const cellY = getTerrainHeightAt(cellX, cellZ);

                    // Top wall
                    if (cell.walls.top) {
                        const wallGeometry = new THREE.BoxGeometry(cellSize, wallHeight, wallThickness);
                        const wall = new THREE.Mesh(wallGeometry, wallMaterial);
                        wall.position.set(
                            cellX,
                            cellY + wallHeight / 2,
                            cellZ - cellSize / 2
                        );
                        mazeGroup.add(wall);
                        mazeWallsGroup.add(wall);
                        const box = new THREE.Box3().setFromObject(wall);
                        mazeWallsBoxes.push(box);
                    }

                    // Bottom wall
                    if (cell.walls.bottom) {
                        const wallGeometry = new THREE.BoxGeometry(cellSize, wallHeight, wallThickness);
                        const wall = new THREE.Mesh(wallGeometry, wallMaterial);
                        wall.position.set(
                            cellX,
                            cellY + wallHeight / 2,
                            cellZ + cellSize / 2
                        );
                        mazeGroup.add(wall);
                        mazeWallsGroup.add(wall);
                        const box = new THREE.Box3().setFromObject(wall);
                        mazeWallsBoxes.push(box);
                    }

                    // Left wall
                    if (cell.walls.left) {
                        const wallGeometry = new THREE.BoxGeometry(wallThickness, wallHeight, cellSize);
                        const wall = new THREE.Mesh(wallGeometry, wallMaterial);
                        wall.position.set(
                            cellX - cellSize / 2,
                            cellY + wallHeight / 2,
                            cellZ
                        );
                        mazeGroup.add(wall);
                        mazeWallsGroup.add(wall);
                        const box = new THREE.Box3().setFromObject(wall);
                        mazeWallsBoxes.push(box);
                    }

                    // Right wall
                    if (cell.walls.right) {
                        const wallGeometry = new THREE.BoxGeometry(wallThickness, wallHeight, cellSize);
                        const wall = new THREE.Mesh(wallGeometry, wallMaterial);
                        wall.position.set(
                            cellX + cellSize / 2,
                            cellY + wallHeight / 2,
                            cellZ
                        );
                        mazeGroup.add(wall);
                        mazeWallsGroup.add(wall);
                        const box = new THREE.Box3().setFromObject(wall);
                        mazeWallsBoxes.push(box);
                    }
                }
            }

            scene.add(mazeGroup);
            scene.add(mazeWallsGroup);

            return mazeWallsBoxes; // Return the array of bounding boxes
        }

        // Function to flatten terrain under the maze
        function flattenTerrainUnderMaze(positionX, positionZ, mazeWidth, mazeHeight, cellSize) {
            const terrainPosition = terrainGeometry.attributes.position;

            // Maze bounding box
            const minX = positionX - cellSize / 2;
            const maxX = positionX + mazeWidth * cellSize - cellSize / 2;
            const minZ = positionZ - cellSize / 2;
            const maxZ = positionZ + mazeHeight * cellSize - cellSize / 2;

            for (let i = 0; i < terrainPosition.count; i++) {
                const x = terrainPosition.getX(i);
                const z = terrainPosition.getY(i); // Note: After rotation, y corresponds to z in world space

                if (x >= minX && x <= maxX && z >= minZ && z <= maxZ) {
                    // Set height to flat value, e.g., 0
                    terrainPosition.setZ(i, 0); // z is height (y-coordinate in world space)
                }
            }
            terrainGeometry.computeVertexNormals();
            terrainGeometry.attributes.position.needsUpdate = true;
        }

        // Generate the maze
        const mazeWidth = 20;
        const mazeHeight = 20;
        const maze = new Maze(mazeWidth, mazeHeight);

        // Choose a random position on the map
        const terrainSize = 1000;
        const mazeCellSize = 4;
        const mazeTotalSize = mazeWidth * mazeCellSize;

        const mazePositionX = Math.random() * (terrainSize - mazeTotalSize) - terrainSize / 2 + mazeTotalSize / 2;
        const mazePositionZ = Math.random() * (terrainSize - mazeTotalSize) - terrainSize / 2 + mazeTotalSize / 2;

        // Flatten the terrain under the maze
        flattenTerrainUnderMaze(mazePositionX, mazePositionZ, mazeWidth, mazeHeight, mazeCellSize);

        // Create the maze mesh
        // createMazeMesh(maze, mazePositionX, mazePositionZ);

        // Create a pulsing golden orb at the center of the maze
        const mazeCenterX = mazePositionX + (mazeWidth / 2) * mazeCellSize;
        const mazeCenterZ = mazePositionZ + (mazeHeight / 2) * mazeCellSize;
        const mazeCenterY = getTerrainHeightAt(mazeCenterX, mazeCenterZ) + 2.5; // Slightly above ground

        // Orb geometry and material
        const orbGeometry = new THREE.SphereGeometry(1, 32, 32);
        const orbMaterial = new THREE.MeshStandardMaterial({
            color: 0xFFD700,
            emissive: 0xFFD700,
            emissiveIntensity: 1,
            roughness: 0.3,
            metalness: 0.7,
        });
        const goldenOrb = new THREE.Mesh(orbGeometry, orbMaterial);
        goldenOrb.position.set(mazeCenterX, mazeCenterY, mazeCenterZ);
        scene.add(goldenOrb);

        // Add a PointLight to emit golden light
        const orbLight = new THREE.PointLight(0xFFD700, 2, 50); // Color, intensity, distance
        orbLight.position.set(mazeCenterX, mazeCenterY, mazeCenterZ);
        orbLight.castShadow = true; // Enable shadows for dramatic effect
        scene.add(orbLight);

        // Optional: Add a glowing effect using a small SphereGeometry with a transparent material
        const glowMaterial = new THREE.MeshBasicMaterial({
            color: 0xFFD700,
            transparent: true,
            opacity: 0.5,
        });
        const glowSphere = new THREE.Mesh(new THREE.SphereGeometry(1.5, 32, 32), glowMaterial);
        glowSphere.position.set(mazeCenterX, mazeCenterY, mazeCenterZ);
        scene.add(glowSphere);

        // Goblin Class Definition
        class Goblin {
            constructor(x, z) {
                // Create goblin mesh
                this.mesh = new THREE.Group();

                // Body
                const bodyGeometry = new THREE.CylinderGeometry(0.5, 0.5, 2, 8);
                const bodyMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 }); // Red color
                this.body = new THREE.Mesh(bodyGeometry, bodyMaterial);
                this.body.position.y = 1;
                this.mesh.add(this.body);

                // Head
                const headGeometry = new THREE.SphereGeometry(0.6, 16, 16);
                const headMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 }); // Red color
                this.head = new THREE.Mesh(headGeometry, headMaterial);
                this.head.position.y = 2.5;
                this.mesh.add(this.head);

                // Arms
                const armGeometry = new THREE.CylinderGeometry(0.1, 0.1, 1, 8);
                const armMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
                // Left Arm
                this.leftArm = new THREE.Mesh(armGeometry, armMaterial);
                this.leftArm.position.set(-0.6, 1.5, 0);
                this.leftArm.rotation.z = Math.PI / 4;
                this.mesh.add(this.leftArm);
                // Right Arm
                this.rightArm = new THREE.Mesh(armGeometry, armMaterial);
                this.rightArm.position.set(0.6, 1.5, 0);
                this.rightArm.rotation.z = -Math.PI / 4;
                this.mesh.add(this.rightArm);

                // Legs
                const legGeometry = new THREE.CylinderGeometry(0.1, 0.1, 1, 8);
                const legMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
                // Left Leg
                this.leftLeg = new THREE.Mesh(legGeometry, legMaterial);
                this.leftLeg.position.set(-0.3, -0.5, 0);
                this.mesh.add(this.leftLeg);
                // Right Leg
                this.rightLeg = new THREE.Mesh(legGeometry, legMaterial);
                this.rightLeg.position.set(0.3, -0.5, 0);
                this.mesh.add(this.rightLeg);

                // Weapon (e.g., club)
                const weaponGeometry = new THREE.BoxGeometry(0.2, 1, 0.2);
                const weaponMaterial = new THREE.MeshLambertMaterial({ color: 0x654321 });
                this.weapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
                this.weapon.position.set(0, 1, -0.6);
                this.mesh.add(this.weapon);

                // Position goblin on terrain
                this.mesh.position.set(x, getTerrainHeightAt(x, z) + 1, z);

                // Add to scene
                scene.add(this.mesh);

                // Health
                this.health = 100;

                // Movement speed
                this.speed = 0.12;

                // Vertical velocity for gravity
                this.velocityY = 0;

                // Flag to prevent multiple hits per swing
                this.isHit = false;

                // Attack cooldown
                this.attackCooldown = 0;

                // Animation phase for walking
                this.walkPhase = Math.random() * Math.PI * 2; // Random initial phase
                this.walkSpeed = 0.05; // Speed of walking animation

                // Target position for wandering
                this.targetPosition = null;

                // Random movement cooldown
                this.wanderCooldown = 0;

                // State management
                this.state = 'wandering'; // 'wandering', 'approaching_orbit', 'orbiting', 'rearing_back', 'charging', 'pausing'
                this.orbitStartTime = null;
                this.orbitDuration = 2000; // Duration to orbit in milliseconds
                this.angularSpeed = 0.0008; // Radians per millisecond (slower orbit)
                this.orbitRadius = 5; // Orbit radius
                this.orbitAngle = 0;

                this.orbitingClock = new THREE.Clock(false); // Clock to manage orbit duration

                // Variables for smooth transition to orbit
                this.approachSpeed = 0.08; // Speed when approaching orbit position
                this.hasReachedOrbit = false; // Flag to indicate if goblin has reached orbit path

                // Variables for pausing state
                this.pauseStartTime = 0;
                this.pauseDuration = 1000; // Pause duration in milliseconds

                // Variables for rearing back state
                this.rearStartTime = 0;
                this.rearDuration = 1000; // Time in milliseconds to rear back

                // Initial rotation for rearing back animation
                this.originalBodyRotationX = this.body.rotation.x;

                // Charge behavior variables
                this.chargeStopDistance = 3; // Distance at which the goblin stops before the player during charge
            }

            // Method to move goblin towards the player
            moveTowardsPlayer(deltaTimeMs) {
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);
                const direction = new THREE.Vector3();
                direction.subVectors(playerPosition, this.mesh.position).normalize();
                const moveStep = direction.multiplyScalar(this.speed * deltaTimeMs * 0.06); // Adjust for deltaTime

                // Check collision before moving
                if (!checkCollision(this.mesh.position.x + moveStep.x, this.mesh.position.z + moveStep.z, goblinCollisionRadius)) {
                    this.mesh.position.add(moveStep);
                }

                // Rotate to face the player
                const angle = Math.atan2(direction.x, direction.z);
                this.mesh.rotation.y = angle;
            }

            // Method to move goblin randomly
            wander(deltaTimeMs) {
                if (!this.targetPosition || this.wanderCooldown <= 0) {
                    // Choose a new random direction
                    const angle = Math.random() * Math.PI * 2;
                    const distance = 5 + Math.random() * 10;
                    this.targetPosition = new THREE.Vector3(
                        this.mesh.position.x + Math.cos(angle) * distance,
                        this.mesh.position.y,
                        this.mesh.position.z + Math.sin(angle) * distance
                    );
                    this.wanderCooldown = 100 + Math.random() * 100;
                } else {
                    const direction = new THREE.Vector3();
                    direction.subVectors(this.targetPosition, this.mesh.position).normalize();
                    const moveStep = direction.multiplyScalar(this.speed * deltaTimeMs * 0.06); // Adjust for deltaTime

                    // Check collision before moving
                    if (!checkCollision(this.mesh.position.x + moveStep.x, this.mesh.position.z + moveStep.z, goblinCollisionRadius)) {
                        this.mesh.position.add(moveStep);
                    }

                    // Rotate to face movement direction
                    const angle = Math.atan2(direction.x, direction.z);
                    this.mesh.rotation.y = angle;

                    // If reached target position
                    if (this.mesh.position.distanceTo(this.targetPosition) < 1) {
                        this.targetPosition = null;
                    }

                    this.wanderCooldown--;
                }
            }

            // Method to apply damage to goblin
            takeDamage(amount) {
                this.health -= amount;
                console.log('Goblin hit! Health:', this.health);
                if (this.health <= 0) {
                    this.die();
                }
            }

            // Method to handle goblin death
            die() {
                scene.remove(this.mesh);
                const index = goblins.indexOf(this);
                if (index > -1) {
                    goblins.splice(index, 1);
                }
                console.log('Goblin died!');
            }

            // Method to check if goblin can attack the player
            canAttackPlayer() {
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);
                const distance = playerPosition.distanceTo(this.mesh.position);
                return distance < 2; // Attack range
            }

            // Method to attack the player
            attackPlayer() {
                if (this.attackCooldown <= 0) {
                    if (!isBlocking) {
                        playerHealth -= 10; // Damage per attack
                        updateHealthBar();
                        console.log('Player hit! Health:', playerHealth);
                        if (playerHealth <= 0) {
                            gameOver();
                        }
                    } else {
                        console.log('Attack blocked!');
                        // You can add visual or sound effects here
                    }
                    this.attackCooldown = 1000; // Cooldown in milliseconds
                }
            }

            // Apply gravity
            applyGravity(deltaTimeMs) {
                const gravityPerMs = gravity / (1000 / 60); // gravity per millisecond
                this.velocityY += gravityPerMs * deltaTimeMs;
                this.mesh.position.y += this.velocityY * deltaTimeMs;

                // Collision detection with terrain
                const terrainHeight = getInterpolatedHeightAt(this.mesh.position.x, this.mesh.position.z) + 1;
                if (this.mesh.position.y < terrainHeight) {
                    this.mesh.position.y = terrainHeight;
                    this.velocityY = 0;
                }
            }

            // Update method for goblin behavior and animations
            update(deltaTimeMs) {
                const currentTime = performance.now();
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);
                const distanceToPlayer = playerPosition.distanceTo(this.mesh.position);

                if (this.state === 'wandering') {
                    if (distanceToPlayer < 20) {
                        // Begin approaching orbit path
                        this.state = 'approaching_orbit';
                        this.hasReachedOrbit = false;
                        // Compute initial orbit angle based on current position relative to player
                        const vectorToPlayer = new THREE.Vector3().subVectors(this.mesh.position, playerPosition);
                        this.orbitAngle = Math.atan2(vectorToPlayer.z, vectorToPlayer.x);
                    } else {
                        this.wander(deltaTimeMs);
                    }
                } else if (this.state === 'approaching_orbit') {
                    this.approachOrbit(deltaTimeMs);
                    if (this.hasReachedOrbit) {
                        this.state = 'orbiting';
                        this.orbitingClock.start();
                    }
                } else if (this.state === 'orbiting') {
                    this.orbitPlayer(deltaTimeMs);
                    if (this.orbitingClock.getElapsedTime() * 1000 > this.orbitDuration) {
                        this.state = 'rearing_back';
                        this.rearStartTime = currentTime;
                    }
                } else if (this.state === 'rearing_back') {
                    this.rearBack(deltaTimeMs);
                    if (currentTime - this.rearStartTime >= this.rearDuration) {
                        this.state = 'charging';
                        // Reset body rotation after rearing back
                        this.body.rotation.x = this.originalBodyRotationX;
                    }
                } else if (this.state === 'charging') {
                    this.chargeTowardsPlayer(deltaTimeMs);
                    // Check if goblin has reached the stopping distance
                    const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);
                    if (distanceToPlayer <= this.chargeStopDistance) {
                        this.state = 'pausing'; // Switch to pausing state
                        this.pauseStartTime = currentTime;
                        this.pauseDuration = 1000; // Pause for 1 second
                    }
                } else if (this.state === 'pausing') {
                    // Face the player
                    this.facePlayer();

                    // Attack the player if cooldown permits and within attack range
                    if (this.canAttackPlayer()) {
                        this.attackPlayer();
                    }

                    // Check if pause duration has elapsed
                    if (currentTime - this.pauseStartTime >= this.pauseDuration) {
                        this.state = 'orbiting';
                        this.hasReachedOrbit = false; // Reset for next orbit
                        this.orbitingClock.start();
                    }
                }

                // Apply gravity
                this.applyGravity(deltaTimeMs);

                // Update walking animation
                this.updateAnimations(deltaTimeMs);

                if (this.attackCooldown > 0) {
                    this.attackCooldown -= deltaTimeMs;
                }
            }

            // Move towards orbit path smoothly
            approachOrbit(deltaTimeMs) {
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);

                // Desired position on orbit path
                const desiredX = playerPosition.x + Math.cos(this.orbitAngle) * this.orbitRadius;
                const desiredZ = playerPosition.z + Math.sin(this.orbitAngle) * this.orbitRadius;

                const desiredPosition = new THREE.Vector3(desiredX, this.mesh.position.y, desiredZ);

                // Move towards desired position
                const direction = new THREE.Vector3().subVectors(desiredPosition, this.mesh.position);
                const distance = direction.length();
                if (distance < 0.1) {
                    this.hasReachedOrbit = true;
                } else {
                    direction.normalize();
                    const moveDistance = Math.min(this.approachSpeed * deltaTimeMs * 0.06, distance);
                    const moveStep = direction.multiplyScalar(moveDistance);

                    // Check collision before moving
                    if (!checkCollision(this.mesh.position.x + moveStep.x, this.mesh.position.z + moveStep.z, goblinCollisionRadius)) {
                        this.mesh.position.add(moveStep);
                    }

                    // Rotate to face the player
                    const angle = Math.atan2((playerPosition.x - this.mesh.position.x), (playerPosition.z - this.mesh.position.z));
                    this.mesh.rotation.y = angle;
                }
            }

            // Orbiting around the player
            orbitPlayer(deltaTimeMs) {
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);

                // Update orbit angle
                this.orbitAngle += this.angularSpeed * deltaTimeMs;

                const offsetX = Math.cos(this.orbitAngle) * this.orbitRadius;
                const offsetZ = Math.sin(this.orbitAngle) * this.orbitRadius;

                // Assign new position
                this.mesh.position.x = playerPosition.x + offsetX;
                this.mesh.position.z = playerPosition.z + offsetZ;

                // Adjust Y position according to terrain
                this.mesh.position.y = getInterpolatedHeightAt(this.mesh.position.x, this.mesh.position.z) + 1;

                // Face the player
                const direction = new THREE.Vector3().subVectors(playerPosition, this.mesh.position).normalize();
                const angle = Math.atan2(direction.x, direction.z);
                this.mesh.rotation.y = angle;
            }

            // Rearing back before charging
            rearBack(deltaTimeMs) {
                // Animate rearing back by tilting the body backward over time
                const currentTime = performance.now();
                const progress = (currentTime - this.rearStartTime) / this.rearDuration;
                const maxTiltAngle = -Math.PI / 6; // Tilt back up to 30 degrees

                this.body.rotation.x = this.originalBodyRotationX + maxTiltAngle * progress;

                // Optional: Move slightly backward while rearing back
                const backwardStep = 0.02 * deltaTimeMs * 0.06; // Adjust speed as needed
                const backwardDirection = new THREE.Vector3(Math.sin(this.mesh.rotation.y), 0, Math.cos(this.mesh.rotation.y));
                this.mesh.position.sub(backwardDirection.multiplyScalar(backwardStep));
            }

            // Charging towards the player
            chargeTowardsPlayer(deltaTimeMs) {
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);
                const direction = new THREE.Vector3().subVectors(playerPosition, this.mesh.position).normalize();
                const distanceToPlayer = this.mesh.position.distanceTo(playerPosition);

                // If the goblin is within stopping distance, stop moving
                if (distanceToPlayer <= this.chargeStopDistance) {
                    // Stop moving
                    return;
                }

                const chargeSpeed = 0.2; // Faster than normal speed
                const moveStep = direction.multiplyScalar(chargeSpeed * deltaTimeMs * 0.06); // Adjust for deltaTime

                // Check collision before moving
                if (!checkCollision(this.mesh.position.x + moveStep.x, this.mesh.position.z + moveStep.z, goblinCollisionRadius)) {
                    this.mesh.position.add(moveStep);
                }

                // Rotate to face the player
                const angle = Math.atan2(direction.x, direction.z);
                this.mesh.rotation.y = angle;
            }

            // Face the player
            facePlayer() {
                const playerPosition = new THREE.Vector3();
                camera.getWorldPosition(playerPosition);
                const direction = new THREE.Vector3().subVectors(playerPosition, this.mesh.position).normalize();
                const angle = Math.atan2(direction.x, direction.z);
                this.mesh.rotation.y = angle;
            }

            // Update animations
            updateAnimations(deltaTimeMs) {
                // Only animate walking when moving
                if (this.state === 'wandering' || this.state === 'approaching_orbit' || this.state === 'orbiting' || this.state === 'charging') {
                    this.walkPhase += this.walkSpeed;
                    const armSwing = Math.sin(this.walkPhase) * 0.5; // Swing angle for arms
                    const legSwing = Math.cos(this.walkPhase) * 0.5; // Swing angle for legs

                    // Animate arms
                    this.leftArm.rotation.z = Math.PI / 4 + armSwing;
                    this.rightArm.rotation.z = -Math.PI / 4 - armSwing;

                    // Animate legs
                    this.leftLeg.rotation.x = legSwing;
                    this.rightLeg.rotation.x = -legSwing;
                } else {
                    // Reset animations when not moving
                    this.leftArm.rotation.z = Math.PI / 4;
                    this.rightArm.rotation.z = -Math.PI / 4;
                    this.leftLeg.rotation.x = 0;
                    this.rightLeg.rotation.x = 0;
                }
            }
        }

        // Goblin collision radius
        const goblinCollisionRadius = 1.0;

        // Function to check collision for player and goblins
        function checkCollision(x, z, radius) {
            const collisionPosition = new THREE.Vector3(x, 0, z); // y is irrelevant for horizontal collision

            for (let i = 0; i < mazeWallsBoxes.length; i++) {
                const wallBox = mazeWallsBoxes[i];
                if (wallBox.containsPoint(collisionPosition)) {
                    return true;
                }

                // Additional circle-box collision
                const closestPoint = wallBox.clampPoint(collisionPosition, new THREE.Vector3());
                const distance = collisionPosition.distanceTo(closestPoint);
                if (distance < radius) {
                    return true;
                }
            }

            return false;
        }

        // Spawn goblins at random positions
        function spawnGoblins(number) {
            for (let i = 0; i < number; i++) {
                let spawnX, spawnZ;
                // 50% chance to spawn in the maze
                if (Math.random() < 0.5) {
                    // Spawn inside the maze
                    const cellSize = mazeCellSize;
                    const mazeX = Math.floor(Math.random() * mazeWidth);
                    const mazeY = Math.floor(Math.random() * mazeHeight);
                    spawnX = mazePositionX + mazeX * cellSize;
                    spawnZ = mazePositionZ + mazeY * cellSize;
                } else {
                    // Spawn outside the maze
                    spawnX = Math.random() * 400 - 200;
                    spawnZ = Math.random() * 400 - 200;
                }
                // Ensure goblin spawns on terrain and not colliding with walls
                const spawnY = getTerrainHeightAt(spawnX, spawnZ) + 1;
                if (!checkCollision(spawnX, spawnZ, goblinCollisionRadius)) {
                    goblins.push(new Goblin(spawnX, spawnZ));
                } else {
                    // Retry spawning if collision detected
                    i--;
                }
            }
        }

        spawnGoblins(20); // Spawn 20 goblins

        // In-game time management: Start 1 hour and 10 minutes before dawn (assuming dawn is 6:00 AM)
        let inGameTime = 21; // Start at 4 AM
        let inGameMinutes = 50; // Start with 50 minutes past the hour (4:50 AM)

        // Function to update the sky and lighting based on in-game time
        function updateDayNightCycle(deltaTime) {
            // Increment in-game time
            inGameMinutes += deltaTime;
            if (inGameMinutes >= 60) { // Each minute represents an hour
                inGameMinutes = 0;
                inGameTime += 1;
                if (inGameTime >= 24) {
                    inGameTime = 0;
                }
            }

            // Calculate sun position based on in-game time
            const timeFraction = (inGameTime + inGameMinutes / 60) / 24; // 0 to 1
            const theta = Math.PI * (timeFraction - 0.25); // Adjusted to start at sunrise
            const phi = 2 * Math.PI * (0.25); // Fixed azimuthal angle for simplicity

            sun.setFromSphericalCoords(1, theta, phi);
            sky.material.uniforms['sunPosition'].value.copy(sun);
            sunLight.position.copy(sun);

            // Adjust lighting based on time of day
            if (inGameTime >= 6 && inGameTime < 18) { // Daytime
                ambientLight.intensity = 0.7;
                sunLight.intensity = 1;
                sunLight.color.set(0xffffff);
            } else { // Nighttime
                ambientLight.intensity = 0.2;
                sunLight.intensity = 0.2;
                sunLight.color.set(0x555577); // Dim bluish light
            }

            // Update HUD with current time
            updateTimeDisplay();
        }

        // Update Health Bar
        function updateHealthBar() {
            const healthPercentage = Math.max(playerHealth, 0) / 100; // Ensure non-negative
            healthBar.style.width = `${healthPercentage * 100}%`;
            if (healthPercentage > 0.6) {
                healthBar.style.backgroundColor = 'green';
            } else if (healthPercentage > 0.3) {
                healthBar.style.backgroundColor = 'yellow';
            } else {
                healthBar.style.backgroundColor = 'red';
            }
        }

        // Update Time Display
        function updateTimeDisplay() {
            // Format in-game time to HH:MM AM/PM
            let displayHour = inGameTime;
            let period = 'AM';
            if (displayHour >= 12) {
                period = 'PM';
                if (displayHour > 12) displayHour -= 12;
            }
            if (displayHour === 0) {
                displayHour = 12;
            }
            const displayMinutes = inGameMinutes.toString().padStart(2, '0');
            timeDisplay.innerText = `Time: ${displayHour}:${displayMinutes} ${period}`;
        }

        // Increment in-game time by one hour
        function incrementTimeByOneHour() {
            inGameTime += 1;
            if (inGameTime >= 24) {
                inGameTime = 0;
            }
            // Reset minutes to 0 to represent the start of the new hour
            inGameMinutes = 0;
            // Update sun position and lighting immediately
            updateDayNightCycle(0);
            console.log(`Time increased by 1 hour. Current time: ${inGameTime}:00`);
        }

        // Handle game over
        function gameOver() {
            gameOverScreen.style.display = 'block';
            // Stop the animation loop
            cancelAnimationFrame(animate.id);
        }

        // Handle game win
        function gameWin() {
            youWinScreen.style.display = 'block';
            // Stop the main animation loop
            cancelAnimationFrame(animate.id);
            // Start the YOU WIN animation
            startWinAnimation();
        }

        // Variables for the YOU WIN screen
        let winScene, winCamera, winRenderer, winObjects = [];

        function startWinAnimation() {
            // Set up the YOU WIN scene
            winScene = new THREE.Scene();
            winCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            winCamera.position.z = 5;

            // Create renderer for the YOU WIN screen
            winRenderer = new THREE.WebGLRenderer({ canvas: document.getElementById('winCanvas'), alpha: true });
            winRenderer.setSize(window.innerWidth, window.innerHeight);

            // Add colorful 3D objects (90s themed)
            const colors = [0xFF00FF, 0x00FFFF, 0xFFFF00, 0xFF0000, 0x00FF00];
            const geometries = [
                new THREE.TorusGeometry(1, 0.4, 16, 100),
                new THREE.SphereGeometry(1, 32, 32),
                new THREE.ConeGeometry(1, 2, 32),
                new THREE.BoxGeometry(1, 1, 1),
                new THREE.DodecahedronGeometry(1)
            ];

            for (let i = 0; i < 20; i++) {
                const material = new THREE.MeshBasicMaterial({ color: colors[i % colors.length], wireframe: true });
                const geometry = geometries[i % geometries.length];
                const mesh = new THREE.Mesh(geometry, material);
                mesh.position.set(
                    Math.random() * 10 - 5,
                    Math.random() * 10 - 5,
                    Math.random() * -10
                );
                mesh.rotationSpeed = Math.random() * 0.05;
                winScene.add(mesh);
                winObjects.push(mesh);
            }

            // Animate the YOU WIN screen
            animateWinScreen();
        }

        function animateWinScreen() {
            requestAnimationFrame(animateWinScreen);

            // Rotate objects
            winObjects.forEach(obj => {
                obj.rotation.x += obj.rotationSpeed;
                obj.rotation.y += obj.rotationSpeed;
            });

            winRenderer.render(winScene, winCamera);
        }

        // Handle window resize
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);

            if (winCamera && winRenderer) {
                winCamera.aspect = window.innerWidth / window.innerHeight;
                winCamera.updateProjectionMatrix();
                winRenderer.setSize(window.innerWidth, window.innerHeight);
            }
        });

        // Animation loop to render the scene
        let lastTime = performance.now();
        function animate() {
            animate.id = requestAnimationFrame(animate);

            const currentTime = performance.now();
            const deltaTimeMs = currentTime - lastTime;
            const deltaTime = deltaTimeMs / 1000 * 60; // Convert to in-game minutes
            lastTime = currentTime;

            // Update day/night cycle
            updateDayNightCycle(deltaTime / 60); // Adjust deltaTime for smoother increment

            let speed = movement.running ? 0.2 : 0.1;
            if (movement.forward) controls.moveForward(speed);
            if (movement.backward) controls.moveForward(-speed);
            if (movement.left) controls.moveRight(-speed);
            if (movement.right) controls.moveRight(speed);

            // Apply gravity to the player
            velocity.y += gravity;
            camera.position.y += velocity.y;

            // Collision detection with terrain for the player
            const terrainHeight = getInterpolatedHeightAt(camera.position.x, camera.position.z) + 1.6; // Adjusted player height for better collision
            if (camera.position.y < terrainHeight) {
                camera.position.y = terrainHeight;
                velocity.y = 0;
            }

            // Collision detection with maze walls for the player
            const playerCollisionRadius = 1.0; // Adjust as needed
            const playerX = camera.position.x;
            const playerZ = camera.position.z;
            if (checkCollision(playerX, playerZ, playerCollisionRadius)) {
                // Revert player movement if collision detected
                if (movement.forward) controls.moveForward(-speed);
                if (movement.backward) controls.moveForward(speed);
                if (movement.left) controls.moveRight(speed);
                if (movement.right) controls.moveRight(-speed);
            }

            // Update goblins
            goblins.forEach(goblin => goblin.update(deltaTimeMs));

            // Animate the pulsing golden orb
            const pulseSpeed = 2; // Pulses per second
            const pulseScale = 0.1; // Scale variation
            goldenOrb.scale.set(
                1 + Math.sin(performance.now() * 0.002 * pulseSpeed) * pulseScale,
                1 + Math.sin(performance.now() * 0.002 * pulseSpeed) * pulseScale,
                1 + Math.sin(performance.now() * 0.002 * pulseSpeed) * pulseScale
            );

            // Check for collision with the golden orb
            const orbDistance = camera.position.distanceTo(goldenOrb.position);
            if (orbDistance < 2) {
                gameWin();
            }

            // Smoothly interpolate sword rotation for blocking
            const rotationSpeed = 0.1; // Adjust rotation speed (higher is faster)
            swordGroup.rotation.z += (swordRotationTargetZ - swordGroup.rotation.z) * rotationSpeed;

            // Render the scene
            renderer.render(scene, camera);
        }
        animate();
    </script>
</body>
</html>