import { Server } from 'socket.io' import Logger, { LoggerType } from '#application/logger' import SocketManager from '#managers/socketManager' import worldRepository from '#repositories/worldRepository' import worldService from '#services/worldService' type WeatherState = { isRainEnabled: boolean rainPercentage: number isFogEnabled: boolean 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 = { isRainEnabled: false, rainPercentage: 0, isFogEnabled: false, 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 async toggleRain(): Promise { this.updateWeatherProperty('rain') await this.saveAndEmitWeather() } public async toggleFog(): Promise { this.updateWeatherProperty('fog') await this.saveAndEmitWeather() } public cleanup(): void { this.intervalId && clearInterval(this.intervalId) } private async loadWeather(): Promise { try { const world = await worldRepository.getFirst() if (world) { this.weatherState = { isRainEnabled: world.isRainEnabled, rainPercentage: world.rainPercentage, isFogEnabled: world.isFogEnabled, 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') } if (Math.random() < WeatherManager.CONFIG.FOG_CHANCE) { this.updateWeatherProperty('fog') } } private updateWeatherProperty(type: 'rain' | 'fog'): void { if (type === 'rain') { this.weatherState.isRainEnabled = !this.weatherState.isRainEnabled this.weatherState.rainPercentage = this.weatherState.isRainEnabled ? this.getRandomNumber(WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max) : 0 } if (type === 'fog') { this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled this.weatherState.fogDensity = this.weatherState.isFogEnabled ? this.getRandomNumber(WeatherManager.CONFIG.FOG_DENSITY_RANGE.min, WeatherManager.CONFIG.FOG_DENSITY_RANGE.max) : 0 } } private async saveAndEmitWeather(): Promise { await this.saveWeather() this.emitWeather() } private emitWeather(): void { this.io?.emit('weather', this.weatherState) } private async saveWeather(): Promise { try { await worldService.update(this.weatherState) } 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()