1
0
forked from noxious/client

Finish improving effects component

This commit is contained in:
Dennis Postma 2025-01-25 15:44:49 +01:00
parent 967cb1893d
commit 284ca6f64e

View File

@ -10,8 +10,30 @@ import { useMapStore } from '@/stores/mapStore'
import { Scene } from 'phavuer' import { Scene } from 'phavuer'
import { onBeforeUnmount, onMounted, ref, watch } from 'vue' import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
// Types
interface LightConfig {
SUNRISE_HOUR: number
SUNSET_HOUR: number
DAY_STRENGTH: number
NIGHT_STRENGTH: number
TRANSITION_HOURS: number
}
interface EffectObjects {
light: Phaser.GameObjects.Graphics | null
rain: Phaser.GameObjects.Particles.ParticleEmitter | null
fog: Phaser.GameObjects.Sprite | null
}
interface EffectValues {
light?: number
rain?: number
fog?: number
[key: string]: number | undefined
}
// Constants // Constants
const LIGHT_CONFIG = { const LIGHT_CONFIG: LightConfig = {
SUNRISE_HOUR: 6, SUNRISE_HOUR: 6,
SUNSET_HOUR: 20, SUNSET_HOUR: 20,
DAY_STRENGTH: 100, DAY_STRENGTH: 100,
@ -19,61 +41,18 @@ const LIGHT_CONFIG = {
TRANSITION_HOURS: 3 TRANSITION_HOURS: 3
} }
// Stores and refs // Composables
const gameStore = useGameStore() const useEffects = () => {
const mapStore = useMapStore() const effects = ref<EffectObjects>({
const mapStorage = new MapStorage() light: null,
const sceneRef = ref<Phaser.Scene | null>(null) rain: null,
const mapObject = ref<Map | null>(null) fog: 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>({
rainPercentage: 0,
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) {
await loadMap()
updateScene()
}
},
{ immediate: true }
)
const initializeEffects = (scene: Phaser.Scene) => { const initializeEffects = (scene: Phaser.Scene) => {
// Light effects.value.light = scene.add.graphics().setDepth(1000)
effects.light.value = scene.add.graphics().setDepth(1000)
// Rain effects.value.rain = scene.add
effects.rain.value = scene.add
.particles(0, 0, 'raindrop', { .particles(0, 0, 'raindrop', {
x: { min: 0, max: window.innerWidth }, x: { min: 0, max: window.innerWidth },
y: -50, y: -50,
@ -85,68 +64,120 @@ const initializeEffects = (scene: Phaser.Scene) => {
blendMode: 'ADD' blendMode: 'ADD'
}) })
.setDepth(900) .setDepth(900)
effects.rain.value.stop() effects.value.rain.stop()
// Fog effects.value.fog = scene.add
effects.fog.value = scene.add
.sprite(window.innerWidth / 2, window.innerHeight / 2, 'fog') .sprite(window.innerWidth / 2, window.innerHeight / 2, 'fog')
.setScale(2) .setScale(2)
.setAlpha(0) .setAlpha(0)
.setDepth(950) .setDepth(950)
} }
// Effect updates const applyEffects = (effectValues: EffectValues) => {
const updateScene = () => { if (effects.value.light) {
const timeBasedLight = calculateLightStrength(gameStore.world.date) const darkness = 1 - (effectValues.light ?? 0) / 100
const mapEffects = mapObject.value?.mapEffects?.reduce( effects.value.light.clear().fillStyle(0x000000, darkness).fillRect(0, 0, window.innerWidth, window.innerHeight)
(acc, curr) => ({
...acc,
[curr.effect]: curr.strength
}),
{}
) as { [key: string]: number }
const finalEffects = { ...mapEffects, light: timeBasedLight, rain: weatherState.value.rainPercentage, fog: weatherState.value.fogDensity }
applyEffects(finalEffects)
} }
const applyEffects = (effectValues: any) => { if (effects.value.rain) {
if (effects.light.value) { if (effectValues.rain) {
const darkness = 1 - effectValues.light / 100 effects.value.rain.start().setQuantity(effectValues.rain / 10)
effects.light.value.clear().fillStyle(0x000000, darkness).fillRect(0, 0, window.innerWidth, window.innerHeight) } else {
effects.value.rain.stop()
}
} }
if (effects.rain.value) { if (effects.value.fog && effectValues.fog !== undefined) {
if (effectValues.rain.value) effects.rain.value.start().setQuantity(effectValues.rain / 10) effects.value.fog.setAlpha(effectValues.fog / 100)
else effects.rain.value.stop() }
} }
if (effectValues.fog.value && effects.fog.value) effects.fog.value.setAlpha(effectValues.fog / 100) const handleResize = () => {
if (effects.value.rain) {
effects.value.rain.updateConfig({ x: { min: 0, max: window.innerWidth } })
}
if (effects.value.fog) {
effects.value.fog.setPosition(window.innerWidth / 2, window.innerHeight / 2)
}
} }
// Linear Interpolation return { effects, initializeEffects, applyEffects, handleResize }
// moves a value between x and y where a is the percentage of progress the value has made in moving from x to y }
// a = 0 return x
// a = 0.5 return (x+y)/2 // Store instances
// a = 1 return y const gameStore = useGameStore()
const lerp = (x: number, y: number, a: number) => x * (1 - a) + y * a const mapStore = useMapStore()
const mapStorage = new MapStorage()
// State
const sceneRef = ref<Phaser.Scene | null>(null)
const mapObject = ref<Map | null>(null)
const weatherState = ref<WeatherState>({
rainPercentage: 0,
fogDensity: 0
})
// Effects management
const { effects, initializeEffects, applyEffects, handleResize } = useEffects()
// Utility functions
const lerp = (x: number, y: number, a: number): number => x * (1 - a) + y * a
const calculateLightStrength = (time: Date): number => { const calculateLightStrength = (time: Date): number => {
const hour = time.getHours() const hour = time.getHours()
const minute = time.getMinutes() const minute = time.getMinutes()
const totalMinutes = (hour - (LIGHT_CONFIG.SUNSET_HOUR - 2)) * 60 + minute if (hour >= LIGHT_CONFIG.SUNSET_HOUR - LIGHT_CONFIG.TRANSITION_HOURS && hour < LIGHT_CONFIG.SUNSET_HOUR) {
return lerp(LIGHT_CONFIG.DAY_STRENGTH, LIGHT_CONFIG.NIGHT_STRENGTH, (hour + minute / 60 - (LIGHT_CONFIG.SUNSET_HOUR - LIGHT_CONFIG.TRANSITION_HOURS)) / LIGHT_CONFIG.TRANSITION_HOURS)
//Transition from daylight to night } else if (hour >= LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNRISE_HOUR + LIGHT_CONFIG.TRANSITION_HOURS) {
if (hour >= LIGHT_CONFIG.SUNSET_HOUR - LIGHT_CONFIG.TRANSITION_HOURS && hour < LIGHT_CONFIG.SUNSET_HOUR) return lerp(LIGHT_CONFIG.DAY_STRENGTH, LIGHT_CONFIG.NIGHT_STRENGTH, (hour + minute / 60 - (LIGHT_CONFIG.SUNSET_HOUR - LIGHT_CONFIG.TRANSITION_HOURS)) / LIGHT_CONFIG.TRANSITION_HOURS) return lerp(LIGHT_CONFIG.NIGHT_STRENGTH, LIGHT_CONFIG.DAY_STRENGTH, (hour + minute / 60 - LIGHT_CONFIG.SUNRISE_HOUR) / LIGHT_CONFIG.TRANSITION_HOURS)
//Transition from sunrise to morning } else if (hour > LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNSET_HOUR) {
else if (hour >= LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNRISE_HOUR + LIGHT_CONFIG.TRANSITION_HOURS) return lerp(LIGHT_CONFIG.NIGHT_STRENGTH, LIGHT_CONFIG.DAY_STRENGTH, (hour + minute / 60 - LIGHT_CONFIG.SUNRISE_HOUR) / LIGHT_CONFIG.TRANSITION_HOURS) return LIGHT_CONFIG.DAY_STRENGTH
else if (hour > LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNSET_HOUR) return LIGHT_CONFIG.DAY_STRENGTH }
else return LIGHT_CONFIG.NIGHT_STRENGTH return LIGHT_CONFIG.NIGHT_STRENGTH
} }
// Socket and window handlers // Scene handlers
const setupSocketListeners = () => { const preloadScene = (scene: Phaser.Scene): void => {
scene.load.image('raindrop', 'assets/raindrop.png')
scene.load.image('fog', 'assets/fog.png')
}
const createScene = (scene: Phaser.Scene): void => {
sceneRef.value = scene
initializeEffects(scene)
setupSocketListeners()
}
const updateScene = (): void => {
const timeBasedLight = calculateLightStrength(gameStore.world.date)
const mapEffects =
mapObject.value?.mapEffects?.reduce<Record<string, number>>(
(acc, curr) => ({
...acc,
[curr.effect]: curr.strength
}),
{}
) ?? {}
const finalEffects: EffectValues = {
...mapEffects,
light: timeBasedLight,
rain: weatherState.value.rainPercentage,
fog: weatherState.value.fogDensity
}
applyEffects(finalEffects)
}
// Map management
const loadMap = async (): Promise<void> => {
if (!mapStore.mapId) return
mapObject.value = await mapStorage.get(mapStore.mapId)
}
// Socket handlers
const setupSocketListeners = (): void => {
gameStore.connection?.emit('weather', (response: WeatherState) => { gameStore.connection?.emit('weather', (response: WeatherState) => {
weatherState.value = response weatherState.value = response
updateScene() updateScene()
@ -160,12 +191,17 @@ const setupSocketListeners = () => {
gameStore.connection?.on('date', updateScene) gameStore.connection?.on('date', updateScene)
} }
const handleResize = () => { // Watchers
if (effects.rain.value) effects.rain.value.updateConfig({ x: { min: 0, max: window.innerWidth } }) watch(
if (effects.fog.value) effects.fog.value.setPosition(window.innerWidth / 2, window.innerHeight / 2) () => mapStore.mapId,
async (newMapId) => {
if (newMapId) {
await loadMap()
updateScene()
} }
}
)
// Lifecycle
watch( watch(
() => mapObject.value, () => mapObject.value,
() => { () => {
@ -173,6 +209,7 @@ watch(
} }
) )
// Lifecycle hooks
onMounted(() => window.addEventListener('resize', handleResize)) onMounted(() => window.addEventListener('resize', handleResize))
onBeforeUnmount(() => { onBeforeUnmount(() => {