silphius/app/react-components/targeting-grid.jsx
2024-06-25 08:41:20 -05:00

109 lines
3.3 KiB
JavaScript

import {RenderTexture} from '@pixi/core';
import {Container} from '@pixi/display';
import {BlurFilter} from '@pixi/filter-blur';
import {Graphics} from '@pixi/graphics';
import {PixiComponent, useApp} from '@pixi/react';
import {Sprite} from '@pixi/sprite';
import {useEffect, useState} from 'react';
const tileSize = {x: 16, y: 16};
const radius = 9;
function makeFade(renderer) {
const fade = new Graphics();
fade.beginFill(0xffffff);
fade.alpha = 0.5;
fade.drawCircle(
tileSize.x * (radius / 2),
tileSize.y * (radius / 2),
((tileSize.x + tileSize.y) / 2) * (radius * 0.35),
)
fade.filters = [new BlurFilter(24)];
const renderTexture = RenderTexture.create({
width: tileSize.x * radius,
height: tileSize.y * radius,
});
renderer.render(fade, {renderTexture});
return new Sprite(renderTexture);
}
const TargetingGridInternal = PixiComponent('TargetingGrid', {
create: ({app, ecs}) => {
const fade = makeFade(app.renderer);
const grid = new Graphics();
const lineWidth = 1;
grid.lineStyle(lineWidth, 0xffffff);
for (let y = 0; y < radius; ++y) {
for (let x = 0; x < radius; ++x) {
grid.drawRect(
(x * tileSize.x) + (lineWidth / 2),
(y * tileSize.y) + (lineWidth / 2),
tileSize.x,
tileSize.y,
);
}
}
grid.mask = fade;
const innerGrid = new Graphics();
innerGrid.lineStyle(lineWidth, 0x777777);
for (let y = 0; y < radius; ++y) {
for (let x = 0; x < radius; ++x) {
innerGrid.drawRect(
(x * tileSize.x) + (lineWidth / 2) + 1,
(y * tileSize.y) + (lineWidth / 2) + 1,
tileSize.x - 2,
tileSize.y - 2,
);
}
}
innerGrid.mask = fade;
const container = new Container();
container.addChild(fade, grid, innerGrid);
// Clamp within layer area.
const area = new Graphics();
area.beginFill(0xffffff);
const {TileLayers: {layers: [layer]}} = ecs.get(1)
const {x, y} = layer.area;
area.drawRect(0, 0, x * tileSize.x, y * tileSize.y);
container.mask = area;
const top = new Container();
top.addChild(container, area);
return top;
},
applyProps: ({children: [container]}, oldProps, {entity, radians}) => {
const pulse = (Math.cos(radians) + 1) * 0.5;
const {Position: {x, y}} = entity;
const tileX = x - (x % tileSize.x);
const tileY = y - (y % tileSize.y);
const gridX = (tileSize.x * (radius / 2));
const gridY = (tileSize.y * (radius / 2));
container.x = tileX + (tileSize.x / 2) - gridX;
container.y = tileY + (tileSize.y / 2) - gridY;
container.children[0].x = x % tileSize.x - (tileSize.x / 2) ;
container.children[0].y = y % tileSize.y - (tileSize.y / 2) ;
const color = Math.round(255 - (pulse * 255));
container.children[2].tint = `rgb(${color}, ${color}, ${color})`;
},
})
export default function TargetingGrid({ecs, entity}) {
const app = useApp();
const [radians, setRadians] = useState(0);
useEffect(() => {
const handle = setInterval(() => {
setRadians((radians) => (radians + 0.1) % (Math.PI * 2))
}, 50);
return () => {
clearInterval(handle);
};
}, []);
return (
<TargetingGridInternal
app={app}
ecs={ecs}
entity={entity}
radians={radians}
/>
);
}