import Phaser from 'phaser' import { AUTOSAVE_INTERVAL, TILE_SIZE } from '../config' import type { BuildingType } from '../types' import { stateManager } from '../StateManager' import { LocalAdapter } from '../NetworkAdapter' import { WorldSystem } from '../systems/WorldSystem' import { CameraSystem } from '../systems/CameraSystem' import { ResourceSystem } from '../systems/ResourceSystem' import { BuildingSystem } from '../systems/BuildingSystem' import { FarmingSystem } from '../systems/FarmingSystem' import { VillagerSystem } from '../systems/VillagerSystem' import { DebugSystem } from '../systems/DebugSystem' export class GameScene extends Phaser.Scene { private adapter!: LocalAdapter private worldSystem!: WorldSystem private cameraSystem!: CameraSystem private resourceSystem!: ResourceSystem private buildingSystem!: BuildingSystem private farmingSystem!: FarmingSystem villagerSystem!: VillagerSystem debugSystem!: DebugSystem private autosaveTimer = 0 private menuOpen = false constructor() { super({ key: 'Game' }) } /** * Initialises all game systems, wires up inter-system events, * launches the UI scene overlay, and starts the autosave timer. */ create(): void { this.adapter = new LocalAdapter() this.worldSystem = new WorldSystem(this) this.cameraSystem = new CameraSystem(this, this.adapter) this.resourceSystem = new ResourceSystem(this, this.adapter) this.buildingSystem = new BuildingSystem(this, this.adapter) this.farmingSystem = new FarmingSystem(this, this.adapter) this.villagerSystem = new VillagerSystem(this, this.adapter, this.worldSystem) this.villagerSystem.init(this.resourceSystem, this.farmingSystem) this.debugSystem = new DebugSystem(this, this.villagerSystem, this.worldSystem) this.worldSystem.create() this.renderPersistentObjects() this.cameraSystem.create() this.resourceSystem.create() this.resourceSystem.onHarvest = (msg) => this.events.emit('toast', msg) this.buildingSystem.create() this.buildingSystem.onModeChange = (active, building) => this.events.emit('buildModeChanged', active, building) this.buildingSystem.onPlaced = (msg) => { this.events.emit('toast', msg) this.renderPersistentObjects() } this.farmingSystem.create() this.farmingSystem.onMessage = (msg) => this.events.emit('toast', msg) this.farmingSystem.onToolChange = (tool, label) => this.events.emit('farmToolChanged', tool, label) this.villagerSystem.create() this.villagerSystem.onMessage = (msg) => this.events.emit('toast', msg) this.debugSystem.create() // Sync tile changes and building visuals through adapter this.adapter.onAction = (action) => { if (action.type === 'CHANGE_TILE') { this.worldSystem.setTile(action.tileX, action.tileY, action.tile) } } this.scene.launch('UI') this.events.on('selectBuilding', (kind: BuildingType) => this.buildingSystem.selectBuilding(kind)) this.events.on('uiMenuOpen', () => { this.menuOpen = true }) this.events.on('uiMenuClose', () => { this.menuOpen = false }) this.events.on('uiRequestBuildMenu', () => { if (!this.buildingSystem.isActive()) this.events.emit('openBuildMenu') }) this.events.on('updatePriorities', (villagerId: string, priorities: { chop: number; mine: number; farm: number }) => { this.adapter.send({ type: 'UPDATE_PRIORITIES', villagerId, priorities }) }) this.events.on('debugToggle', () => this.debugSystem.toggle()) this.autosaveTimer = AUTOSAVE_INTERVAL } /** * Main game loop: updates all systems and emits the cameraMoved event for the UI. * Skips system updates while a menu is open. * @param _time - Total elapsed time (unused) * @param delta - Frame delta in milliseconds */ update(_time: number, delta: number): void { if (this.menuOpen) return this.cameraSystem.update(delta) this.resourceSystem.update(delta) this.farmingSystem.update(delta) this.villagerSystem.update(delta) this.debugSystem.update() this.events.emit('cameraMoved', this.cameraSystem.getCenterTile()) this.buildingSystem.update() this.autosaveTimer -= delta if (this.autosaveTimer <= 0) { this.autosaveTimer = AUTOSAVE_INTERVAL stateManager.save() } } /** Render game objects that persist across sessions (buildings + crop sprites etc.) */ private renderPersistentObjects(): void { const state = stateManager.getState() for (const building of Object.values(state.world.buildings)) { const wx = building.tileX * TILE_SIZE + TILE_SIZE / 2 const wy = building.tileY * TILE_SIZE + TILE_SIZE / 2 const name = `bobj_${building.id}` if (this.children.getByName(name)) continue if (building.kind === 'chest') { const g = this.add.graphics().setName(name).setDepth(8) g.fillStyle(0x8B4513); g.fillRect(wx - 10, wy - 7, 20, 14) g.fillStyle(0xCD853F); g.fillRect(wx - 9, wy - 6, 18, 6) g.lineStyle(1, 0x5C3317); g.strokeRect(wx - 10, wy - 7, 20, 14) } else if (building.kind === 'bed') { this.add.image(wx, wy, 'bed_obj').setName(name).setDepth(8) } else if (building.kind === 'stockpile_zone') { this.add.image(wx, wy, 'stockpile_obj').setName(name).setDepth(4).setAlpha(0.8) } } } /** Saves game state and destroys all systems cleanly on scene shutdown. */ shutdown(): void { stateManager.save() this.worldSystem.destroy() this.resourceSystem.destroy() this.buildingSystem.destroy() this.farmingSystem.destroy() this.villagerSystem.destroy() } }