import {useEffect, useRef, useState} from 'react'; import {useClient} from '@/context/client.js'; import {useEcs} from '@/context/ecs.js'; import useRect from '@/util/react-hooks/use-rect.js'; import styles from './tiles.module.css'; export default function Tiles({eventsChannel}) { const client = useClient(); const wrapperRef = useRef(); const imageRef = useRef(); const imageRect = useRect(imageRef); const [selection, setSelection] = useState({x: 0, y: 0, w: 1, h: 1}); const [moveStart, setMoveStart] = useState(); const [layer, setLayer] = useState(0); const [brush, setBrush] = useState(0); const [stamp, setStamp] = useState([]); const [ecs] = useEcs(); useEffect(() => { if (!ecs) { return false; } const master = ecs.get(1); if (!master) { return false; } const {TileLayers} = master; const {area, tileSize} = TileLayers.layer(0); function onClick({x, y}) { const at = { x: Math.floor(x / tileSize.x), y: Math.floor(y / tileSize.y), }; if (at.x < 0 || at.y < 0 || at.x >= area.x || at.y >= area.y) { return; } const payload = { brush, layer, stamp: { at, data: stamp, }, } client.send({ type: 'AdminAction', payload: {type: 'paint', value: payload}, }); } eventsChannel.addListener('click', onClick); return () => { eventsChannel.removeListener('click', onClick); }; }); if (!ecs) { return false; } const master = ecs.get(1); if (!master) { return false; } const {TileLayers} = master; const {sourceJson, tileSize} = TileLayers.layer(0); const {w, h} = sourceJson.meta.size; const factor = (imageRect?.width ?? 1) / w; return (
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
{ const wrapperRect = wrapperRef.current.getBoundingClientRect(); const {left, top} = wrapperRect; const c = { x: event.clientX, y: event.clientY + wrapperRef.current.scrollTop, }; if ( c.x - left >= (w * factor) || c.y - top >= (h * factor) ) { return; } const x = Math.floor((c.x - left) / (tileSize.x * factor)); const y = Math.floor((c.y - top) / (tileSize.y * factor)); setMoveStart({x, y}); setSelection({x, y, w: 1, h: 1}); }} onMouseMove={(event) => { if (!moveStart) { return; } const {x: sx, y: sy} = moveStart; const wrapperRect = wrapperRef.current.getBoundingClientRect(); const {left, top} = wrapperRect; const c = { x: event.clientX, y: event.clientY + wrapperRef.current.scrollTop, }; const x = Math.floor( Math.max(0, Math.min((w * factor) - 1, (c.x - left)) / (tileSize.x * factor)), ); const y = Math.floor( Math.max(0, Math.min((h * factor) - 1, (c.y - top)) / (tileSize.y * factor)), ); const mx = Math.min(sx, x); const my = Math.min(sy, y); setSelection({x: mx, y: my, w: Math.abs(sx - x) + 1, h: Math.abs(sy - y) + 1}); }} onMouseUp={() => { setMoveStart(); const stamp = []; const {x, y, w: sw, h: sh} = selection; const tw = w / tileSize.x; for (let iy = 0; iy < sh; ++iy) { const row = []; for (let ix = 0; ix < sw; ++ix) { row.push((y + iy) * tw + x + ix); } stamp.push(row); } setStamp(stamp); }} className={styles.selectionWrapper} ref={wrapperRef} >
tileset
); }