Initial commit: 3D Hommie RPG game
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
385
js/game/Game.js
Normal file
385
js/game/Game.js
Normal file
@@ -0,0 +1,385 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user