Fantasy-Map-Generator/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts
2022-09-10 23:04:03 +03:00

122 lines
3.9 KiB
TypeScript

import {WARN} from "config/logging";
import {getPolesOfInaccessibility} from "scripts/getPolesOfInaccessibility";
import {pick} from "utils/functionUtils";
import {getInputNumber} from "utils/nodeUtils";
import {collectStatistics} from "./collectStatistics";
import {NEUTRALS, NO_BURG} from "./config";
import {createCapitals} from "./createCapitals";
import {createStateData} from "./createStateData";
import {createTowns} from "./createTowns";
import {expandStates} from "./expandStates";
import {generateRelations} from "./generateRelations";
import {specifyBurgs} from "./specifyBurgs";
import {specifyStates} from "./specifyStates";
// prettier-ignore
type TCellsData = Pick<IPack["cells"], | "v" | "c" | "p" | "b" | "i" | "g" | "area" | "h" | "f" | "t" | "haven" | "harbor" | "r" | "fl" | "biome" | "s" | "pop" | "culture">;
export function generateBurgsAndStates(
cultures: TCultures,
features: TPackFeatures,
temp: Int8Array,
rivers: Omit<IRiver, "name" | "basin" | "type">[],
vertices: IGraphVertices,
cells: TCellsData
): {burgIds: Uint16Array; stateIds: Uint16Array; burgs: TBurgs; states: TStates; conflicts: IConflict[]} {
const cellsNumber = cells.i.length;
const scoredCellIds = getScoredCellIds();
const statesNumber = getStatesNumber(scoredCellIds.length);
if (statesNumber === 0) {
return {
burgIds: new Uint16Array(cellsNumber),
stateIds: new Uint16Array(cellsNumber),
burgs: [NO_BURG],
states: [NEUTRALS],
conflicts: []
};
}
const capitals = createCapitals(statesNumber, scoredCellIds, cultures, pick(cells, "p", "f", "culture"));
const capitalCells = new Map(capitals.map(({cell}) => [cell, true]));
const statesData = createStateData(capitals, cultures);
const towns = createTowns(
cultures,
scoredCellIds.filter(i => !capitalCells.has(i)),
pick(cells, "p", "i", "f", "s", "culture")
);
const stateIds = expandStates(
capitalCells,
statesData,
features,
pick(cells, "c", "h", "f", "t", "r", "fl", "s", "biome", "culture")
);
const burgs = specifyBurgs(
capitals,
towns,
stateIds,
features,
temp,
vertices,
cultures,
statesData,
rivers,
pick(cells, "v", "p", "g", "h", "f", "haven", "harbor", "s", "biome", "fl", "r")
);
const burgIds = assignBurgIds(burgs);
const statistics = collectStatistics({...cells, state: stateIds, burg: burgIds}, burgs);
const diplomacy = generateRelations(statesData, statistics, pick(cells, "f"));
const poles = getPolesOfInaccessibility({
vertices,
getType: (cellId: number) => stateIds[cellId],
cellNeighbors: cells.c,
cellVertices: cells.v
});
const {states, conflicts} = specifyStates(statesData, statistics, diplomacy, poles, cultures, burgs);
return {burgIds, stateIds, burgs, states, conflicts};
function getScoredCellIds() {
const score = new Int16Array(cells.s.map(s => s * Math.random()));
// filtered and sorted array of indexes: only populated cells not on map edge
const sorted = cells.i
.filter(i => !cells.b[i] && score[i] > 0 && cells.culture[i])
.sort((a, b) => score[b] - score[a]);
return sorted;
}
function getStatesNumber(populatedCells: number) {
const requestedStatesNumber = getInputNumber("regionsOutput");
if (populatedCells < requestedStatesNumber * 10) {
const maxAllowed = Math.floor(populatedCells / 10);
if (maxAllowed === 0) {
WARN && console.warn("There is no populated cells. Cannot generate states");
return 0;
}
WARN && console.warn(`Not enough populated cells (${populatedCells}). Will generate only ${maxAllowed} states`);
return maxAllowed;
}
return requestedStatesNumber;
}
function assignBurgIds(burgs: TBurgs) {
const burgIds = new Uint16Array(cellsNumber);
for (let i = 1; i < burgs.length; i++) {
const {cell} = burgs[i] as IBurg;
burgIds[cell] = i;
}
return burgIds;
}
}