import {AdjustmentFilter} from '@pixi/filter-adjustment'; import {GlowFilter} from '@pixi/filter-glow'; import {Container} from '@pixi/react'; import {useCallback, useEffect, useRef, useState} from 'react'; import {usePacket} from '@/react/context/client.js'; import {useDebug} from '@/react/context/debug.js'; import {useEcs, useEcsTick} from '@/react/context/ecs.js'; import {useMainEntity} from '@/react/context/main-entity.js'; import {useRadians} from '@/react/context/radians.js'; import Entity from './entity.js'; export default function Entities({monopolizers, particleWorker}) { const [debug] = useDebug(); const [ecs] = useEcs(); const containerRef = useRef(); const latestTick = useRef(); const entities = useRef({}); const pool = useRef([]); const [mainEntity] = useMainEntity(); const radians = useRadians(); const willInteractWith = useRef(0); const [interactionFilters] = useState([ new AdjustmentFilter(), new GlowFilter({color: 0x0}), ]); const pulse = (Math.cos(radians / 4) + 1) * 0.5; interactionFilters[0].brightness = (pulse * 0.75) + 1; interactionFilters[1].outerStrength = pulse * 0.5; const updateEntities = useCallback((payload) => { if (0 === Object.keys(entities.current).length) { for (let i = 0; i < 1000; ++i) { pool.current.push(new Entity()); } } for (const id in payload) { if (!payload[id]) { entities.current[id].removeFromContainer(); pool.current.push(entities.current[id]); delete entities.current[id]; continue; } const entity = ecs.get(id); if (!entity.Position) { continue; } if (!entities.current[id]) { entities.current[id] = pool.current.length > 0 ? pool.current.pop() : new Entity(); entities.current[id].reset(entity, debug); } if (mainEntity === id) { entities.current[id].setMainEntity(); } entities.current[id].update(payload[id]); entities.current[id].addToContainer(containerRef.current); } }, [debug, ecs, mainEntity]) useEffect(() => { for (const key in entities.current) { entities.current[key].setDebug(debug); } }, [debug]); usePacket('EcsChange', async () => { for (const id in entities.current) { entities.current[id].removeFromContainer(); } entities.current = {}; }); const onEcsTickEntities = useCallback((payload) => { updateEntities(payload); }, [updateEntities]); useEcsTick(onEcsTickEntities); useEffect(() => { if (!ecs || !particleWorker) { return; } async function onMessage(diff) { latestTick.current = Promise.resolve(latestTick.current).then(async () => { await ecs.apply(diff.data); updateEntities(diff.data); }); } particleWorker.addEventListener('message', onMessage); return () => { particleWorker.removeEventListener('message', onMessage); }; }, [ecs, particleWorker, updateEntities]); const onEcsTickParticles = useCallback((payload) => { for (const id in payload) { const update = payload[id]; if (update.Emitter?.emit) { for (const id in update.Emitter.emit) { particleWorker?.postMessage(update.Emitter.emit[id]); } } } }, [particleWorker]); useEcsTick(onEcsTickParticles); const onEcsTickInteractions = useCallback((payload, ecs) => { const main = ecs.get(mainEntity); if (main) { if (willInteractWith.current !== main.Interacts.willInteractWith) { if (entities.current[willInteractWith.current]) { entities.current[willInteractWith.current].diffuse.filters = []; } willInteractWith.current = main.Interacts.willInteractWith; } const interacting = entities.current[main.Interacts.willInteractWith]; if (interacting) { interacting.diffuse.filters = 0 === monopolizers.length ? interactionFilters : []; } } }, [interactionFilters, mainEntity, monopolizers]); useEcsTick(onEcsTickInteractions); return ( ); }