import { Server } from 'socket.io' import Logger, { LoggerType } from '#application/logger' import { World } from '#entities/world' import SocketManager from '#managers/socketManager' import WorldRepository from '#repositories/worldRepository' type WeatherState = { rainPercentage: number fogDensity: number } class WeatherManager { private static readonly CONFIG = { UPDATE_INTERVAL_MS: 60_000, // Check weather every minute RAIN_CHANCE: 0.2, // 20% chance FOG_CHANCE: 0.15, // 15% chance RAIN_PERCENTAGE_RANGE: { min: 50, max: 100 }, FOG_DENSITY_RANGE: { min: 30, max: 100 } } as const private readonly logger = Logger.type(LoggerType.APP) private io: Server | null = null private intervalId: NodeJS.Timeout | null = null private weatherState: WeatherState = { rainPercentage: 0, fogDensity: 0 } public async boot(): Promise { this.io = SocketManager.getIO() await this.loadWeather() this.startWeatherLoop() this.logger.info('Weather manager loaded') } public getWeatherState(): WeatherState { return { ...this.weatherState } } public randomWeatherValue(type: 'rain' | 'fog' ) { switch (type) { case 'rain': return this.getRandomNumber(WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max) case 'fog': return this.getRandomNumber(WeatherManager.CONFIG.FOG_DENSITY_RANGE.min, WeatherManager.CONFIG.FOG_DENSITY_RANGE.max) } } public async setRainValue(value? : number): Promise { if (value === undefined) { value = this.weatherState.fogDensity > 0 ? 0 : this.randomWeatherValue('fog') } this.updateWeatherProperty('rain', value) await this.saveAndEmitWeather() } public async setFogValue(value? : number): Promise { if (value === undefined) { value = this.weatherState.fogDensity > 0 ? 0 : this.randomWeatherValue('fog') } this.updateWeatherProperty('fog', value) await this.saveAndEmitWeather() } public cleanup(): void { this.intervalId && clearInterval(this.intervalId) } private async loadWeather(): Promise { try { const worldRepository = new WorldRepository() const world = await worldRepository.getFirst() if (world) { this.weatherState = { rainPercentage: world.rainPercentage, fogDensity: world.fogDensity } } } catch (error) { this.logError('load', error) } } private startWeatherLoop(): void { this.intervalId = setInterval(async () => { this.updateRandomWeather() await this.saveAndEmitWeather() }, WeatherManager.CONFIG.UPDATE_INTERVAL_MS) } private updateRandomWeather(): void { if (Math.random() < WeatherManager.CONFIG.RAIN_CHANCE) { this.updateWeatherProperty('rain', this.randomWeatherValue('rain') ) } if (Math.random() < WeatherManager.CONFIG.FOG_CHANCE) { this.updateWeatherProperty('fog', this.randomWeatherValue('fog')) } } private updateWeatherProperty(type: 'rain' | 'fog', value: number): void { if (type === 'rain') { this.weatherState.rainPercentage = value } if (type === 'fog') { this.weatherState.fogDensity = value } } private async saveAndEmitWeather(): Promise { await this.saveWeather() this.emitWeather() } private emitWeather(): void { this.io?.emit('weather', this.weatherState) } private async saveWeather(): Promise { try { const worldRepository = new WorldRepository() let world = await worldRepository.getFirst() if (!world) world = new World() //left them in here because the await world .setRainPercentage(this.weatherState.rainPercentage) .setIsRainEnabled(this.weatherState.rainPercentage > 0) .setFogDensity(this.weatherState.fogDensity) .setIsFogEnabled(this.weatherState.fogDensity > 0) .save() } catch (error) { this.logError('save', error) } } private getRandomNumber(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min } private logError(operation: string, error: unknown): void { this.logger.error(`Failed to ${operation} weather: ${error instanceof Error ? error.message : String(error)}`) } } export default new WeatherManager()