import {useEffect, useRef, useState} from 'react'; import {useClient} from '@/react/context/client.js'; import {useEcs} from '@/react/context/ecs.js'; import useRect from '@/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 [indices, setIndices] = useState([]); 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; } client.send({ type: 'AdminAction', payload: { type: 'paint', value: { brush, layer, stamp: { at, data: stamp, }, }, }, }); } 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; const wt = w / tileSize.x; 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}); setStamp([[y * wt + x]]); setIndices([y * wt + x]); }} onMouseMove={(event) => { if (0 === event.buttons) { setMoveStart(); return; } 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); const sw = Math.abs(sx - x) + 1; const sh = Math.abs(sy - y) + 1; const newSelection = { x: mx, y: my, w: sw, h: sh, }; if ( selection.x === newSelection.x && selection.y === newSelection.y && selection.w === newSelection.w && selection.h === newSelection.h ) { return; } setSelection(newSelection); const stamp = []; for (let iy = 0; iy < sh; ++iy) { const row = []; for (let ix = 0; ix < sw; ++ix) { row.push((my + iy) * wt + mx + ix); } stamp.push(row); } setStamp(stamp); const indices = []; for (let sy = 0; sy < newSelection.h; ++sy) { for (let sx = 0; sx < newSelection.w; ++sx) { indices.push(((newSelection.y + sy) * wt) + (newSelection.x + sx)); } } setIndices(indices); }} className={styles.selectionWrapper} ref={wrapperRef} >
tileset
Sel: {' {'} {selection.x}{', '} {selection.y}{', '} {selection.w}{', '} {selection.h} {'}'} Idx: {' ['} {indices.join(', ')} {']'}
); }