mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
Zones generator update (#1113)
* feat: style - store emblem size mod in style (v1.99.10) * fix the isOutdated function for versions past 1.99 * fix: showUploadMessage function not called correctly for isUpdated case * feat: load - improve version detection * feat: improve version detection and update process * feat: Update version and use constant for VERSION in multiple files * Update versioning.js to fix incorrect message display for stored version * feat: zones editor - update to work with pack data * feat: zones editor - update editor * feat: zones editor - update editor * chore: update version * feat: zones - regenerate * feat: zones - render zones as continuius line * feat: zones - editot changes * feat: zones - auto-update * feat: zones - generation fixes * feat: zones - generation fixes * feat: zones - restore layer * feat: zones - proselytism - check population --------- Co-authored-by: Azgaar <azgaar.fmg@yandex.com>
This commit is contained in:
parent
e77202a08a
commit
eb29c5ec5d
25 changed files with 1057 additions and 885 deletions
155
utils/pathUtils.js
Normal file
155
utils/pathUtils.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
"use strict";
|
||||
|
||||
// get continuous paths for all cells at once based on getType(cellId) comparison
|
||||
function getVertexPaths({getType, options}) {
|
||||
const {cells, vertices} = pack;
|
||||
const paths = {};
|
||||
|
||||
const checkedCells = new Uint8Array(cells.c.length);
|
||||
const addToChecked = cellId => (checkedCells[cellId] = 1);
|
||||
const isChecked = cellId => checkedCells[cellId] === 1;
|
||||
|
||||
for (let cellId = 0; cellId < cells.c.length; cellId++) {
|
||||
if (isChecked(cellId) || getType(cellId) === 0) continue;
|
||||
addToChecked(cellId);
|
||||
|
||||
const type = getType(cellId);
|
||||
const ofSameType = cellId => getType(cellId) === type;
|
||||
const ofDifferentType = cellId => getType(cellId) !== type;
|
||||
|
||||
const onborderCell = cells.c[cellId].find(ofDifferentType);
|
||||
if (onborderCell === undefined) continue;
|
||||
|
||||
const feature = pack.features[cells.f[onborderCell]];
|
||||
if (feature.type === "lake" && feature.shoreline.every(ofSameType)) continue; // inner lake
|
||||
|
||||
const startingVertex = cells.v[cellId].find(v => vertices.c[v].some(ofDifferentType));
|
||||
if (startingVertex === undefined) throw new Error(`Starting vertex for cell ${cellId} is not found`);
|
||||
|
||||
const vertexChain = connectVertices({startingVertex, ofSameType, addToChecked, closeRing: true});
|
||||
if (vertexChain.length < 3) continue;
|
||||
|
||||
addPath(type, vertexChain);
|
||||
}
|
||||
|
||||
return Object.entries(paths);
|
||||
|
||||
function getBorderPath(vertexChain, discontinue) {
|
||||
let discontinued = true;
|
||||
let lastOperation = "";
|
||||
const path = vertexChain.map(vertex => {
|
||||
if (discontinue(vertex)) {
|
||||
discontinued = true;
|
||||
return "";
|
||||
}
|
||||
|
||||
const operation = discontinued ? "M" : "L";
|
||||
const command = operation === lastOperation ? "" : operation;
|
||||
|
||||
discontinued = false;
|
||||
lastOperation = operation;
|
||||
|
||||
return ` ${command}${getVertexPoint(vertex)}`;
|
||||
});
|
||||
|
||||
return path.join("").trim();
|
||||
}
|
||||
|
||||
function isBorderVertex(vertex) {
|
||||
const adjacentCells = vertices.c[vertex];
|
||||
return adjacentCells.some(i => cells.b[i]);
|
||||
}
|
||||
|
||||
function isLandVertex(vertex) {
|
||||
const adjacentCells = vertices.c[vertex];
|
||||
return adjacentCells.every(i => cells.h[i] >= MIN_LAND_HEIGHT);
|
||||
}
|
||||
|
||||
function addPath(index, vertexChain) {
|
||||
if (!paths[index]) paths[index] = {fill: "", waterGap: "", halo: ""};
|
||||
if (options.fill) paths[index].fill += getFillPath(vertexChain);
|
||||
if (options.halo) paths[index].halo += getBorderPath(vertexChain, isBorderVertex);
|
||||
if (options.waterGap) paths[index].waterGap += getBorderPath(vertexChain, isLandVertex);
|
||||
}
|
||||
}
|
||||
|
||||
function getVertexPoint(vertexId) {
|
||||
return pack.vertices.p[vertexId];
|
||||
}
|
||||
|
||||
function getFillPath(vertexChain) {
|
||||
const points = vertexChain.map(getVertexPoint);
|
||||
const firstPoint = points.shift();
|
||||
return `M${firstPoint} L${points.join(" ")}`;
|
||||
}
|
||||
|
||||
// get single path for an non-continuous array of cells
|
||||
function getVertexPath(cellsArray) {
|
||||
const {cells, vertices} = pack;
|
||||
|
||||
const cellsObj = Object.fromEntries(cellsArray.map(cellId => [cellId, true]));
|
||||
const ofSameType = cellId => cellsObj[cellId];
|
||||
const ofDifferentType = cellId => !cellsObj[cellId];
|
||||
|
||||
const checkedCells = new Uint8Array(cells.c.length);
|
||||
const addToChecked = cellId => (checkedCells[cellId] = 1);
|
||||
const isChecked = cellId => checkedCells[cellId] === 1;
|
||||
|
||||
let path = "";
|
||||
|
||||
for (const cellId of cellsArray) {
|
||||
if (isChecked(cellId)) continue;
|
||||
|
||||
const onborderCell = cells.c[cellId].find(ofDifferentType);
|
||||
if (onborderCell === undefined) continue;
|
||||
|
||||
const feature = pack.features[cells.f[onborderCell]];
|
||||
if (feature.type === "lake" && feature.shoreline.every(ofSameType)) continue; // inner lake
|
||||
|
||||
const startingVertex = cells.v[cellId].find(v => vertices.c[v].some(ofDifferentType));
|
||||
if (startingVertex === undefined) throw new Error(`Starting vertex for cell ${cellId} is not found`);
|
||||
|
||||
const vertexChain = connectVertices({startingVertex, ofSameType, addToChecked, closeRing: true});
|
||||
if (vertexChain.length < 3) continue;
|
||||
|
||||
path += getFillPath(vertexChain);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
function connectVertices({startingVertex, ofSameType, addToChecked, closeRing}) {
|
||||
const vertices = pack.vertices;
|
||||
const MAX_ITERATIONS = pack.cells.i.length;
|
||||
const chain = []; // vertices chain to form a path
|
||||
|
||||
let next = startingVertex;
|
||||
for (let i = 0; i === 0 || next !== startingVertex; i++) {
|
||||
const previous = chain.at(-1);
|
||||
const current = next;
|
||||
chain.push(current);
|
||||
|
||||
const neibCells = vertices.c[current];
|
||||
if (addToChecked) neibCells.filter(ofSameType).forEach(addToChecked);
|
||||
|
||||
const [c1, c2, c3] = neibCells.map(ofSameType);
|
||||
const [v1, v2, v3] = vertices.v[current];
|
||||
|
||||
if (v1 !== previous && c1 !== c2) next = v1;
|
||||
else if (v2 !== previous && c2 !== c3) next = v2;
|
||||
else if (v3 !== previous && c1 !== c3) next = v3;
|
||||
|
||||
if (next === current) {
|
||||
ERROR && console.error("ConnectVertices: next vertex is not found");
|
||||
break;
|
||||
}
|
||||
|
||||
if (i === MAX_ITERATIONS) {
|
||||
ERROR && console.error("ConnectVertices: max iterations reached", MAX_ITERATIONS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (closeRing) chain.push(startingVertex);
|
||||
return chain;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue