import * as THREE from 'three'; import { World } from './World.js'; import { Player } from './Player.js'; import { CameraController } from './Camera.js'; import { NPCManager } from './NPC.js'; import { Inventory } from './Inventory.js'; import { QuestSystem } from './QuestSystem.js'; import { UI } from './UI.js'; import { ParticleSystem } from './Particles.js'; import { Weather } from './Weather.js'; import { SaveSystem } from './SaveSystem.js'; import { SoundManager } from './SoundManager.js'; import { EventSystem } from './Events.js'; import { Dog } from './Dog.js'; import { Skills } from './Skills.js'; import { Reputation } from './Reputation.js'; import { JobSystem } from './JobSystem.js'; import { Seasons } from './Seasons.js'; import { Dangers } from './Dangers.js'; import { Equipment } from './Equipment.js'; import { Achievements } from './Achievements.js'; import { Housing } from './Housing.js'; import { Police } from './Police.js'; import { Interiors } from './Interiors.js'; export class Game { constructor(canvas) { this.canvas = canvas; this.clock = new THREE.Clock(); this.running = false; this.paused = false; // Время this.gameTime = 8 * 60; this.gameDay = 1; this.timeSpeed = 1.5; // Статистика this.totalJobsCompleted = 0; this.totalBottlesSold = 0; this.totalCrafted = 0; this.talkedNPCs = new Set(); this.visitedLocations = new Set(); this.enemiesDefeated = 0; this.consecutiveFights = 0; // Renderer this.renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; this.renderer.toneMapping = THREE.ACESFilmicToneMapping; this.renderer.toneMappingExposure = 1.0; // Scene this.scene = new THREE.Scene(); this.scene.background = new THREE.Color(0x87CEEB); this.scene.fog = new THREE.Fog(0x87CEEB, 80, 200); // Camera this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 300); this.camera.layers.enable(0); this.camera.layers.disable(1); // Core systems this.world = new World(this); this.player = new Player(this); this.cameraController = new CameraController(this); this.npcManager = new NPCManager(this); this.inventory = new Inventory(this); this.questSystem = new QuestSystem(this); this.ui = new UI(this); // Extended systems this.particles = new ParticleSystem(this); this.weather = new Weather(this); this.saveSystem = new SaveSystem(this); this.sound = new SoundManager(this); this.events = new EventSystem(this); this.dog = new Dog(this); this.skills = new Skills(this); this.reputation = new Reputation(this); this.jobSystem = new JobSystem(this); this.seasons = new Seasons(this); this.dangers = new Dangers(this); // New systems this.equipment = new Equipment(this); this.achievements = new Achievements(this); this.housing = new Housing(this); this.police = new Police(this); this.interiors = new Interiors(this); // Night sky this.stars = null; this.moon = null; // Input this.keys = {}; this.setupInput(); window.addEventListener('resize', () => this.onResize()); } async start() { this.running = true; this.paused = true; this.sound.init(); await this.world.build(); this.housing.initFromConfig(); this.player.spawn(); this.npcManager.spawnNPCs(); this.questSystem.initQuests(); this.weather.init(); this.ui.init(); this.initParticles(); this.createNightSky(); this.dog.spawn(); this.npcManager.spawnPassersby(); this.jobSystem.init(); this.world.createVehicles(); this.police.spawnPatrols(); this.animate(); // Показать интро this.ui.showIntro(() => { this.paused = false; this.canvas.requestPointerLock(); }); } async startFromSave() { this.running = true; this.sound.init(); await this.world.build(); this.housing.initFromConfig(); this.player.spawn(); this.npcManager.spawnNPCs(); this.questSystem.initQuests(); this.weather.init(); this.ui.init(); this.initParticles(); this.createNightSky(); this.dog.spawn(); this.npcManager.spawnPassersby(); this.jobSystem.init(); this.world.createVehicles(); this.police.spawnPatrols(); this.saveSystem.load(); this.animate(); } initParticles() { const shelterCfg = this.world.mapConfig?.structures?.shelter || {}; const fireX = (shelterCfg.x ?? -35) - 2; const fireZ = shelterCfg.z ?? 35; this.particles.createFire(new THREE.Vector3(fireX, 0.3, fireZ)); this.particles.createRain(); this.particles.createSnow(); } createNightSky() { // Звёзды const starCount = 800; const starGeo = new THREE.BufferGeometry(); const starPos = new Float32Array(starCount * 3); for (let i = 0; i < starCount; i++) { const theta = Math.random() * Math.PI * 2; const phi = Math.random() * Math.PI * 0.5; const r = 250; starPos[i * 3] = Math.cos(theta) * Math.sin(phi) * r; starPos[i * 3 + 1] = Math.cos(phi) * r; starPos[i * 3 + 2] = Math.sin(theta) * Math.sin(phi) * r; } starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3)); const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.8, transparent: true, opacity: 0, depthWrite: false }); this.stars = new THREE.Points(starGeo, starMat); this.scene.add(this.stars); // Луна const moonGeo = new THREE.SphereGeometry(5, 16, 16); const moonMat = new THREE.MeshBasicMaterial({ color: 0xffffee, transparent: true, opacity: 0 }); this.moon = new THREE.Mesh(moonGeo, moonMat); this.moon.position.set(-100, 120, -80); this.scene.add(this.moon); } animate() { if (!this.running) return; requestAnimationFrame(() => this.animate()); const dt = Math.min(this.clock.getDelta(), 0.1); if (this.paused) return; // Игровое время this.gameTime += this.timeSpeed * dt; if (this.gameTime >= 24 * 60) { this.gameTime -= 24 * 60; this.gameDay++; } // Все системы this.world.updateLighting(this.gameTime); this.world.updateVehicles(dt); this.player.update(dt); this.cameraController.update(dt); this.npcManager.update(dt); this.questSystem.update(dt); this.weather.update(dt); this.particles.update(dt); this.events.update(dt); this.dog.update(dt); this.seasons.update(); this.jobSystem.update(dt); this.dangers.update(dt); this.police.update(dt); this.achievements.updateChecks(); this.ui.update(dt); // Ночное небо this.updateNightSky(); // Амбиент this.updateAmbientSound(); // Рендер this.renderer.render(this.scene, this.camera); } updateNightSky() { const hour = this.gameTime / 60; let nightFactor = 0; if (hour < 5) nightFactor = 1; else if (hour < 7) nightFactor = 1 - (hour - 5) / 2; else if (hour > 19 && hour < 21) nightFactor = (hour - 19) / 2; else if (hour >= 21) nightFactor = 1; if (this.stars) { this.stars.material.opacity = nightFactor * 0.8; this.stars.rotation.y += 0.00005; } if (this.moon) { this.moon.material.opacity = nightFactor * 0.9; const moonAngle = ((hour - 18) / 12) * Math.PI; this.moon.position.set( Math.cos(moonAngle) * 100, Math.sin(moonAngle) * 100 + 40, -80 ); } } updateAmbientSound() { if (this.weather.current === 'rain') { this.sound.playAmbient('rain'); } else if (this.isNight()) { this.sound.playAmbient('night'); } else { this.sound.stopAmbient(); } } setupInput() { document.addEventListener('keydown', (e) => { if (!this.running) return; this.keys[e.code] = true; if (e.code === 'KeyE') this.player.interact(); if (e.code === 'KeyI') this.ui.toggleInventory(); if (e.code === 'KeyQ') this.ui.toggleQuests(); if (e.code === 'KeyJ') this.ui.toggleSkills(); if (e.code === 'KeyU') this.ui.toggleAchievements(); if (e.code === 'KeyF') this.player.startBegging(); if (e.code === 'KeyG') this.player.startBusking(); if (e.code === 'KeyM') { const on = this.sound.toggle(); this.notify(on ? 'Звук включён' : 'Звук выключен'); } if (e.code === 'Space') { this.dangers.playerFightBack(); } if (e.code === 'KeyH') { this.jobSystem.cancelJob(); } if (e.code === 'F5') { e.preventDefault(); this.saveSystem.save(); } }); document.addEventListener('keyup', (e) => { this.keys[e.code] = false; if (e.code === 'KeyF') this.player.stopBegging(); if (e.code === 'KeyG') this.player.stopBusking(); }); } onResize() { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); } getTimeString() { const h = Math.floor(this.gameTime / 60) % 24; const m = Math.floor(this.gameTime % 60); return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`; } isNight() { const h = this.gameTime / 60; return h < 6 || h > 21; } notify(text, type) { this.ui.showNotification(text, type); } gameOver(reason) { this.running = false; this.sound.stopAmbient(); this.sound.playHurt(); this.dangers.reset(); this.ui.showDeathScreen(reason, this.gameDay); document.exitPointerLock(); } async restart() { this.scene.clear(); this.particles.clear(); this.gameTime = 8 * 60; this.gameDay = 1; this.totalJobsCompleted = 0; this.totalBottlesSold = 0; this.totalCrafted = 0; this.talkedNPCs = new Set(); this.visitedLocations = new Set(); this.enemiesDefeated = 0; this.consecutiveFights = 0; this.player.reset(); this.inventory.reset(); this.questSystem.reset(); this.weather.reset(); this.events.reset(); this.dog.reset(); this.skills.reset(); this.reputation.reset(); this.jobSystem.reset(); this.seasons.reset(); this.dangers.reset(); this.police.reset(); this.interiors.reset(); this.equipment.reset(); this.achievements.reset(); this.housing.reset(); await this.world.build(); this.housing.initFromConfig(); this.world.createVehicles(); this.player.spawn(); this.npcManager.spawnNPCs(); this.npcManager.spawnPassersby(); this.questSystem.initQuests(); this.initParticles(); this.createNightSky(); this.dog.spawn(); this.police.spawnPatrols(); this.ui.hideDeathScreen(); this.running = true; this.clock.getDelta(); this.animate(); } }