131 lines
3.7 KiB
TypeScript
131 lines
3.7 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
this.updateWeatherProperty('rain')
|
|
await this.saveAndEmitWeather()
|
|
}
|
|
|
|
public async toggleFog(): Promise<void> {
|
|
this.updateWeatherProperty('fog')
|
|
await this.saveAndEmitWeather()
|
|
}
|
|
|
|
public cleanup(): void {
|
|
this.intervalId && clearInterval(this.intervalId)
|
|
}
|
|
|
|
private async loadWeather(): Promise<void> {
|
|
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<void> {
|
|
await this.saveWeather()
|
|
this.emitWeather()
|
|
}
|
|
|
|
private emitWeather(): void {
|
|
this.io?.emit('weather', this.weatherState)
|
|
}
|
|
|
|
private async saveWeather(): Promise<void> {
|
|
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()
|