import {useEffect, useState} from 'react'; import addKeyListener from '@/add-key-listener.js'; import ClientEcs from '@/client-ecs'; import {RESOLUTION} from '@/constants.js'; import {useClient, usePacket} from '@/context/client.js'; import {useDebug} from '@/context/debug.js'; import {useEcs, useEcsTick} from '@/context/ecs.js'; import {useMainEntity} from '@/context/main-entity.js'; import Disconnected from './disconnected.jsx'; import Dom from './dom.jsx'; import HotBar from './hotbar.jsx'; import Pixi from './pixi.jsx'; import styles from './ui.module.css'; const ratio = RESOLUTION.x / RESOLUTION.y; function emptySlots() { return Array(10).fill(undefined); } export default function Ui({disconnected}) { // Key input. const client = useClient(); const [mainEntity, setMainEntity] = useMainEntity(); const [debug, setDebug] = useDebug(); const [ecs, setEcs] = useEcs(); const [showDisconnected, setShowDisconnected] = useState(false); const [bufferSlot, setBufferSlot] = useState(); const [hotbarSlots, setHotbarSlots] = useState(emptySlots()); const [activeSlot, setActiveSlot] = useState(0); const [scale, setScale] = useState(2); const [Components, setComponents] = useState(); const [Systems, setSystems] = useState(); useEffect(() => { async function setEcsStuff() { const {default: Components} = await import('@/ecs-components/index.js'); const {default: Systems} = await import('@/ecs-systems/index.js'); setComponents(Components); setSystems(Systems); } setEcsStuff(); }, []); useEffect(() => { setEcs(new ClientEcs({Components, Systems})); }, [Components, setEcs, Systems]); useEffect(() => { let handle; if (disconnected) { handle = setTimeout(() => { setShowDisconnected(true); }, 1000); } else { setShowDisconnected(false) } return () => { clearTimeout(handle); }; }, [disconnected]); useEffect(() => { return addKeyListener(document.body, ({event, type, payload}) => { const KEY_MAP = { keyDown: 1, keyUp: 0, }; let actionPayload; switch (payload) { case '-': if ('keyDown' === type) { setScale((scale) => scale > 1 ? scale - 1 : 0.666); } break; case '=': case '+': if ('keyDown' === type) { setScale((scale) => scale < 4 ? Math.floor(scale + 1) : 4); } break; case 'F3': { if (event) { event.preventDefault(); } if ('keyDown' === type) { setDebug(!debug); } break; } case 'w': { actionPayload = {type: 'moveUp', value: KEY_MAP[type]}; break; } case 'a': { actionPayload = {type: 'moveLeft', value: KEY_MAP[type]}; break; } case 's': { actionPayload = {type: 'moveDown', value: KEY_MAP[type]}; break; } case 'd': { actionPayload = {type: 'moveRight', value: KEY_MAP[type]}; break; } case ' ': { actionPayload = {type: 'use', value: KEY_MAP[type]}; break; } case 'e': { actionPayload = {type: 'interact', value: KEY_MAP[type]}; break; } case '1': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 1}; } break; } case '2': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 2}; } break; } case '3': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 3}; } break; } case '4': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 4}; } break; } case '5': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 5}; } break; } case '6': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 6}; } break; } case '7': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 7}; } break; } case '8': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 8}; } break; } case '9': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 9}; } break; } case '0': { if ('keyDown' === type) { actionPayload = {type: 'changeSlot', value: 10}; } break; } } if (actionPayload) { client.send({ type: 'Action', payload: actionPayload, }); } }); }, [client, debug, setDebug, setScale]); usePacket('EcsChange', async () => { setMainEntity(undefined); setEcs(new ClientEcs({Components, Systems})); }, [Components, Systems, setEcs, setMainEntity]); usePacket('Tick', async (payload, client) => { if (0 === Object.keys(payload.ecs).length) { return; } await ecs.apply(payload.ecs); for (const listener of client.listeners[':Ecs'] ?? []) { listener(payload.ecs); } }, [ecs]); useEcsTick((payload) => { let localMainEntity = mainEntity; for (const id in payload) { const entity = ecs.get(id); const update = payload[id]; if (update.Sound?.play) { for (const sound of update.Sound.play) { (new Audio(sound)).play(); } } if (update?.MainEntity) { setMainEntity(localMainEntity = id); } if (localMainEntity === id) { if (update.Inventory) { setBufferSlot(entity.Inventory.item(0)); const newHotbarSlots = emptySlots(); for (let i = 1; i < 11; ++i) { newHotbarSlots[i - 1] = entity.Inventory.item(i); } setHotbarSlots(newHotbarSlots); } if (update.Wielder && 'activeSlot' in update.Wielder) { setActiveSlot(update.Wielder.activeSlot); } } } }, [ecs, mainEntity]); useEffect(() => { function onContextMenu(event) { event.preventDefault(); } document.body.addEventListener('contextmenu', onContextMenu); return () => { document.body.removeEventListener('contextmenu', onContextMenu); }; }, []) return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
{ switch (event.button) { case 0: client.send({ type: 'Action', payload: {type: 'use', value: 1}, }); break; case 2: client.send({ type: 'Action', payload: {type: 'interact', value: 1}, }); break; } event.preventDefault(); }} onMouseUp={(event) => { switch (event.button) { case 0: client.send({ type: 'Action', payload: {type: 'use', value: 0}, }); break; case 2: client.send({ type: 'Action', payload: {type: 'interact', value: 0}, }); break; } event.preventDefault(); }} onWheel={(event) => { if (event.deltaY > 0) { client.send({ type: 'Action', payload: {type: 'changeSlot', value: 1 + ((activeSlot + 1) % 10)}, }); } else { client.send({ type: 'Action', payload: {type: 'changeSlot', value: 1 + ((activeSlot + 9) % 10)}, }); } }} > {mainEntity && ( { client.send({ type: 'Action', payload: {type: 'swapSlots', value: [0, i + 1]}, }); }} slots={hotbarSlots} /> {showDisconnected && ( )} )}
); }