180 lines
5.0 KiB
Vue
180 lines
5.0 KiB
Vue
<template>
|
|
<Scene name="effects" @preload="preloadScene" @create="createScene" @update="updateScene" />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { Scene } from 'phavuer'
|
|
import { useZoneStore } from '@/stores/zoneStore'
|
|
import { useGameStore } from '@/stores/gameStore'
|
|
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
import type { WeatherState } from '@/application/types'
|
|
|
|
// Constants
|
|
const LIGHT_CONFIG = {
|
|
SUNRISE_HOUR: 6,
|
|
SUNSET_HOUR: 20,
|
|
DAY_STRENGTH: 100,
|
|
NIGHT_STRENGTH: 30
|
|
}
|
|
|
|
// Stores and refs
|
|
const gameStore = useGameStore()
|
|
const zoneStore = useZoneStore()
|
|
const sceneRef = ref<Phaser.Scene | null>(null)
|
|
const zoneEffectsReady = ref(false)
|
|
|
|
// 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 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 zoneEffects = zoneStore.zone?.zoneEffects?.reduce(
|
|
(acc, curr) => ({
|
|
...acc,
|
|
[curr.effect]: curr.strength
|
|
}),
|
|
{}
|
|
) as { [key: string]: number }
|
|
|
|
// Only update effects once zoneEffects are loaded
|
|
if (!zoneEffectsReady.value) {
|
|
if (zoneEffects && Object.keys(zoneEffects).length) {
|
|
zoneEffectsReady.value = true
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
|
|
const finalEffects =
|
|
zoneEffects && Object.keys(zoneEffects).length
|
|
? zoneEffects
|
|
: {
|
|
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(
|
|
() => zoneStore.zone,
|
|
() => {
|
|
zoneEffectsReady.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>
|