208 lines
5.5 KiB
JavaScript
208 lines
5.5 KiB
JavaScript
import {useContext, useEffect, useState} from 'react';
|
|
|
|
import addKeyListener from '@/add-key-listener.js';
|
|
import {RESOLUTION} from '@/constants.js';
|
|
import ClientContext from '@/context/client.js';
|
|
import {useEcs} from '@/context/ecs.js';
|
|
import {useMainEntity} from '@/context/main-entity.js';
|
|
import usePacket from '@/hooks/use-packet.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 = useContext(ClientContext);
|
|
const [mainEntity, setMainEntity] = useMainEntity();
|
|
const [ecs] = useEcs();
|
|
const [showDisconnected, setShowDisconnected] = useState(false);
|
|
const [bufferSlot, setBufferSlot] = useState();
|
|
const [hotbarSlots, setHotbarSlots] = useState(emptySlots());
|
|
const [activeSlot, setActiveSlot] = useState(0);
|
|
useEffect(() => {
|
|
let handle;
|
|
if (disconnected) {
|
|
handle = setTimeout(() => {
|
|
setShowDisconnected(true);
|
|
}, 1000);
|
|
}
|
|
else {
|
|
setShowDisconnected(false)
|
|
}
|
|
return () => {
|
|
clearTimeout(handle);
|
|
};
|
|
}, [disconnected]);
|
|
useEffect(() => {
|
|
return addKeyListener(document.body, ({type, payload}) => {
|
|
const KEY_MAP = {
|
|
keyDown: 1,
|
|
keyUp: 0,
|
|
};
|
|
let actionPayload;
|
|
switch (payload) {
|
|
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 '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]);
|
|
usePacket('Tick', (payload) => {
|
|
if (0 === Object.keys(payload.ecs).length) {
|
|
return;
|
|
}
|
|
ecs.apply(payload.ecs);
|
|
let localMainEntity = mainEntity;
|
|
for (const id in payload.ecs) {
|
|
const entity = ecs.get(id);
|
|
const update = payload.ecs[id];
|
|
if (update?.MainEntity) {
|
|
setMainEntity(localMainEntity = id);
|
|
}
|
|
if (localMainEntity === id) {
|
|
if (update.Inventory) {
|
|
setBufferSlot(entity.Inventory.slots[0]);
|
|
const newHotbarSlots = emptySlots();
|
|
for (let i = 1; i < 11; ++i) {
|
|
newHotbarSlots[i - 1] = entity.Inventory.slots[i];
|
|
}
|
|
setHotbarSlots(newHotbarSlots);
|
|
}
|
|
if (update.Wielder && 'activeSlot' in update.Wielder) {
|
|
setActiveSlot(update.Wielder.activeSlot);
|
|
}
|
|
}
|
|
}
|
|
}, [hotbarSlots, mainEntity, setMainEntity]);
|
|
return (
|
|
<div
|
|
className={styles.ui}
|
|
>
|
|
<style>
|
|
{`
|
|
@media (max-aspect-ratio: ${ratio}) { .${styles.ui} { width: 100%; } }
|
|
@media (min-aspect-ratio: ${ratio}) { .${styles.ui} { height: 100%; } }
|
|
.${styles.ui} {
|
|
cursor: ${
|
|
bufferSlot
|
|
? `url('${bufferSlot.source}/icon.png'), auto !important`
|
|
: 'auto'
|
|
};
|
|
}
|
|
`}
|
|
</style>
|
|
<Pixi />
|
|
{mainEntity && (
|
|
<Dom>
|
|
<HotBar
|
|
active={activeSlot}
|
|
onActivate={(i) => {
|
|
client.send({
|
|
type: 'Action',
|
|
payload: {type: 'swapSlots', value: [0, i + 1]},
|
|
});
|
|
}}
|
|
slots={hotbarSlots}
|
|
/>
|
|
{showDisconnected && (
|
|
<Disconnected />
|
|
)}
|
|
</Dom>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|