mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
150 lines
4.7 KiB
JavaScript
150 lines
4.7 KiB
JavaScript
"use strict";
|
|
|
|
window.Lakes = (function () {
|
|
const setClimateData = function (h) {
|
|
const cells = pack.cells;
|
|
const lakeOutCells = new Uint16Array(cells.i.length);
|
|
|
|
pack.features.forEach(f => {
|
|
if (f.type !== "lake") return;
|
|
|
|
// default flux: sum of precipitation around lake
|
|
f.flux = f.shoreline.reduce((acc, c) => acc + grid.cells.prec[cells.g[c]], 0);
|
|
|
|
// temperature and evaporation to detect closed lakes
|
|
f.temp = f.cells < 6 ? grid.cells.temp[cells.g[f.firstCell]] : rn(d3.mean(f.shoreline.map(c => grid.cells.temp[cells.g[c]])), 1);
|
|
const height = (f.height - 18) ** heightExponentInput.value; // height in meters
|
|
const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp); // based on Penman formula, [1-11]
|
|
f.evaporation = rn(evaporation * f.cells);
|
|
|
|
// no outlet for lakes in depressed areas
|
|
if (f.closed) return;
|
|
|
|
// lake outlet cell
|
|
f.outCell = f.shoreline[d3.scan(f.shoreline, (a, b) => h[a] - h[b])];
|
|
lakeOutCells[f.outCell] = f.i;
|
|
});
|
|
|
|
return lakeOutCells;
|
|
};
|
|
|
|
// get array of land cells aroound lake
|
|
const getShoreline = function (lake) {
|
|
const uniqueCells = new Set();
|
|
lake.vertices.forEach(v => pack.vertices.c[v].forEach(c => pack.cells.h[c] >= 20 && uniqueCells.add(c)));
|
|
lake.shoreline = [...uniqueCells];
|
|
};
|
|
|
|
const prepareLakeData = h => {
|
|
const cells = pack.cells;
|
|
const ELEVATION_LIMIT = +document.getElementById("lakeElevationLimitOutput").value;
|
|
|
|
pack.features.forEach(f => {
|
|
if (f.type !== "lake") return;
|
|
delete f.flux;
|
|
delete f.inlets;
|
|
delete f.outlet;
|
|
delete f.height;
|
|
delete f.closed;
|
|
!f.shoreline && Lakes.getShoreline(f);
|
|
|
|
// lake surface height is as lowest land cells around
|
|
const min = f.shoreline.sort((a, b) => h[a] - h[b])[0];
|
|
f.height = h[min] - 0.1;
|
|
|
|
// check if lake can be open (not in deep depression)
|
|
if (ELEVATION_LIMIT === 80) {
|
|
f.closed = false;
|
|
return;
|
|
}
|
|
|
|
let deep = true;
|
|
const threshold = f.height + ELEVATION_LIMIT;
|
|
const queue = [min];
|
|
const checked = [];
|
|
checked[min] = true;
|
|
|
|
// check if elevated lake can potentially pour to another water body
|
|
while (deep && queue.length) {
|
|
const q = queue.pop();
|
|
|
|
for (const n of cells.c[q]) {
|
|
if (checked[n]) continue;
|
|
if (h[n] >= threshold) continue;
|
|
|
|
if (h[n] < 20) {
|
|
const nFeature = pack.features[cells.f[n]];
|
|
if (nFeature.type === "ocean" || f.height > nFeature.height) {
|
|
deep = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
checked[n] = true;
|
|
queue.push(n);
|
|
}
|
|
}
|
|
|
|
f.closed = deep;
|
|
});
|
|
};
|
|
|
|
const cleanupLakeData = function () {
|
|
for (const feature of pack.features) {
|
|
if (feature.type !== "lake") continue;
|
|
delete feature.river;
|
|
delete feature.enteringFlux;
|
|
delete feature.outCell;
|
|
delete feature.closed;
|
|
feature.height = rn(feature.height, 3);
|
|
|
|
const inlets = feature.inlets?.filter(r => pack.rivers.find(river => river.i === r));
|
|
if (!inlets || !inlets.length) delete feature.inlets;
|
|
else feature.inlets = inlets;
|
|
|
|
const outlet = feature.outlet && pack.rivers.find(river => river.i === feature.outlet);
|
|
if (!outlet) delete feature.outlet;
|
|
}
|
|
};
|
|
|
|
const defineGroup = function () {
|
|
for (const feature of pack.features) {
|
|
if (feature.type !== "lake") continue;
|
|
const lakeEl = lakes.select(`[data-f="${feature.i}"]`).node();
|
|
if (!lakeEl) continue;
|
|
|
|
feature.group = getGroup(feature);
|
|
document.getElementById(feature.group).appendChild(lakeEl);
|
|
}
|
|
};
|
|
|
|
const generateName = function () {
|
|
Math.random = aleaPRNG(seed);
|
|
for (const feature of pack.features) {
|
|
if (feature.type !== "lake") continue;
|
|
feature.name = getName(feature);
|
|
}
|
|
};
|
|
|
|
const getName = function (feature) {
|
|
const landCell = pack.cells.c[feature.firstCell].find(c => pack.cells.h[c] >= 20);
|
|
const culture = pack.cells.culture[landCell];
|
|
return Names.getCulture(culture);
|
|
};
|
|
|
|
function getGroup(feature) {
|
|
if (feature.temp < -3) return "frozen";
|
|
if (feature.height > 60 && feature.cells < 10 && feature.firstCell % 10 === 0) return "lava";
|
|
|
|
if (!feature.inlets && !feature.outlet) {
|
|
if (feature.evaporation > feature.flux * 4) return "dry";
|
|
if (feature.cells < 3 && feature.firstCell % 10 === 0) return "sinkhole";
|
|
}
|
|
|
|
if (!feature.outlet && feature.evaporation > feature.flux) return "salt";
|
|
|
|
return "freshwater";
|
|
}
|
|
|
|
return {setClimateData, cleanupLakeData, prepareLakeData, defineGroup, generateName, getName, getShoreline};
|
|
})();
|