silphius/app/ecs-components/tile-layers.js

180 lines
5.0 KiB
JavaScript
Raw Normal View History

2024-06-26 21:08:09 -05:00
import Component from '@/ecs/component.js';
2024-07-07 17:31:27 -05:00
import {floodwalk2D, ortho, removeCollinear} from '@/util/math.js';
2024-06-14 12:05:02 -05:00
2024-06-26 21:08:09 -05:00
import vector2d from './helpers/vector-2d';
2024-06-25 04:50:13 -05:00
2024-07-07 17:29:36 -05:00
class LayerProxy {
constructor(instance, Component, index) {
this.instance = instance;
this.Component = Component;
this.index = index;
}
get area() {
return this.layer.area;
}
get data() {
return this.layer.data;
}
2024-07-07 17:31:27 -05:00
get hulls() {
const {data, area, tileSize} = this;
const hulls = [];
const seen = {};
const n = data.length;
const {x: w, y: h} = area;
let x = 0;
let y = 0;
for (let i = 0; i < n; ++i) {
if (data[i]) {
if (!seen[i]) {
const indices = floodwalk2D(new Set([data[i]]), data, {x, y, w, h});
if (indices.size > 0) {
const pointHash = Object.create(null);
const points = [];
const seePoint = ({x, y}) => {
if (pointHash[y]?.[x]) {
return false;
}
if (!pointHash[y]) {
pointHash[y] = Object.create({});
}
return pointHash[y][x] = true;
};
for (const index of indices) {
seen[index] = true;
const op = {
x: tileSize.x * (index % area.x),
y: tileSize.y * (Math.floor(index / area.x)),
};
let p;
const tsq = {x: tileSize.x / 4, y: tileSize.y / 4};
p = {x: op.x + tsq.x, y: op.y + tsq.y};
if (seePoint(p)) {
points.push(p);
}
p = {x: op.x + tileSize.x - tsq.x, y: op.y + tsq.y};
if (seePoint(p)) {
points.push(p);
}
p = {x: op.x + tileSize.x - tsq.x, y: op.y + tileSize.y - tsq.y};
if (seePoint(p)) {
points.push(p);
}
p = {x: op.x + tsq.x, y: op.y + tileSize.y - tsq.y};
if (seePoint(p)) {
points.push(p);
}
}
hulls.push(removeCollinear(ortho(points, {x: tileSize.x / 2, y: tileSize.y / 2})));
}
}
}
x += 1;
if (x === w) {
x -= w;
y += 1;
}
}
return hulls;
}
2024-07-07 17:29:36 -05:00
get layer() {
return this.instance.layers[this.index];
}
get source() {
return this.layer.source;
}
stamp(at, data) {
const changes = {};
for (const row in data) {
const columns = data[row];
for (const column in columns) {
const tile = columns[column];
const x = at.x + parseInt(column);
const y = at.y + parseInt(row);
if (x < 0 || y < 0 || x >= this.layer.area.x || y >= this.layer.area.y) {
continue;
}
const calculated = y * this.layer.area.x + x;
this.layer.data[calculated] = tile;
changes[calculated] = tile;
}
}
this.Component.markChange(this.instance.entity, 'layerChange', {[this.index]: changes});
}
tile({x, y}) {
if (x < 0 || y < 0 || x >= this.layer.area.x || y >= this.layer.area.y) {
return undefined;
}
return this.layer.data[y * this.layer.area.x + x];
}
get tileSize() {
return this.layer.tileSize;
}
}
2024-06-26 21:08:09 -05:00
export default class TileLayers extends Component {
insertMany(entities) {
for (const [id, {layerChange}] of entities) {
if (layerChange) {
const component = this.get(id);
const {layers} = component;
for (const layerIndex in layerChange) {
for (const calculated in layerChange[layerIndex]) {
const tile = layerChange[layerIndex][calculated];
layers[layerIndex].data[calculated] = tile;
2024-06-25 04:50:13 -05:00
}
2024-06-26 21:08:09 -05:00
layers[layerIndex] = {...layers[layerIndex]};
2024-07-07 17:29:36 -05:00
component.$$layersProxies[layerIndex] = new LayerProxy(component, this, layerIndex);
2024-06-25 04:50:13 -05:00
}
}
}
2024-06-26 21:08:09 -05:00
return super.insertMany(entities);
}
2024-07-07 17:29:36 -05:00
load(instance) {
for (const index in instance.layers) {
instance.$$layersProxies[index] = new LayerProxy(instance, this, index);
}
}
2024-06-26 21:08:09 -05:00
mergeDiff(original, update) {
if (!update.layerChange) {
return super.mergeDiff(original, update);
}
const layerChange = {
...original.layerChange,
};
for (const index in update.layerChange) {
layerChange[index] = {
...layerChange[index],
...update.layerChange[index],
2024-06-25 04:50:13 -05:00
};
}
2024-06-26 21:08:09 -05:00
return {layerChange};
}
instanceFromSchema() {
2024-07-07 17:29:36 -05:00
return class TileLayersInstance extends super.instanceFromSchema() {
$$layersProxies = {};
2024-06-27 13:56:43 -05:00
layer(index) {
2024-07-07 17:29:36 -05:00
return this.$$layersProxies[index];
2024-06-26 21:08:09 -05:00
}
2024-06-27 13:56:43 -05:00
}
2024-06-26 21:08:09 -05:00
}
static properties = {
layers: {
type: 'array',
subtype: {
type: 'object',
properties: {
area: vector2d('float32'),
data: {
type: 'array',
subtype: {
type: 'uint16',
2024-06-25 04:50:13 -05:00
},
2024-06-12 13:19:16 -05:00
},
2024-06-26 21:08:09 -05:00
source: {type: 'string'},
tileSize: vector2d('float32'),
2024-06-12 13:19:16 -05:00
},
},
2024-06-26 21:08:09 -05:00
},
};
2024-06-25 04:50:13 -05:00
}