Fantasy-Map-Generator/src/scripts/rankCells.ts
2022-07-14 01:38:45 +03:00

86 lines
2.9 KiB
TypeScript

import * as d3 from "d3";
import {TIME} from "config/logging";
import {normalize} from "utils/numberUtils";
import {isWater, isCoastal} from "utils/graphUtils";
const FLUX_MAX_BONUS = 250;
const SUITABILITY_FACTOR = 5;
// assess cells suitability for population and rank cells for culture centers and burgs placement
export function rankCells(
features: TPackFeatures,
cells: Pick<IPack["cells"], "i" | "f" | "fl" | "conf" | "r" | "h" | "area" | "biome" | "haven" | "harbor">
) {
TIME && console.time("rankCells");
const suitability = new Int16Array(cells.i.length); // cell suitability array
const population = new Float32Array(cells.i.length); // cell population array
const meanFlux = d3.median(cells.fl.filter(f => f)) || 0;
const maxFlux = (d3.max(cells.fl) || 0) + (d3.max(cells.conf) || 0); // to normalize flux
const meanArea = d3.mean(cells.area) || 0; // to adjust population by cell area
for (const cellId of cells.i) {
if (isWater(cellId)) continue; // no population in water
const habitabilityBonus = getHabitabilityBonus(cellId); // [0, 100]
if (!habitabilityBonus) continue; // uninhabitable biomes are excluded
const riverBonus = getFluxBonus(cellId); // [0, 250]
const elevationBonus = getElevationBonus(cellId); // [-10, 6]
const coastBonus = getCoastBonus(cellId); // [-30, 30]
const estuaryBonus = getEstuaryBonus(cellId); // [0, 15]
const bonuses = [habitabilityBonus, riverBonus, elevationBonus, coastBonus, estuaryBonus];
const total = d3.sum(bonuses) / SUITABILITY_FACTOR; // [-30, 311]
suitability[cellId] = total;
// cell rural population is suitability adjusted by cell area
population[cellId] = total > 0 ? total * (cells.area[cellId] / meanArea) : 0;
}
function getHabitabilityBonus(cellId: number) {
return biomesData.habitability[cells.biome[cellId]];
}
function getFluxBonus(cellId: number) {
if (!cells.fl[cellId]) return 0;
return normalize(cells.fl[cellId] + cells.conf[cellId], meanFlux, maxFlux) * FLUX_MAX_BONUS;
}
function getElevationBonus(cellId: number) {
return (50 - cells.h[cellId]) / 5;
}
function getCoastBonus(cellId: number) {
if (!isCoastal(cellId)) return 0;
const havenCell = cells.haven[cellId];
const feature = features[cells.f[havenCell]];
if (!feature) return 0;
const {group} = feature;
// lake coast
if (group === "freshwater") return 30;
if (group == "salt") return 10;
if (group == "frozen") return 1;
if (group == "dry") return 1;
if (group == "sinkhole") return 3;
if (group == "lava") return -30;
// ocean coast
if (cells.harbor[cellId] === 1) return 25; // safe harbor
return 5; // unsafe harbor
}
// estuary bonus is [0, 15]
function getEstuaryBonus(cellId: number) {
return cells.r[cellId] && isCoastal(cellId) ? 15 : 0;
}
TIME && console.timeEnd("rankCells");
return {s: suitability, pop: population};
}