134 lines
3.8 KiB
React
134 lines
3.8 KiB
React
|
import {useRef, useState} from 'react';
|
||
|
|
||
|
import {useEcs} from '@/context/ecs.js';
|
||
|
|
||
|
import styles from './devtools.module.css';
|
||
|
|
||
|
export default function Devtools({
|
||
|
brush,
|
||
|
layer,
|
||
|
setBrush,
|
||
|
setLayer,
|
||
|
setStamp,
|
||
|
}) {
|
||
|
const offsetRef = useRef();
|
||
|
const [selection, setSelection] = useState({x: 0, y: 0, w: 2, h: 2});
|
||
|
const [moveStart, setMoveStart] = useState();
|
||
|
const [ecs] = useEcs();
|
||
|
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;
|
||
|
return (
|
||
|
<div className={styles.devtools}>
|
||
|
<form>
|
||
|
<div className={styles.topBar}>
|
||
|
<div className={styles.layer}>
|
||
|
<label>
|
||
|
Layer:
|
||
|
<select
|
||
|
onChange={(event) => {
|
||
|
setLayer(event.target.value)
|
||
|
}}
|
||
|
value={layer}
|
||
|
>
|
||
|
{
|
||
|
Object.keys(TileLayers.layers)
|
||
|
.map((layer, i) => (
|
||
|
<option
|
||
|
key={i}
|
||
|
value={i}
|
||
|
>
|
||
|
{i}
|
||
|
</option>
|
||
|
))
|
||
|
}
|
||
|
</select>
|
||
|
</label>
|
||
|
</div>
|
||
|
<div className={styles.brush}>
|
||
|
<label>
|
||
|
Brush:
|
||
|
<select
|
||
|
onChange={(event) => {
|
||
|
setLayer(event.target.value)
|
||
|
}}
|
||
|
value={brush}
|
||
|
>
|
||
|
<option>Paint</option>
|
||
|
</select>
|
||
|
</label>
|
||
|
</div>
|
||
|
</div>
|
||
|
</form>
|
||
|
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||
|
<div
|
||
|
onMouseDown={(event) => {
|
||
|
if (!offsetRef.current) {
|
||
|
return;
|
||
|
}
|
||
|
const {left, top} = offsetRef.current.getBoundingClientRect();
|
||
|
const x = Math.floor((event.clientX - left) / tileSize.x);
|
||
|
const y = Math.floor((event.clientY - top) / tileSize.y);
|
||
|
setMoveStart({x, y});
|
||
|
setSelection({x, y, w: 1, h: 1});
|
||
|
}}
|
||
|
onMouseMove={(event) => {
|
||
|
if (!offsetRef.current) {
|
||
|
return;
|
||
|
}
|
||
|
if (!moveStart) {
|
||
|
return;
|
||
|
}
|
||
|
const {x: sx, y: sy} = moveStart;
|
||
|
const {left, top} = offsetRef.current.getBoundingClientRect();
|
||
|
const x = Math.floor(
|
||
|
Math.max(0, Math.min(w - 1, (event.clientX - left)) / tileSize.x),
|
||
|
);
|
||
|
const y = Math.floor(
|
||
|
Math.max(0, Math.min(h - 1, (event.clientY - top)) / tileSize.y),
|
||
|
);
|
||
|
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={offsetRef}
|
||
|
>
|
||
|
<div
|
||
|
className={styles.selection}
|
||
|
style={{
|
||
|
top: selection.y * tileSize.y,
|
||
|
left: selection.x * tileSize.x,
|
||
|
height: selection.h * tileSize.x,
|
||
|
width: selection.w * tileSize.y,
|
||
|
}}
|
||
|
/>
|
||
|
<img
|
||
|
alt="tileset"
|
||
|
src={TileLayers.layer(0).source.replace('.json', '.png')}
|
||
|
/>
|
||
|
</div>
|
||
|
</div>
|
||
|
);
|
||
|
}
|