<template> <Scene name="effects" @preload="preloadScene" @create="createScene" @update="updateScene" /> </template> <script setup lang="ts"> import type { Map, WeatherState } from '@/application/types' import { useGameStore } from '@/stores/gameStore' import { MapStorage } from '@/storage/storages' import { useMapStore } from '@/stores/mapStore' import { Scene } from 'phavuer' import { onBeforeUnmount, onMounted, ref, watch } from 'vue' // Constants const LIGHT_CONFIG = { SUNRISE_HOUR: 6, SUNSET_HOUR: 20, DAY_STRENGTH: 100, NIGHT_STRENGTH: 30 } // Stores and refs const gameStore = useGameStore() const mapStore = useMapStore() const mapStorage = new MapStorage() const sceneRef = ref<Phaser.Scene | null>(null) const mapEffectsReady = ref(false) const mapObject = ref<Map | null>(null) // Effect objects const effects = { light: ref<Phaser.GameObjects.Graphics | null>(null), rain: ref<Phaser.GameObjects.Particles.ParticleEmitter | null>(null), fog: ref<Phaser.GameObjects.Sprite | null>(null) } // Weather state const weatherState = ref<WeatherState>({ isRainEnabled: false, rainPercentage: 0, isFogEnabled: false, fogDensity: 0 }) // Scene setup const preloadScene = (scene: Phaser.Scene) => { scene.load.image('raindrop', 'assets/raindrop.png') scene.load.image('fog', 'assets/fog.png') } const createScene = (scene: Phaser.Scene) => { sceneRef.value = scene initializeEffects(scene) setupSocketListeners() } const loadMap = async () => { if (!mapStore.mapId) return mapObject.value = await mapStorage.get(mapStore.mapId) } // Watch for mapId changes and load map when it's available watch( () => mapStore.mapId, async (newMapId) => { if (newMapId) { mapEffectsReady.value = false await loadMap() updateScene() } }, { immediate: true } ) const initializeEffects = (scene: Phaser.Scene) => { // Light effects.light.value = scene.add.graphics().setDepth(1000) // Rain effects.rain.value = scene.add .particles(0, 0, 'raindrop', { x: { min: 0, max: window.innerWidth }, y: -50, quantity: 5, lifespan: 2000, speedY: { min: 300, max: 500 }, scale: { start: 0.005, end: 0.005 }, alpha: { start: 0.5, end: 0 }, blendMode: 'ADD' }) .setDepth(900) effects.rain.value.stop() // Fog effects.fog.value = scene.add .sprite(window.innerWidth / 2, window.innerHeight / 2, 'fog') .setScale(2) .setAlpha(0) .setDepth(950) } // Effect updates const updateScene = () => { const timeBasedLight = calculateLightStrength(gameStore.world.date) const mapEffects = mapObject.value?.mapEffects?.reduce( (acc, curr) => ({ ...acc, [curr.effect]: curr.strength }), {} ) as { [key: string]: number } // Only update effects once mapEffects are loaded if (!mapEffectsReady.value) { if (mapObject.value) { mapEffectsReady.value = true } else { return } } const finalEffects = mapEffects && Object.keys(mapEffects).length ? mapEffects : { light: timeBasedLight, rain: weatherState.value.isRainEnabled ? weatherState.value.rainPercentage : 0, fog: weatherState.value.isFogEnabled ? weatherState.value.fogDensity * 100 : 0 } applyEffects(finalEffects) } const applyEffects = (effectValues: any) => { if (effects.light.value) { const darkness = 1 - (effectValues.light ?? 100) / 100 effects.light.value.clear().fillStyle(0x000000, darkness).fillRect(0, 0, window.innerWidth, window.innerHeight) } if (effects.rain.value) { const strength = effectValues.rain ?? 0 strength > 0 ? effects.rain.value.start().setQuantity(Math.floor((strength / 100) * 10)) : effects.rain.value.stop() } if (effects.fog.value) { effects.fog.value.setAlpha((effectValues.fog ?? 0) / 100) } } const calculateLightStrength = (time: Date): number => { const hour = time.getHours() const minute = time.getMinutes() if (hour >= LIGHT_CONFIG.SUNSET_HOUR || hour < LIGHT_CONFIG.SUNRISE_HOUR) return LIGHT_CONFIG.NIGHT_STRENGTH if (hour > LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNSET_HOUR - 2) return LIGHT_CONFIG.DAY_STRENGTH if (hour === LIGHT_CONFIG.SUNRISE_HOUR) return LIGHT_CONFIG.NIGHT_STRENGTH + ((LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * minute) / 60 const totalMinutes = (hour - (LIGHT_CONFIG.SUNSET_HOUR - 2)) * 60 + minute return LIGHT_CONFIG.DAY_STRENGTH - (LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * (totalMinutes / 120) } // Socket and window handlers const setupSocketListeners = () => { gameStore.connection?.emit('weather', (response: WeatherState) => { weatherState.value = response updateScene() }) gameStore.connection?.on('weather', (data: WeatherState) => { weatherState.value = data updateScene() }) gameStore.connection?.on('date', updateScene) } const handleResize = () => { if (effects.rain.value) effects.rain.value.updateConfig({ x: { min: 0, max: window.innerWidth } }) if (effects.fog.value) effects.fog.value.setPosition(window.innerWidth / 2, window.innerHeight / 2) } // Lifecycle watch( () => mapObject.value, () => { mapEffectsReady.value = false updateScene() }, { deep: true } ) onMounted(() => window.addEventListener('resize', handleResize)) onBeforeUnmount(() => { window.removeEventListener('resize', handleResize) if (sceneRef.value) sceneRef.value.scene.remove('effects') gameStore.connection?.off('weather') }) </script>