| 
							 | 
						import * as THREE from 'three'; | 
					
					
						
						| 
							 | 
						import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; | 
					
					
						
						| 
							 | 
						import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						const GAME_DURATION = 180; | 
					
					
						
						| 
							 | 
						const MAP_SIZE = 1000; | 
					
					
						
						| 
							 | 
						const TANK_HEIGHT = 0.5; | 
					
					
						
						| 
							 | 
						const ENEMY_GROUND_HEIGHT = 0; | 
					
					
						
						| 
							 | 
						const ENEMY_SCALE = 10; | 
					
					
						
						| 
							 | 
						const MAX_HEALTH = 1000; | 
					
					
						
						| 
							 | 
						const ENEMY_MOVE_SPEED = 0.1; | 
					
					
						
						| 
							 | 
						const ENEMY_COUNT_MAX = 3; | 
					
					
						
						| 
							 | 
						const PARTICLE_COUNT = 15; | 
					
					
						
						| 
							 | 
						const BUILDING_COUNT = 30; | 
					
					
						
						| 
							 | 
						const ENEMY_CONFIG = { | 
					
					
						
						| 
							 | 
						    ATTACK_RANGE: 100, | 
					
					
						
						| 
							 | 
						    ATTACK_INTERVAL: 2000, | 
					
					
						
						| 
							 | 
						    BULLET_SPEED: 2 | 
					
					
						
						| 
							 | 
						}; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						class TankPlayer { | 
					
					
						
						| 
							 | 
						    constructor() { | 
					
					
						
						| 
							 | 
						        this.body = null; | 
					
					
						
						| 
							 | 
						        this.turret = null; | 
					
					
						
						| 
							 | 
						        this.position = new THREE.Vector3(0, 0, 0); | 
					
					
						
						| 
							 | 
						        this.rotation = new THREE.Euler(0, 0, 0); | 
					
					
						
						| 
							 | 
						        this.turretRotation = 0; | 
					
					
						
						| 
							 | 
						        this.moveSpeed = 0.5; | 
					
					
						
						| 
							 | 
						        this.turnSpeed = 0.03; | 
					
					
						
						| 
							 | 
						        this.turretGroup = new THREE.Group(); | 
					
					
						
						| 
							 | 
						        this.health = MAX_HEALTH; | 
					
					
						
						| 
							 | 
						        this.isLoaded = false; | 
					
					
						
						| 
							 | 
						        this.ammo = 1;   | 
					
					
						
						| 
							 | 
						        this.maxAmmo = 1;   | 
					
					
						
						| 
							 | 
						        this.isReloading = false;   | 
					
					
						
						| 
							 | 
						        this.reloadTime = 3000;   | 
					
					
						
						| 
							 | 
						        this.lastShootTime = 0; | 
					
					
						
						| 
							 | 
						        this.bullets = []; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    async initialize(scene, loader) { | 
					
					
						
						| 
							 | 
						    try { | 
					
					
						
						| 
							 | 
						        const bodyResult = await loader.loadAsync('/models/abramsBody.glb'); | 
					
					
						
						| 
							 | 
						        this.body = bodyResult.scene; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const turretResult = await loader.loadAsync('/models/abramsTurret.glb'); | 
					
					
						
						| 
							 | 
						        this.turret = turretResult.scene; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.turretGroup.position.y = 0.2; | 
					
					
						
						| 
							 | 
						        this.turretGroup.add(this.turret); | 
					
					
						
						| 
							 | 
						        this.body.add(this.turretGroup); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.body.traverse((child) => { | 
					
					
						
						| 
							 | 
						            if (child.isMesh) { | 
					
					
						
						| 
							 | 
						                child.castShadow = true; | 
					
					
						
						| 
							 | 
						                child.receiveShadow = true; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.turret.traverse((child) => { | 
					
					
						
						| 
							 | 
						            if (child.isMesh) { | 
					
					
						
						| 
							 | 
						                child.castShadow = true; | 
					
					
						
						| 
							 | 
						                child.receiveShadow = true; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (window.gameInstance) { | 
					
					
						
						| 
							 | 
						            const spawnPos = window.gameInstance.findValidSpawnPosition(); | 
					
					
						
						| 
							 | 
						            this.body.position.copy(spawnPos); | 
					
					
						
						| 
							 | 
						        } else { | 
					
					
						
						| 
							 | 
						            this.body.position.copy(this.position); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.createExplosionEffect = (scene, position) => { | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            for (let i = 0; i < 15; i++) { | 
					
					
						
						| 
							 | 
						                const size = Math.random() * 0.2 + 0.1; | 
					
					
						
						| 
							 | 
						                const geometry = new THREE.SphereGeometry(size); | 
					
					
						
						| 
							 | 
						                const material = new THREE.MeshBasicMaterial({ | 
					
					
						
						| 
							 | 
						                    color: Math.random() < 0.5 ? 0xff4500 : 0xff8c00 | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						                const particle = new THREE.Mesh(geometry, material); | 
					
					
						
						| 
							 | 
						                particle.position.copy(position); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                const speed = Math.random() * 0.3 + 0.2; | 
					
					
						
						| 
							 | 
						                const angle = Math.random() * Math.PI * 2; | 
					
					
						
						| 
							 | 
						                const elevation = Math.random() * Math.PI - Math.PI / 2; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                particle.velocity = new THREE.Vector3( | 
					
					
						
						| 
							 | 
						                    Math.cos(angle) * Math.cos(elevation) * speed, | 
					
					
						
						| 
							 | 
						                    Math.sin(elevation) * speed, | 
					
					
						
						| 
							 | 
						                    Math.sin(angle) * Math.cos(elevation) * speed | 
					
					
						
						| 
							 | 
						                ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                particle.gravity = -0.01; | 
					
					
						
						| 
							 | 
						                particle.life = Math.random() * 20 + 20; | 
					
					
						
						| 
							 | 
						                particle.fadeRate = 1 / particle.life; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                scene.add(particle); | 
					
					
						
						| 
							 | 
						                window.gameInstance.particles.push({ | 
					
					
						
						| 
							 | 
						                    mesh: particle, | 
					
					
						
						| 
							 | 
						                    velocity: particle.velocity, | 
					
					
						
						| 
							 | 
						                    gravity: particle.gravity, | 
					
					
						
						| 
							 | 
						                    life: particle.life, | 
					
					
						
						| 
							 | 
						                    fadeRate: particle.fadeRate | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const explosionSound = new Audio('sounds/explosion.ogg'); | 
					
					
						
						| 
							 | 
						            explosionSound.volume = 0.3; | 
					
					
						
						| 
							 | 
						            explosionSound.play(); | 
					
					
						
						| 
							 | 
						        }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        scene.add(this.body); | 
					
					
						
						| 
							 | 
						        this.isLoaded = true; | 
					
					
						
						| 
							 | 
						        this.updateAmmoDisplay(); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						    } catch (error) { | 
					
					
						
						| 
							 | 
						        console.error('Error loading tank models:', error); | 
					
					
						
						| 
							 | 
						        this.isLoaded = false; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    shoot(scene) { | 
					
					
						
						| 
							 | 
						    if (this.isReloading || this.ammo <= 0) return null; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const sounds = ['sounds/mbtfire1.ogg', 'sounds/mbtfire2.ogg', 'sounds/mbtfire3.ogg', 'sounds/mbtfire4.ogg']; | 
					
					
						
						| 
							 | 
						    const randomSound = sounds[Math.floor(Math.random() * sounds.length)]; | 
					
					
						
						| 
							 | 
						    const audio = new Audio(randomSound); | 
					
					
						
						| 
							 | 
						    audio.volume = 0.5; | 
					
					
						
						| 
							 | 
						    audio.play(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const bullet = this.createBullet(scene); | 
					
					
						
						| 
							 | 
						    if (bullet) { | 
					
					
						
						| 
							 | 
						        this.ammo--; | 
					
					
						
						| 
							 | 
						        this.updateAmmoDisplay(); | 
					
					
						
						| 
							 | 
						        this.startReload(); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						    return bullet; | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    createBullet(scene) { | 
					
					
						
						| 
							 | 
						        const bulletGeometry = new THREE.SphereGeometry(0.2); | 
					
					
						
						| 
							 | 
						        const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); | 
					
					
						
						| 
							 | 
						        const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const bulletOffset = new THREE.Vector3(0, 0.5, 2); | 
					
					
						
						| 
							 | 
						        bulletOffset.applyQuaternion(this.turretGroup.quaternion); | 
					
					
						
						| 
							 | 
						        bulletOffset.applyQuaternion(this.body.quaternion); | 
					
					
						
						| 
							 | 
						        bullet.position.copy(this.body.position).add(bulletOffset); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const direction = new THREE.Vector3(0, 0, 1); | 
					
					
						
						| 
							 | 
						        direction.applyQuaternion(this.turretGroup.quaternion); | 
					
					
						
						| 
							 | 
						        direction.applyQuaternion(this.body.quaternion); | 
					
					
						
						| 
							 | 
						        bullet.velocity = direction.multiplyScalar(2); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        scene.add(bullet); | 
					
					
						
						| 
							 | 
						        this.bullets.push(bullet); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        return bullet; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    update(mouseX, mouseY, scene) { | 
					
					
						
						| 
							 | 
						    if (!this.body || !this.turretGroup) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const absoluteTurretRotation = mouseX; | 
					
					
						
						| 
							 | 
						    this.turretGroup.rotation.y = absoluteTurretRotation - this.body.rotation.y; | 
					
					
						
						| 
							 | 
						    this.turretRotation = absoluteTurretRotation; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    for (let i = this.bullets.length - 1; i >= 0; i--) { | 
					
					
						
						| 
							 | 
						        const bullet = this.bullets[i]; | 
					
					
						
						| 
							 | 
						        const oldPosition = bullet.position.clone(); | 
					
					
						
						| 
							 | 
						        bullet.position.add(bullet.velocity); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const terrainHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            bullet.position.x, | 
					
					
						
						| 
							 | 
						            bullet.position.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        if (bullet.position.y < terrainHeight || | 
					
					
						
						| 
							 | 
						            Math.abs(bullet.position.x) > MAP_SIZE / 2 || | 
					
					
						
						| 
							 | 
						            Math.abs(bullet.position.z) > MAP_SIZE / 2) { | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            this.createExplosionEffect(scene, bullet.position); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            scene.remove(bullet); | 
					
					
						
						| 
							 | 
						            this.bullets.splice(i, 1); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    move(direction) { | 
					
					
						
						| 
							 | 
						    if (!this.body) return; | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const moveVector = new THREE.Vector3(); | 
					
					
						
						| 
							 | 
						    moveVector.x = direction.x * this.moveSpeed; | 
					
					
						
						| 
							 | 
						    moveVector.z = direction.z * this.moveSpeed; | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const newPosition = this.body.position.clone().add(moveVector); | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const halfMap = MAP_SIZE / 2; | 
					
					
						
						| 
							 | 
						    const margin = 10;  | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    newPosition.x = Math.max(-halfMap + margin, Math.min(halfMap - margin, newPosition.x)); | 
					
					
						
						| 
							 | 
						    newPosition.z = Math.max(-halfMap + margin, Math.min(halfMap - margin, newPosition.z)); | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const heightAtNewPos = window.gameInstance.getHeightAtPosition(newPosition.x, newPosition.z); | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    newPosition.y = heightAtNewPos + TANK_HEIGHT; | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const currentHeight = this.body.position.y; | 
					
					
						
						| 
							 | 
						    const heightDifference = Math.abs(newPosition.y - currentHeight); | 
					
					
						
						| 
							 | 
						    const maxClimbAngle = 0.5; | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    if (heightDifference / this.moveSpeed < maxClimbAngle) { | 
					
					
						
						| 
							 | 
						        this.body.position.copy(newPosition); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.body.quaternion); | 
					
					
						
						| 
							 | 
						        const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.body.quaternion); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const frontHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            newPosition.x + forwardVector.x, | 
					
					
						
						| 
							 | 
						            newPosition.z + forwardVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						        const backHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            newPosition.x - forwardVector.x, | 
					
					
						
						| 
							 | 
						            newPosition.z - forwardVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						        const rightHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            newPosition.x + rightVector.x, | 
					
					
						
						| 
							 | 
						            newPosition.z + rightVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						        const leftHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            newPosition.x - rightVector.x, | 
					
					
						
						| 
							 | 
						            newPosition.z - rightVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const pitch = Math.atan2(frontHeight - backHeight, 2); | 
					
					
						
						| 
							 | 
						        const roll = Math.atan2(rightHeight - leftHeight, 2); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const currentYRotation = this.body.rotation.y; | 
					
					
						
						| 
							 | 
						        this.body.rotation.set(pitch, currentYRotation, roll); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    rotate(angle) { | 
					
					
						
						| 
							 | 
						        if (!this.body) return; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.body.rotation.y += angle * this.turnSpeed; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const position = this.body.position; | 
					
					
						
						| 
							 | 
						        const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.body.quaternion); | 
					
					
						
						| 
							 | 
						        const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.body.quaternion); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const frontHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            position.x + forwardVector.x, | 
					
					
						
						| 
							 | 
						            position.z + forwardVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						        const backHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            position.x - forwardVector.x, | 
					
					
						
						| 
							 | 
						            position.z - forwardVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						        const rightHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            position.x + rightVector.x, | 
					
					
						
						| 
							 | 
						            position.z + rightVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						        const leftHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						            position.x - rightVector.x, | 
					
					
						
						| 
							 | 
						            position.z - rightVector.z | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const pitch = Math.atan2(frontHeight - backHeight, 2); | 
					
					
						
						| 
							 | 
						        const roll = Math.atan2(rightHeight - leftHeight, 2); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const currentYRotation = this.body.rotation.y; | 
					
					
						
						| 
							 | 
						        this.body.rotation.set(pitch, currentYRotation, roll); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    getPosition() { | 
					
					
						
						| 
							 | 
						        return this.body ? this.body.position : new THREE.Vector3(); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    takeDamage(damage) { | 
					
					
						
						| 
							 | 
						        this.health -= damage; | 
					
					
						
						| 
							 | 
						        return this.health <= 0; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    startReload() { | 
					
					
						
						| 
							 | 
						        this.isReloading = true; | 
					
					
						
						| 
							 | 
						        const reloadingText = document.getElementById('reloadingText'); | 
					
					
						
						| 
							 | 
						        reloadingText.style.display = 'block'; | 
					
					
						
						| 
							 | 
						        setTimeout(() => { | 
					
					
						
						| 
							 | 
						            this.ammo = this.maxAmmo; | 
					
					
						
						| 
							 | 
						            this.isReloading = false; | 
					
					
						
						| 
							 | 
						            reloadingText.style.display = 'none'; | 
					
					
						
						| 
							 | 
						            this.updateAmmoDisplay(); | 
					
					
						
						| 
							 | 
						        }, this.reloadTime); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    updateAmmoDisplay() { | 
					
					
						
						| 
							 | 
						        document.getElementById('ammoDisplay').textContent = `APFSDS: ${this.ammo}/${this.maxAmmo}`; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						class Enemy { | 
					
					
						
						| 
							 | 
						    constructor(scene, position, type = 'tank') { | 
					
					
						
						| 
							 | 
						        this.scene = scene; | 
					
					
						
						| 
							 | 
						        this.position = position; | 
					
					
						
						| 
							 | 
						        this.mesh = null; | 
					
					
						
						| 
							 | 
						        this.type = type; | 
					
					
						
						| 
							 | 
						        this.health = type === 'tank' ? 100 : 200; | 
					
					
						
						| 
							 | 
						        this.lastAttackTime = 0; | 
					
					
						
						| 
							 | 
						        this.bullets = []; | 
					
					
						
						| 
							 | 
						        this.isLoaded = false; | 
					
					
						
						| 
							 | 
						        this.moveSpeed = type === 'tank' ? ENEMY_MOVE_SPEED : ENEMY_MOVE_SPEED * 0.7; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    async initialize(loader) { | 
					
					
						
						| 
							 | 
						        try { | 
					
					
						
						| 
							 | 
						            const modelPath = this.type === 'tank' ? '/models/enemy1.glb' : '/models/enemy4.glb'; | 
					
					
						
						| 
							 | 
						            const result = await loader.loadAsync(modelPath); | 
					
					
						
						| 
							 | 
						            this.mesh = result.scene; | 
					
					
						
						| 
							 | 
						            this.mesh.position.copy(this.position); | 
					
					
						
						| 
							 | 
						            this.mesh.scale.set(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            this.mesh.traverse((child) => { | 
					
					
						
						| 
							 | 
						                if (child.isMesh) { | 
					
					
						
						| 
							 | 
						                    child.castShadow = true; | 
					
					
						
						| 
							 | 
						                    child.receiveShadow = true; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            this.scene.add(this.mesh); | 
					
					
						
						| 
							 | 
						            this.isLoaded = true; | 
					
					
						
						| 
							 | 
						        } catch (error) { | 
					
					
						
						| 
							 | 
						            console.error('Error loading enemy model:', error); | 
					
					
						
						| 
							 | 
						            this.isLoaded = false; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    update(playerPosition) { | 
					
					
						
						| 
							 | 
						    if (!this.mesh || !this.isLoaded) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const direction = new THREE.Vector3() | 
					
					
						
						| 
							 | 
						        .subVectors(playerPosition, this.mesh.position) | 
					
					
						
						| 
							 | 
						        .normalize(); | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const distanceToPlayer = this.mesh.position.distanceTo(playerPosition); | 
					
					
						
						| 
							 | 
						    const minDistance = 50; | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    this.mesh.lookAt(playerPosition); | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    if (distanceToPlayer > minDistance) { | 
					
					
						
						| 
							 | 
						        const moveVector = direction.multiplyScalar(this.moveSpeed); | 
					
					
						
						| 
							 | 
						        const newPosition = this.mesh.position.clone().add(moveVector); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const halfMap = MAP_SIZE / 2; | 
					
					
						
						| 
							 | 
						        const margin = 10; | 
					
					
						
						| 
							 | 
						        newPosition.x = Math.max(-halfMap + margin, Math.min(halfMap - margin, newPosition.x)); | 
					
					
						
						| 
							 | 
						        newPosition.z = Math.max(-halfMap + margin, Math.min(halfMap - margin, newPosition.z)); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const heightAtNewPos = window.gameInstance.getHeightAtPosition(newPosition.x, newPosition.z); | 
					
					
						
						| 
							 | 
						        newPosition.y = heightAtNewPos + TANK_HEIGHT; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const heightDifference = Math.abs(newPosition.y - this.mesh.position.y); | 
					
					
						
						| 
							 | 
						        const maxClimbAngle = 0.5; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (heightDifference / this.moveSpeed < maxClimbAngle) { | 
					
					
						
						| 
							 | 
						            this.mesh.position.copy(newPosition); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(this.mesh.quaternion); | 
					
					
						
						| 
							 | 
						            const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(this.mesh.quaternion); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const frontHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						                newPosition.x + forwardVector.x, | 
					
					
						
						| 
							 | 
						                newPosition.z + forwardVector.z | 
					
					
						
						| 
							 | 
						            ); | 
					
					
						
						| 
							 | 
						            const backHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						                newPosition.x - forwardVector.x, | 
					
					
						
						| 
							 | 
						                newPosition.z - forwardVector.z | 
					
					
						
						| 
							 | 
						            ); | 
					
					
						
						| 
							 | 
						            const rightHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						                newPosition.x + rightVector.x, | 
					
					
						
						| 
							 | 
						                newPosition.z + rightVector.z | 
					
					
						
						| 
							 | 
						            ); | 
					
					
						
						| 
							 | 
						            const leftHeight = window.gameInstance.getHeightAtPosition( | 
					
					
						
						| 
							 | 
						                newPosition.x - rightVector.x, | 
					
					
						
						| 
							 | 
						                newPosition.z - rightVector.z | 
					
					
						
						| 
							 | 
						            ); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const pitch = Math.atan2(frontHeight - backHeight, 2); | 
					
					
						
						| 
							 | 
						            const roll = Math.atan2(rightHeight - leftHeight, 2); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            this.mesh.rotation.x = pitch; | 
					
					
						
						| 
							 | 
						            this.mesh.rotation.z = roll; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    for (let i = this.bullets.length - 1; i >= 0; i--) { | 
					
					
						
						| 
							 | 
						        const bullet = this.bullets[i]; | 
					
					
						
						| 
							 | 
						        bullet.position.add(bullet.velocity); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const halfMap = MAP_SIZE / 2; | 
					
					
						
						| 
							 | 
						        if (Math.abs(bullet.position.x) > halfMap ||  | 
					
					
						
						| 
							 | 
						            Math.abs(bullet.position.z) > halfMap) { | 
					
					
						
						| 
							 | 
						            this.scene.remove(bullet); | 
					
					
						
						| 
							 | 
						            this.bullets.splice(i, 1); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    shoot(playerPosition) { | 
					
					
						
						| 
							 | 
						        const currentTime = Date.now(); | 
					
					
						
						| 
							 | 
						        const attackInterval = this.type === 'tank' ?  | 
					
					
						
						| 
							 | 
						            ENEMY_CONFIG.ATTACK_INTERVAL :  | 
					
					
						
						| 
							 | 
						            ENEMY_CONFIG.ATTACK_INTERVAL * 1.5; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        if (currentTime - this.lastAttackTime < attackInterval) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const bulletGeometry = new THREE.SphereGeometry(this.type === 'tank' ? 0.2 : 0.3); | 
					
					
						
						| 
							 | 
						        const bulletMaterial = new THREE.MeshBasicMaterial({  | 
					
					
						
						| 
							 | 
						            color: this.type === 'tank' ? 0xff0000 : 0xff6600  | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						        const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        bullet.position.copy(this.mesh.position); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const direction = new THREE.Vector3() | 
					
					
						
						| 
							 | 
						            .subVectors(playerPosition, this.mesh.position) | 
					
					
						
						| 
							 | 
						            .normalize(); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const bulletSpeed = this.type === 'tank' ?  | 
					
					
						
						| 
							 | 
						            ENEMY_CONFIG.BULLET_SPEED :  | 
					
					
						
						| 
							 | 
						            ENEMY_CONFIG.BULLET_SPEED * 0.8; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        bullet.velocity = direction.multiplyScalar(bulletSpeed); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.scene.add(bullet); | 
					
					
						
						| 
							 | 
						        this.bullets.push(bullet); | 
					
					
						
						| 
							 | 
						        this.lastAttackTime = currentTime; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    takeDamage(damage) { | 
					
					
						
						| 
							 | 
						        this.health -= damage; | 
					
					
						
						| 
							 | 
						        return this.health <= 0; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    destroy() { | 
					
					
						
						| 
							 | 
						        if (this.mesh) { | 
					
					
						
						| 
							 | 
						            this.scene.remove(this.mesh); | 
					
					
						
						| 
							 | 
						            this.bullets.forEach(bullet => this.scene.remove(bullet)); | 
					
					
						
						| 
							 | 
						            this.bullets = []; | 
					
					
						
						| 
							 | 
						            this.isLoaded = false; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						class Particle { | 
					
					
						
						| 
							 | 
						    constructor(scene, position) { | 
					
					
						
						| 
							 | 
						        const geometry = new THREE.SphereGeometry(0.1); | 
					
					
						
						| 
							 | 
						        const material = new THREE.MeshBasicMaterial({ color: 0xff0000 }); | 
					
					
						
						| 
							 | 
						        this.mesh = new THREE.Mesh(geometry, material); | 
					
					
						
						| 
							 | 
						        this.mesh.position.copy(position); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.velocity = new THREE.Vector3( | 
					
					
						
						| 
							 | 
						            (Math.random() - 0.5) * 0.3, | 
					
					
						
						| 
							 | 
						            Math.random() * 0.2, | 
					
					
						
						| 
							 | 
						            (Math.random() - 0.5) * 0.3 | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.gravity = -0.01; | 
					
					
						
						| 
							 | 
						        this.lifetime = 60; | 
					
					
						
						| 
							 | 
						        this.age = 0; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        scene.add(this.mesh); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    update() { | 
					
					
						
						| 
							 | 
						        this.velocity.y += this.gravity; | 
					
					
						
						| 
							 | 
						        this.mesh.position.add(this.velocity); | 
					
					
						
						| 
							 | 
						        this.age++; | 
					
					
						
						| 
							 | 
						        return this.age < this.lifetime; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    destroy(scene) { | 
					
					
						
						| 
							 | 
						        scene.remove(this.mesh); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						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({ antialias: true }); | 
					
					
						
						| 
							 | 
						        this.renderer.setSize(window.innerWidth, window.innerHeight); | 
					
					
						
						| 
							 | 
						        this.renderer.shadowMap.enabled = true; | 
					
					
						
						| 
							 | 
						        document.getElementById('gameContainer').appendChild(this.renderer.domElement); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.radarUpdateInterval = 100;  | 
					
					
						
						| 
							 | 
						        this.lastRadarUpdate = 0; | 
					
					
						
						| 
							 | 
						        this.radarRange = 200;  | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        this.tank = new TankPlayer(); | 
					
					
						
						| 
							 | 
						        this.enemies = []; | 
					
					
						
						| 
							 | 
						        this.particles = []; | 
					
					
						
						| 
							 | 
						        this.buildings = []; | 
					
					
						
						| 
							 | 
						        this.loader = new GLTFLoader(); | 
					
					
						
						| 
							 | 
						        this.controls = null; | 
					
					
						
						| 
							 | 
						        this.gameTime = GAME_DURATION; | 
					
					
						
						| 
							 | 
						        this.score = 0; | 
					
					
						
						| 
							 | 
						        this.isGameOver = false; | 
					
					
						
						| 
							 | 
						        this.isLoading = true; | 
					
					
						
						| 
							 | 
						        this.previousTankPosition = new THREE.Vector3(); | 
					
					
						
						| 
							 | 
						        this.lastTime = performance.now(); | 
					
					
						
						| 
							 | 
						        this.gameTimer = null; | 
					
					
						
						| 
							 | 
						        this.animationFrameId = null; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        this.mouse = { x: 0, y: 0 }; | 
					
					
						
						| 
							 | 
						        this.keys = { | 
					
					
						
						| 
							 | 
						            forward: false, | 
					
					
						
						| 
							 | 
						            backward: false, | 
					
					
						
						| 
							 | 
						            left: false, | 
					
					
						
						| 
							 | 
						            right: false | 
					
					
						
						| 
							 | 
						        }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        this.setupEventListeners(); | 
					
					
						
						| 
							 | 
						        this.initialize(); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						      async initialize() { | 
					
					
						
						| 
							 | 
						        try { | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.scene.fog = null; | 
					
					
						
						| 
							 | 
						        this.scene.background = new THREE.Color(0x87CEEB); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						               | 
					
					
						
						| 
							 | 
						          const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); | 
					
					
						
						| 
							 | 
						          this.scene.add(ambientLight); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); | 
					
					
						
						| 
							 | 
						          directionalLight.position.set(100, 100, 50); | 
					
					
						
						| 
							 | 
						          directionalLight.castShadow = true; | 
					
					
						
						| 
							 | 
						          directionalLight.shadow.mapSize.width = 1024; | 
					
					
						
						| 
							 | 
						          directionalLight.shadow.mapSize.height = 1024; | 
					
					
						
						| 
							 | 
						          this.scene.add(directionalLight); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						        const groundGeometry = new THREE.PlaneGeometry(MAP_SIZE, MAP_SIZE, 100, 100); | 
					
					
						
						| 
							 | 
						        const groundMaterial = new THREE.MeshStandardMaterial({ | 
					
					
						
						| 
							 | 
						            color: 0xD2B48C,  | 
					
					
						
						| 
							 | 
						            roughness: 0.8, | 
					
					
						
						| 
							 | 
						            metalness: 0.2, | 
					
					
						
						| 
							 | 
						            wireframe: false | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const ground = new THREE.Mesh(groundGeometry, groundMaterial); | 
					
					
						
						| 
							 | 
						        ground.rotation.x = -Math.PI / 2; | 
					
					
						
						| 
							 | 
						        ground.receiveShadow = true; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const vertices = ground.geometry.attributes.position.array; | 
					
					
						
						| 
							 | 
						        const heightScale = 15; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        for (let i = 0; i < vertices.length; i += 3) { | 
					
					
						
						| 
							 | 
						            const x = vertices[i]; | 
					
					
						
						| 
							 | 
						            const z = vertices[i + 1]; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if (x < 0 && z > 0) { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                vertices[i + 2] = 0; | 
					
					
						
						| 
							 | 
						            } else { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const distance = Math.sqrt(x * x + z * z); | 
					
					
						
						| 
							 | 
						                const height = Math.sin(distance * 0.02) * heightScale; | 
					
					
						
						| 
							 | 
						                vertices[i + 2] = Math.max(0, height);  | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        ground.geometry.attributes.position.needsUpdate = true; | 
					
					
						
						| 
							 | 
						        ground.geometry.computeVertexNormals(); | 
					
					
						
						| 
							 | 
						        this.ground = ground; | 
					
					
						
						| 
							 | 
						        this.scene.add(ground); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          const contourMaterial = new THREE.LineBasicMaterial({ | 
					
					
						
						| 
							 | 
						              color: 0x000000, | 
					
					
						
						| 
							 | 
						              opacity: 0.15, | 
					
					
						
						| 
							 | 
						              transparent: true | 
					
					
						
						| 
							 | 
						          }); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						          const contourLines = new THREE.LineSegments( | 
					
					
						
						| 
							 | 
						              new THREE.EdgesGeometry(groundGeometry), | 
					
					
						
						| 
							 | 
						              contourMaterial | 
					
					
						
						| 
							 | 
						          ); | 
					
					
						
						| 
							 | 
						          contourLines.rotation.x = -Math.PI / 2; | 
					
					
						
						| 
							 | 
						          contourLines.position.y = 0.1; | 
					
					
						
						| 
							 | 
						          this.scene.add(contourLines); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          const gridHelper = new THREE.GridHelper(flatlandRadius * 2, 50, 0x000000, 0x000000); | 
					
					
						
						| 
							 | 
						          gridHelper.material.opacity = 0.1; | 
					
					
						
						| 
							 | 
						          gridHelper.material.transparent = true; | 
					
					
						
						| 
							 | 
						          gridHelper.position.y = 0.1; | 
					
					
						
						| 
							 | 
						          this.scene.add(gridHelper); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          await this.addDesertDecorations(); | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          await this.tank.initialize(this.scene, this.loader); | 
					
					
						
						| 
							 | 
						          if (!this.tank.isLoaded) { | 
					
					
						
						| 
							 | 
						              throw new Error('Tank loading failed'); | 
					
					
						
						| 
							 | 
						          } | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          const spawnPos = this.findValidSpawnPosition(); | 
					
					
						
						| 
							 | 
						          const heightAtSpawn = this.getHeightAtPosition(spawnPos.x, spawnPos.z); | 
					
					
						
						| 
							 | 
						          const slopeCheckPoints = [ | 
					
					
						
						| 
							 | 
						              { x: spawnPos.x + 2, z: spawnPos.z }, | 
					
					
						
						| 
							 | 
						              { x: spawnPos.x - 2, z: spawnPos.z }, | 
					
					
						
						| 
							 | 
						              { x: spawnPos.x, z: spawnPos.z + 2 }, | 
					
					
						
						| 
							 | 
						              { x: spawnPos.x, z: spawnPos.z - 2 } | 
					
					
						
						| 
							 | 
						          ]; | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						          const slopes = slopeCheckPoints.map(point => { | 
					
					
						
						| 
							 | 
						              const pointHeight = this.getHeightAtPosition(point.x, point.z); | 
					
					
						
						| 
							 | 
						              return Math.abs(pointHeight - heightAtSpawn) / 2; | 
					
					
						
						| 
							 | 
						          }); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						          const maxSlope = Math.max(...slopes); | 
					
					
						
						| 
							 | 
						          if (maxSlope > 0.3) {  | 
					
					
						
						| 
							 | 
						              location.reload();  | 
					
					
						
						| 
							 | 
						              return; | 
					
					
						
						| 
							 | 
						          } | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          const tankPosition = this.tank.getPosition(); | 
					
					
						
						| 
							 | 
						          this.camera.position.set( | 
					
					
						
						| 
							 | 
						              tankPosition.x, | 
					
					
						
						| 
							 | 
						              tankPosition.y + 15, | 
					
					
						
						| 
							 | 
						              tankPosition.z - 30 | 
					
					
						
						| 
							 | 
						          ); | 
					
					
						
						| 
							 | 
						          this.camera.lookAt(tankPosition); | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          this.isLoading = false; | 
					
					
						
						| 
							 | 
						          document.getElementById('loading').style.display = 'none'; | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						           | 
					
					
						
						| 
							 | 
						          this.animate(); | 
					
					
						
						| 
							 | 
						          this.spawnEnemies(); | 
					
					
						
						| 
							 | 
						          this.startGameTimer(); | 
					
					
						
						| 
							 | 
						       | 
					
					
						
						| 
							 | 
						      } catch (error) { | 
					
					
						
						| 
							 | 
						          console.error('Game initialization error:', error); | 
					
					
						
						| 
							 | 
						          this.handleLoadingError(); | 
					
					
						
						| 
							 | 
						      } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    updateRadar() { | 
					
					
						
						| 
							 | 
						        const currentTime = Date.now(); | 
					
					
						
						| 
							 | 
						        if (currentTime - this.lastRadarUpdate < this.radarUpdateInterval) return; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const radar = document.getElementById('radar'); | 
					
					
						
						| 
							 | 
						        const radarRect = radar.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						        const radarCenter = { | 
					
					
						
						| 
							 | 
						            x: radarRect.width / 2, | 
					
					
						
						| 
							 | 
						            y: radarRect.height / 2 | 
					
					
						
						| 
							 | 
						        }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const oldDots = radar.getElementsByClassName('enemy-dot'); | 
					
					
						
						| 
							 | 
						        while (oldDots[0]) { | 
					
					
						
						| 
							 | 
						            oldDots[0].remove(); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const tankPos = this.tank.getPosition(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        this.enemies.forEach(enemy => { | 
					
					
						
						| 
							 | 
						            if (!enemy.mesh || !enemy.isLoaded) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            const enemyPos = enemy.mesh.position; | 
					
					
						
						| 
							 | 
						            const distance = tankPos.distanceTo(enemyPos); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if (distance <= this.radarRange) { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const angle = Math.atan2( | 
					
					
						
						| 
							 | 
						                    enemyPos.x - tankPos.x, | 
					
					
						
						| 
							 | 
						                    enemyPos.z - tankPos.z | 
					
					
						
						| 
							 | 
						                ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const relativeDistance = distance / this.radarRange; | 
					
					
						
						| 
							 | 
						                const dotX = radarCenter.x + Math.sin(angle) * (radarCenter.x * relativeDistance); | 
					
					
						
						| 
							 | 
						                const dotY = radarCenter.y + Math.cos(angle) * (radarCenter.y * relativeDistance); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const dot = document.createElement('div'); | 
					
					
						
						| 
							 | 
						                dot.className = 'enemy-dot'; | 
					
					
						
						| 
							 | 
						                dot.style.left = `${dotX}px`; | 
					
					
						
						| 
							 | 
						                dot.style.top = `${dotY}px`; | 
					
					
						
						| 
							 | 
						                radar.appendChild(dot); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        this.lastRadarUpdate = currentTime; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    async addDesertDecorations() { | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const rockGeometries = [ | 
					
					
						
						| 
							 | 
						            new THREE.DodecahedronGeometry(3), | 
					
					
						
						| 
							 | 
						            new THREE.DodecahedronGeometry(2), | 
					
					
						
						| 
							 | 
						            new THREE.DodecahedronGeometry(4) | 
					
					
						
						| 
							 | 
						        ]; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const rockMaterial = new THREE.MeshStandardMaterial({ | 
					
					
						
						| 
							 | 
						            color: 0x8B4513, | 
					
					
						
						| 
							 | 
						            roughness: 0.9, | 
					
					
						
						| 
							 | 
						            metalness: 0.1 | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        for (let i = 0; i < 100; i++) { | 
					
					
						
						| 
							 | 
						            const rockGeometry = rockGeometries[Math.floor(Math.random() * rockGeometries.length)]; | 
					
					
						
						| 
							 | 
						            const rock = new THREE.Mesh(rockGeometry, rockMaterial); | 
					
					
						
						| 
							 | 
						          rock.position.set( | 
					
					
						
						| 
							 | 
						               (Math.random() - 0.5) * MAP_SIZE * 0.9, | 
					
					
						
						| 
							 | 
						               Math.random() * 2, | 
					
					
						
						| 
							 | 
						               (Math.random() - 0.5) * MAP_SIZE * 0.9 | 
					
					
						
						| 
							 | 
						           ); | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           rock.rotation.set( | 
					
					
						
						| 
							 | 
						               Math.random() * Math.PI, | 
					
					
						
						| 
							 | 
						               Math.random() * Math.PI, | 
					
					
						
						| 
							 | 
						               Math.random() * Math.PI | 
					
					
						
						| 
							 | 
						           ); | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           rock.scale.set( | 
					
					
						
						| 
							 | 
						               1 + Math.random() * 0.5, | 
					
					
						
						| 
							 | 
						               1 + Math.random() * 0.5, | 
					
					
						
						| 
							 | 
						               1 + Math.random() * 0.5 | 
					
					
						
						| 
							 | 
						           ); | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           rock.castShadow = true; | 
					
					
						
						| 
							 | 
						           rock.receiveShadow = true; | 
					
					
						
						| 
							 | 
						           this.scene.add(rock); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       const cactusGeometry = new THREE.CylinderGeometry(0.5, 0.7, 4, 8); | 
					
					
						
						| 
							 | 
						       const cactusMaterial = new THREE.MeshStandardMaterial({ | 
					
					
						
						| 
							 | 
						           color: 0x2F4F2F, | 
					
					
						
						| 
							 | 
						           roughness: 0.8 | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       for (let i = 0; i < 50; i++) { | 
					
					
						
						| 
							 | 
						           const cactus = new THREE.Mesh(cactusGeometry, cactusMaterial); | 
					
					
						
						| 
							 | 
						           cactus.position.set( | 
					
					
						
						| 
							 | 
						               (Math.random() - 0.5) * MAP_SIZE * 0.8, | 
					
					
						
						| 
							 | 
						               2, | 
					
					
						
						| 
							 | 
						               (Math.random() - 0.5) * MAP_SIZE * 0.8 | 
					
					
						
						| 
							 | 
						           ); | 
					
					
						
						| 
							 | 
						           cactus.castShadow = true; | 
					
					
						
						| 
							 | 
						           cactus.receiveShadow = true; | 
					
					
						
						| 
							 | 
						           this.scene.add(cactus); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   getHeightAtPosition(x, z) { | 
					
					
						
						| 
							 | 
						        if (!this.ground) return 0; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const vertices = this.ground.geometry.attributes.position.array; | 
					
					
						
						| 
							 | 
						        const segmentsX = Math.sqrt(vertices.length / 3) - 1; | 
					
					
						
						| 
							 | 
						        const segmentsZ = segmentsX; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const gridX = ((x + MAP_SIZE / 2) / MAP_SIZE) * segmentsX; | 
					
					
						
						| 
							 | 
						        const gridZ = ((z + MAP_SIZE / 2) / MAP_SIZE) * segmentsZ; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const x1 = Math.floor(gridX); | 
					
					
						
						| 
							 | 
						        const z1 = Math.floor(gridZ); | 
					
					
						
						| 
							 | 
						        const x2 = Math.min(x1 + 1, segmentsX); | 
					
					
						
						| 
							 | 
						        const z2 = Math.min(z1 + 1, segmentsZ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const getHeight = (x, z) => { | 
					
					
						
						| 
							 | 
						            if (x < 0 || x > segmentsX || z < 0 || z > segmentsZ) return 0; | 
					
					
						
						| 
							 | 
						            const index = (z * (segmentsX + 1) + x) * 3 + 2; | 
					
					
						
						| 
							 | 
						            return vertices[index]; | 
					
					
						
						| 
							 | 
						        }; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const h11 = getHeight(x1, z1); | 
					
					
						
						| 
							 | 
						        const h21 = getHeight(x2, z1); | 
					
					
						
						| 
							 | 
						        const h12 = getHeight(x1, z2); | 
					
					
						
						| 
							 | 
						        const h22 = getHeight(x2, z2); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const fx = gridX - x1; | 
					
					
						
						| 
							 | 
						        const fz = gridZ - z1; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const h1 = h11 * (1 - fx) + h21 * fx; | 
					
					
						
						| 
							 | 
						        const h2 = h12 * (1 - fx) + h22 * fx; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        return h1 * (1 - fz) + h2 * fz; | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    findValidSpawnPosition() { | 
					
					
						
						| 
							 | 
						        const margin = 50; | 
					
					
						
						| 
							 | 
						        let position; | 
					
					
						
						| 
							 | 
						        let attempts = 0; | 
					
					
						
						| 
							 | 
						        const maxAttempts = 50; | 
					
					
						
						| 
							 | 
						        const maxSlope = 0.3;  | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        while (attempts < maxAttempts) { | 
					
					
						
						| 
							 | 
						            position = new THREE.Vector3( | 
					
					
						
						| 
							 | 
						                (Math.random() - 0.5) * (MAP_SIZE - margin * 2), | 
					
					
						
						| 
							 | 
						                0, | 
					
					
						
						| 
							 | 
						                (Math.random() - 0.5) * (MAP_SIZE - margin * 2) | 
					
					
						
						| 
							 | 
						            ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const height = this.getHeightAtPosition(position.x, position.z); | 
					
					
						
						| 
							 | 
						            position.y = height + TANK_HEIGHT; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const checkPoints = [ | 
					
					
						
						| 
							 | 
						                { x: position.x + 2, z: position.z }, | 
					
					
						
						| 
							 | 
						                { x: position.x - 2, z: position.z }, | 
					
					
						
						| 
							 | 
						                { x: position.x, z: position.z + 2 }, | 
					
					
						
						| 
							 | 
						                { x: position.x, z: position.z - 2 } | 
					
					
						
						| 
							 | 
						            ]; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            const slopes = checkPoints.map(point => { | 
					
					
						
						| 
							 | 
						                const pointHeight = this.getHeightAtPosition(point.x, point.z); | 
					
					
						
						| 
							 | 
						                return Math.abs(pointHeight - height) / 2; | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            const maxCurrentSlope = Math.max(...slopes); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if (maxCurrentSlope <= maxSlope) { | 
					
					
						
						| 
							 | 
						                return position; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            attempts++; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        return new THREE.Vector3(0, TANK_HEIGHT, 0); | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   setupEventListeners() { | 
					
					
						
						| 
							 | 
						       document.addEventListener('keydown', (event) => { | 
					
					
						
						| 
							 | 
						           if (this.isLoading || this.isGameOver) return; | 
					
					
						
						| 
							 | 
						           switch(event.code) { | 
					
					
						
						| 
							 | 
						               case 'KeyW': this.keys.forward = true; break; | 
					
					
						
						| 
							 | 
						               case 'KeyS': this.keys.backward = true; break; | 
					
					
						
						| 
							 | 
						               case 'KeyA': this.keys.left = true; break; | 
					
					
						
						| 
							 | 
						               case 'KeyD': this.keys.right = true; break; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       document.addEventListener('keyup', (event) => { | 
					
					
						
						| 
							 | 
						           if (this.isLoading || this.isGameOver) return; | 
					
					
						
						| 
							 | 
						           switch(event.code) { | 
					
					
						
						| 
							 | 
						               case 'KeyW': this.keys.forward = false; break; | 
					
					
						
						| 
							 | 
						               case 'KeyS': this.keys.backward = false; break; | 
					
					
						
						| 
							 | 
						               case 'KeyA': this.keys.left = false; break; | 
					
					
						
						| 
							 | 
						               case 'KeyD': this.keys.right = false; break; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       document.addEventListener('mousemove', (event) => { | 
					
					
						
						| 
							 | 
						           if (this.isLoading || this.isGameOver || !document.pointerLockElement) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						           const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; | 
					
					
						
						| 
							 | 
						           this.mouse.x += movementX * 0.002; | 
					
					
						
						| 
							 | 
						           this.mouse.y = 0; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						           while (this.mouse.x > Math.PI) this.mouse.x -= Math.PI * 2; | 
					
					
						
						| 
							 | 
						           while (this.mouse.x < -Math.PI) this.mouse.x += Math.PI * 2; | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       document.addEventListener('click', () => { | 
					
					
						
						| 
							 | 
						           if (!document.pointerLockElement) { | 
					
					
						
						| 
							 | 
						               document.body.requestPointerLock(); | 
					
					
						
						| 
							 | 
						           } else if (!this.isGameOver) { | 
					
					
						
						| 
							 | 
						               const bullet = this.tank.shoot(this.scene); | 
					
					
						
						| 
							 | 
						               if (bullet) { | 
					
					
						
						| 
							 | 
						                    | 
					
					
						
						| 
							 | 
						               } | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       document.addEventListener('pointerlockchange', () => { | 
					
					
						
						| 
							 | 
						           if (!document.pointerLockElement) { | 
					
					
						
						| 
							 | 
						               this.mouse.x = 0; | 
					
					
						
						| 
							 | 
						               this.mouse.y = 0; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       window.addEventListener('resize', () => { | 
					
					
						
						| 
							 | 
						           this.camera.aspect = window.innerWidth / window.innerHeight; | 
					
					
						
						| 
							 | 
						           this.camera.updateProjectionMatrix(); | 
					
					
						
						| 
							 | 
						           this.renderer.setSize(window.innerWidth, window.innerHeight); | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						  handleMovement() { | 
					
					
						
						| 
							 | 
						       if (!this.tank.isLoaded || this.isGameOver) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       const direction = new THREE.Vector3(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       if (this.keys.forward) direction.z += 1; | 
					
					
						
						| 
							 | 
						       if (this.keys.backward) direction.z -= 1; | 
					
					
						
						| 
							 | 
						       if (this.keys.left) this.tank.rotate(-1); | 
					
					
						
						| 
							 | 
						       if (this.keys.right) this.tank.rotate(1); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       if (direction.length() > 0) { | 
					
					
						
						| 
							 | 
						           direction.normalize(); | 
					
					
						
						| 
							 | 
						           direction.applyEuler(this.tank.body.rotation); | 
					
					
						
						| 
							 | 
						           this.tank.move(direction); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       const tankPos = this.tank.getPosition(); | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       const cameraDistance = 30; | 
					
					
						
						| 
							 | 
						       const cameraHeight = 15; | 
					
					
						
						| 
							 | 
						       const cameraAngle = this.mouse.x + Math.PI;  | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       const cameraX = tankPos.x + Math.sin(cameraAngle) * cameraDistance; | 
					
					
						
						| 
							 | 
						       const cameraZ = tankPos.z + Math.cos(cameraAngle) * cameraDistance; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       this.camera.position.set( | 
					
					
						
						| 
							 | 
						           cameraX, | 
					
					
						
						| 
							 | 
						           tankPos.y + cameraHeight, | 
					
					
						
						| 
							 | 
						           cameraZ | 
					
					
						
						| 
							 | 
						       ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       const lookAtPoint = new THREE.Vector3( | 
					
					
						
						| 
							 | 
						           tankPos.x, | 
					
					
						
						| 
							 | 
						           tankPos.y + 2, | 
					
					
						
						| 
							 | 
						           tankPos.z | 
					
					
						
						| 
							 | 
						       ); | 
					
					
						
						| 
							 | 
						       this.camera.lookAt(lookAtPoint); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   createBuildings() { | 
					
					
						
						| 
							 | 
						       const buildingTypes = [ | 
					
					
						
						| 
							 | 
						           { width: 10, height: 30, depth: 10, color: 0x808080 }, | 
					
					
						
						| 
							 | 
						           { width: 15, height: 40, depth: 15, color: 0x606060 }, | 
					
					
						
						| 
							 | 
						           { width: 20, height: 50, depth: 20, color: 0x404040 } | 
					
					
						
						| 
							 | 
						       ]; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       for (let i = 0; i < BUILDING_COUNT; i++) { | 
					
					
						
						| 
							 | 
						           const type = buildingTypes[Math.floor(Math.random() * buildingTypes.length)]; | 
					
					
						
						| 
							 | 
						           const building = this.createBuilding(type); | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           let position; | 
					
					
						
						| 
							 | 
						           let attempts = 0; | 
					
					
						
						| 
							 | 
						           do { | 
					
					
						
						| 
							 | 
						               position = new THREE.Vector3( | 
					
					
						
						| 
							 | 
						                   (Math.random() - 0.5) * (MAP_SIZE - type.width), | 
					
					
						
						| 
							 | 
						                   type.height / 2, | 
					
					
						
						| 
							 | 
						                   (Math.random() - 0.5) * (MAP_SIZE - type.depth) | 
					
					
						
						| 
							 | 
						               ); | 
					
					
						
						| 
							 | 
						               attempts++; | 
					
					
						
						| 
							 | 
						           } while (this.checkBuildingCollision(position, type) && attempts < 50); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						           if (attempts < 50) { | 
					
					
						
						| 
							 | 
						               building.position.copy(position); | 
					
					
						
						| 
							 | 
						               this.buildings.push(building); | 
					
					
						
						| 
							 | 
						               this.scene.add(building); | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						       return Promise.resolve(); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   createBuilding(type) { | 
					
					
						
						| 
							 | 
						       const geometry = new THREE.BoxGeometry(type.width, type.height, type.depth); | 
					
					
						
						| 
							 | 
						       const material = new THREE.MeshPhongMaterial({  | 
					
					
						
						| 
							 | 
						           color: type.color, | 
					
					
						
						| 
							 | 
						           emissive: 0x222222, | 
					
					
						
						| 
							 | 
						           specular: 0x111111, | 
					
					
						
						| 
							 | 
						           shininess: 30 | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						       const building = new THREE.Mesh(geometry, material); | 
					
					
						
						| 
							 | 
						       building.castShadow = true; | 
					
					
						
						| 
							 | 
						       building.receiveShadow = true; | 
					
					
						
						| 
							 | 
						       return building; | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						  checkBuildingCollision(position, type) { | 
					
					
						
						| 
							 | 
						       const margin = 5; | 
					
					
						
						| 
							 | 
						       const bbox = new THREE.Box3( | 
					
					
						
						| 
							 | 
						           new THREE.Vector3( | 
					
					
						
						| 
							 | 
						               position.x - (type.width / 2 + margin), | 
					
					
						
						| 
							 | 
						               0, | 
					
					
						
						| 
							 | 
						               position.z - (type.depth / 2 + margin) | 
					
					
						
						| 
							 | 
						           ), | 
					
					
						
						| 
							 | 
						           new THREE.Vector3( | 
					
					
						
						| 
							 | 
						               position.x + (type.width / 2 + margin), | 
					
					
						
						| 
							 | 
						               type.height, | 
					
					
						
						| 
							 | 
						               position.z + (type.depth / 2 + margin) | 
					
					
						
						| 
							 | 
						           ) | 
					
					
						
						| 
							 | 
						       ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       return this.buildings.some(building => { | 
					
					
						
						| 
							 | 
						           const buildingBox = new THREE.Box3().setFromObject(building); | 
					
					
						
						| 
							 | 
						           return bbox.intersectsBox(buildingBox); | 
					
					
						
						| 
							 | 
						       }); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   handleLoadingError() { | 
					
					
						
						| 
							 | 
						       this.isLoading = false; | 
					
					
						
						| 
							 | 
						       const loadingElement = document.getElementById('loading'); | 
					
					
						
						| 
							 | 
						       if (loadingElement) { | 
					
					
						
						| 
							 | 
						           loadingElement.innerHTML = ` | 
					
					
						
						| 
							 | 
						               <div class="loading-text" style="color: red;"> | 
					
					
						
						| 
							 | 
						                   Loading failed. Please refresh the page. | 
					
					
						
						| 
							 | 
						               </div> | 
					
					
						
						| 
							 | 
						           `; | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   startGameTimer() { | 
					
					
						
						| 
							 | 
						       if (this.gameTimer) { | 
					
					
						
						| 
							 | 
						           clearInterval(this.gameTimer); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       this.gameTimer = setInterval(() => { | 
					
					
						
						| 
							 | 
						           if (this.isLoading || this.isGameOver) { | 
					
					
						
						| 
							 | 
						               clearInterval(this.gameTimer); | 
					
					
						
						| 
							 | 
						               return; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           this.gameTime--; | 
					
					
						
						| 
							 | 
						           document.getElementById('time').textContent = `Time: ${this.gameTime}s`; | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           if (this.gameTime <= 0) { | 
					
					
						
						| 
							 | 
						               clearInterval(this.gameTimer); | 
					
					
						
						| 
							 | 
						               this.endGame(); | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       }, 1000); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   spawnEnemies() { | 
					
					
						
						| 
							 | 
						       const spawnEnemy = () => { | 
					
					
						
						| 
							 | 
						           if (this.enemies.length < 3 && !this.isGameOver) {   | 
					
					
						
						| 
							 | 
						               const position = this.getValidEnemySpawnPosition(); | 
					
					
						
						| 
							 | 
						               if (position) { | 
					
					
						
						| 
							 | 
						                   const type = Math.random() < 0.7 ? 'tank' : 'heavy'; | 
					
					
						
						| 
							 | 
						                   const enemy = new Enemy(this.scene, position, type); | 
					
					
						
						| 
							 | 
						                   enemy.initialize(this.loader); | 
					
					
						
						| 
							 | 
						                   this.enemies.push(enemy); | 
					
					
						
						| 
							 | 
						               } | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						           if (!this.isGameOver) { | 
					
					
						
						| 
							 | 
						               setTimeout(spawnEnemy, 10000);   | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       }; | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       spawnEnemy(); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   getValidEnemySpawnPosition() { | 
					
					
						
						| 
							 | 
						    const margin = 50; | 
					
					
						
						| 
							 | 
						    let position; | 
					
					
						
						| 
							 | 
						    let attempts = 0; | 
					
					
						
						| 
							 | 
						    const maxAttempts = 50; | 
					
					
						
						| 
							 | 
						    const maxSlope = 0.3; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    do { | 
					
					
						
						| 
							 | 
						        position = new THREE.Vector3( | 
					
					
						
						| 
							 | 
						            (Math.random() - 0.5) * (MAP_SIZE - margin * 2), | 
					
					
						
						| 
							 | 
						            0, | 
					
					
						
						| 
							 | 
						            (Math.random() - 0.5) * (MAP_SIZE - margin * 2) | 
					
					
						
						| 
							 | 
						        ); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const height = this.getHeightAtPosition(position.x, position.z); | 
					
					
						
						| 
							 | 
						        position.y = height + TANK_HEIGHT; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const checkPoints = [ | 
					
					
						
						| 
							 | 
						            { x: position.x + 2, z: position.z }, | 
					
					
						
						| 
							 | 
						            { x: position.x - 2, z: position.z }, | 
					
					
						
						| 
							 | 
						            { x: position.x, z: position.z + 2 }, | 
					
					
						
						| 
							 | 
						            { x: position.x, z: position.z - 2 } | 
					
					
						
						| 
							 | 
						        ]; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const slopes = checkPoints.map(point => { | 
					
					
						
						| 
							 | 
						            const pointHeight = this.getHeightAtPosition(point.x, point.z); | 
					
					
						
						| 
							 | 
						            return Math.abs(pointHeight - height) / 2; | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        const maxCurrentSlope = Math.max(...slopes); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const distanceToPlayer = position.distanceTo(this.tank.getPosition()); | 
					
					
						
						| 
							 | 
						        if (distanceToPlayer > 100 && maxCurrentSlope <= maxSlope) { | 
					
					
						
						| 
							 | 
						            return position; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        attempts++; | 
					
					
						
						| 
							 | 
						    } while (attempts < maxAttempts); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    return null; | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						  updateParticles() { | 
					
					
						
						| 
							 | 
						    for (let i = this.particles.length - 1; i >= 0; i--) { | 
					
					
						
						| 
							 | 
						        const particle = this.particles[i]; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        particle.velocity.y += particle.gravity; | 
					
					
						
						| 
							 | 
						        particle.mesh.position.add(particle.velocity); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        particle.mesh.material.opacity -= particle.fadeRate; | 
					
					
						
						| 
							 | 
						        particle.life--; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (particle.life <= 0 || particle.mesh.material.opacity <= 0) { | 
					
					
						
						| 
							 | 
						            this.scene.remove(particle.mesh); | 
					
					
						
						| 
							 | 
						            this.particles.splice(i, 1); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   createExplosion(position) { | 
					
					
						
						| 
							 | 
						       for (let i = 0; i < PARTICLE_COUNT; i++) { | 
					
					
						
						| 
							 | 
						           this.particles.push(new Particle(this.scene, position)); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   checkCollisions() { | 
					
					
						
						| 
							 | 
						    if (this.isLoading || !this.tank.isLoaded) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    const tankPosition = this.tank.getPosition(); | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    this.enemies.forEach(enemy => { | 
					
					
						
						| 
							 | 
						        if (!enemy.mesh || !enemy.isLoaded) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        enemy.bullets.forEach(bullet => { | 
					
					
						
						| 
							 | 
						            const distance = bullet.position.distanceTo(tankPosition); | 
					
					
						
						| 
							 | 
						            if (distance < 1) { | 
					
					
						
						| 
							 | 
						                if (this.tank.takeDamage(250)) {  | 
					
					
						
						| 
							 | 
						                    this.endGame(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						                this.scene.remove(bullet); | 
					
					
						
						| 
							 | 
						                enemy.bullets = enemy.bullets.filter(b => b !== bullet); | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                this.createExplosion(bullet.position); | 
					
					
						
						| 
							 | 
						                document.getElementById('health').style.width =  | 
					
					
						
						| 
							 | 
						                    `${(this.tank.health / MAX_HEALTH) * 100}%`; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    this.tank.bullets.forEach((bullet, bulletIndex) => { | 
					
					
						
						| 
							 | 
						        this.enemies.forEach((enemy, enemyIndex) => { | 
					
					
						
						| 
							 | 
						            if (!enemy.mesh || !enemy.isLoaded) return; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            const distance = bullet.position.distanceTo(enemy.mesh.position); | 
					
					
						
						| 
							 | 
						            if (distance < 2) { | 
					
					
						
						| 
							 | 
						                if (enemy.takeDamage(50)) { | 
					
					
						
						| 
							 | 
						                    enemy.destroy(); | 
					
					
						
						| 
							 | 
						                    this.enemies.splice(enemyIndex, 1); | 
					
					
						
						| 
							 | 
						                    this.score += 100; | 
					
					
						
						| 
							 | 
						                    document.getElementById('score').textContent = `Score: ${this.score}`; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						                this.scene.remove(bullet); | 
					
					
						
						| 
							 | 
						                this.tank.bullets.splice(bulletIndex, 1); | 
					
					
						
						| 
							 | 
						                this.createExplosion(bullet.position); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    const tankBoundingBox = new THREE.Box3().setFromObject(this.tank.body); | 
					
					
						
						| 
							 | 
						    this.enemies.forEach(enemy => { | 
					
					
						
						| 
							 | 
						        if (!enemy.mesh || !enemy.isLoaded) return; | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const enemyBoundingBox = new THREE.Box3().setFromObject(enemy.mesh); | 
					
					
						
						| 
							 | 
						        if (tankBoundingBox.intersectsBox(enemyBoundingBox)) { | 
					
					
						
						| 
							 | 
						            this.tank.body.position.copy(this.previousTankPosition); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    this.previousTankPosition.copy(this.tank.body.position); | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						  endGame() { | 
					
					
						
						| 
							 | 
						       if (this.isGameOver) return; | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       this.isGameOver = true; | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       if (this.gameTimer) { | 
					
					
						
						| 
							 | 
						           clearInterval(this.gameTimer); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       if (this.animationFrameId) { | 
					
					
						
						| 
							 | 
						           cancelAnimationFrame(this.animationFrameId); | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       document.exitPointerLock(); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       const gameOverDiv = document.createElement('div'); | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.position = 'absolute'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.top = '50%'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.left = '50%'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.transform = 'translate(-50%, -50%)'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.color = '#0f0'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.fontSize = '48px'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.backgroundColor = 'rgba(0, 20, 0, 0.7)'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.padding = '20px'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.borderRadius = '10px'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.style.textAlign = 'center'; | 
					
					
						
						| 
							 | 
						       gameOverDiv.innerHTML = ` | 
					
					
						
						| 
							 | 
						           Game Over<br> | 
					
					
						
						| 
							 | 
						           Score: ${this.score}<br> | 
					
					
						
						| 
							 | 
						           Time Survived: ${GAME_DURATION - this.gameTime}s<br> | 
					
					
						
						| 
							 | 
						           <button onclick="location.reload()"  | 
					
					
						
						| 
							 | 
						                   style="font-size: 24px; padding: 10px; margin-top: 20px;  | 
					
					
						
						| 
							 | 
						                          cursor: pointer; background: #0f0; border: none;  | 
					
					
						
						| 
							 | 
						                          color: black; border-radius: 5px;"> | 
					
					
						
						| 
							 | 
						               Play Again | 
					
					
						
						| 
							 | 
						           </button> | 
					
					
						
						| 
							 | 
						       `; | 
					
					
						
						| 
							 | 
						       document.body.appendChild(gameOverDiv); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   updateUI() { | 
					
					
						
						| 
							 | 
						       if (!this.isGameOver) { | 
					
					
						
						| 
							 | 
						           const healthBar = document.getElementById('health'); | 
					
					
						
						| 
							 | 
						           if (healthBar) { | 
					
					
						
						| 
							 | 
						               healthBar.style.width = `${(this.tank.health / MAX_HEALTH) * 100}%`; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						           const timeElement = document.getElementById('time'); | 
					
					
						
						| 
							 | 
						           if (timeElement) { | 
					
					
						
						| 
							 | 
						               timeElement.textContent = `Time: ${this.gameTime}s`; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						           const scoreElement = document.getElementById('score'); | 
					
					
						
						| 
							 | 
						           if (scoreElement) { | 
					
					
						
						| 
							 | 
						               scoreElement.textContent = `Score: ${this.score}`; | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						   animate() { | 
					
					
						
						| 
							 | 
						       if (this.isGameOver) { | 
					
					
						
						| 
							 | 
						           if (this.animationFrameId) { | 
					
					
						
						| 
							 | 
						               cancelAnimationFrame(this.animationFrameId); | 
					
					
						
						| 
							 | 
						           } | 
					
					
						
						| 
							 | 
						           return; | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       this.animationFrameId = requestAnimationFrame(() => this.animate()); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       const currentTime = performance.now(); | 
					
					
						
						| 
							 | 
						       const deltaTime = (currentTime - this.lastTime) / 1000; | 
					
					
						
						| 
							 | 
						       this.lastTime = currentTime; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						       if (!this.isLoading) { | 
					
					
						
						| 
							 | 
						           this.handleMovement(); | 
					
					
						
						| 
							 | 
						           this.tank.update(this.mouse.x, this.mouse.y, this.scene); | 
					
					
						
						| 
							 | 
						            | 
					
					
						
						| 
							 | 
						           const tankPosition = this.tank.getPosition(); | 
					
					
						
						| 
							 | 
						           this.enemies.forEach(enemy => { | 
					
					
						
						| 
							 | 
						               enemy.update(tankPosition); | 
					
					
						
						| 
							 | 
						                | 
					
					
						
						| 
							 | 
						               if (enemy.isLoaded && enemy.mesh.position.distanceTo(tankPosition) < ENEMY_CONFIG.ATTACK_RANGE) { | 
					
					
						
						| 
							 | 
						                   enemy.shoot(tankPosition); | 
					
					
						
						| 
							 | 
						               } | 
					
					
						
						| 
							 | 
						           }); | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						           this.updateParticles(); | 
					
					
						
						| 
							 | 
						           this.checkCollisions(); | 
					
					
						
						| 
							 | 
						           this.updateUI(); | 
					
					
						
						| 
							 | 
						           this.updateRadar();  | 
					
					
						
						| 
							 | 
						       } | 
					
					
						
						| 
							 | 
						        | 
					
					
						
						| 
							 | 
						       this.renderer.render(this.scene, this.camera); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						} | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						window.startGame = function() { | 
					
					
						
						| 
							 | 
						   document.getElementById('startScreen').style.display = 'none'; | 
					
					
						
						| 
							 | 
						   document.body.requestPointerLock(); | 
					
					
						
						| 
							 | 
						    | 
					
					
						
						| 
							 | 
						    | 
					
					
						
						| 
							 | 
						   if (!window.gameInstance) { | 
					
					
						
						| 
							 | 
						       window.gameInstance = new Game(); | 
					
					
						
						| 
							 | 
						   } | 
					
					
						
						| 
							 | 
						}; | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						document.addEventListener('DOMContentLoaded', () => { | 
					
					
						
						| 
							 | 
						   const game = new Game(); | 
					
					
						
						| 
							 | 
						}); |