mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
refactor(religions): spread folk religion
This commit is contained in:
parent
901120401f
commit
0a77845ebc
10 changed files with 118 additions and 68 deletions
|
|
@ -10,7 +10,7 @@ const {Names, COA} = window;
|
||||||
|
|
||||||
type TCapitals = ReturnType<typeof createCapitals>;
|
type TCapitals = ReturnType<typeof createCapitals>;
|
||||||
|
|
||||||
export function createStates(capitals: TCapitals, cultures: TCultures) {
|
export function createStates(capitals: TCapitals, cultures: TCultures): TStates {
|
||||||
TIME && console.time("createStates");
|
TIME && console.time("createStates");
|
||||||
|
|
||||||
const colors = getColors(capitals.length);
|
const colors = getColors(capitals.length);
|
||||||
|
|
@ -28,7 +28,7 @@ export function createStates(capitals: TCapitals, cultures: TCultures) {
|
||||||
const shield = COA.getShield(cultureShield, null);
|
const shield = COA.getShield(cultureShield, null);
|
||||||
const coa: ICoa = {...COA.generate(null, null, null, type), shield};
|
const coa: ICoa = {...COA.generate(null, null, null, type), shield};
|
||||||
|
|
||||||
return {i: id, name, type, center: cellId, color, expansionism, capital: id, culture: cultureId, coa};
|
return {i: id, name, type, center: cellId, color, expansionism, capital: id, culture: cultureId, coa} as IState;
|
||||||
});
|
});
|
||||||
|
|
||||||
TIME && console.timeEnd("createStates");
|
TIME && console.timeEnd("createStates");
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,8 @@ import FlatQueue from "flatqueue";
|
||||||
import {TIME} from "config/logging";
|
import {TIME} from "config/logging";
|
||||||
import {getInputNumber} from "utils/nodeUtils";
|
import {getInputNumber} from "utils/nodeUtils";
|
||||||
import {minmax} from "utils/numberUtils";
|
import {minmax} from "utils/numberUtils";
|
||||||
import type {createCapitals} from "./createCapitals";
|
|
||||||
import type {createStates} from "./createStates";
|
|
||||||
import {ELEVATION, FOREST_BIOMES, MIN_LAND_HEIGHT, DISTANCE_FIELD} from "config/generation";
|
import {ELEVATION, FOREST_BIOMES, MIN_LAND_HEIGHT, DISTANCE_FIELD} from "config/generation";
|
||||||
import {isState} from "utils/typeUtils";
|
import {isNeutals} from "utils/typeUtils";
|
||||||
|
|
||||||
type TCapitals = ReturnType<typeof createCapitals>;
|
|
||||||
type TStates = ReturnType<typeof createStates>;
|
|
||||||
|
|
||||||
// growth algorithm to assign cells to states
|
// growth algorithm to assign cells to states
|
||||||
export function expandStates(
|
export function expandStates(
|
||||||
|
|
@ -107,7 +102,7 @@ export function expandStates(
|
||||||
|
|
||||||
function getState(stateId: number) {
|
function getState(stateId: number) {
|
||||||
const state = states[stateId];
|
const state = states[stateId];
|
||||||
if (!isState(state)) throw new Error("Neutrals cannot expand");
|
if (isNeutals(state)) throw new Error("Neutrals cannot expand");
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,9 +132,6 @@ export function createPack(grid: IGrid): IPack {
|
||||||
states,
|
states,
|
||||||
cultures,
|
cultures,
|
||||||
burgs,
|
burgs,
|
||||||
cultureIds,
|
|
||||||
stateIds,
|
|
||||||
burgIds,
|
|
||||||
cells: {
|
cells: {
|
||||||
i: cells.i,
|
i: cells.i,
|
||||||
c: cells.c,
|
c: cells.c,
|
||||||
|
|
@ -144,7 +141,9 @@ export function createPack(grid: IGrid): IPack {
|
||||||
t: distanceField,
|
t: distanceField,
|
||||||
biome,
|
biome,
|
||||||
pop: population,
|
pop: population,
|
||||||
burg: burgIds
|
culture: cultureIds,
|
||||||
|
burg: burgIds,
|
||||||
|
state: stateIds
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,43 @@
|
||||||
export function expandReligions(
|
import FlatQueue from "flatqueue";
|
||||||
religions: Pick<IReligion, "i" | "center" | "culture" | "expansion" | "expansionism">[]
|
import {getInputNumber} from "utils/nodeUtils";
|
||||||
) {
|
import {gauss} from "utils/probabilityUtils";
|
||||||
const religionIds = new Uint16Array();
|
import {isReligion} from "utils/typeUtils";
|
||||||
|
|
||||||
|
type TReligionData = Pick<IReligion, "i" | "type" | "center" | "culture" | "expansion" | "expansionism">;
|
||||||
|
type TCellsData = Pick<IPack["cells"], "i" | "c" | "culture">;
|
||||||
|
|
||||||
|
export function expandReligions(religions: TReligionData[], cells: TCellsData) {
|
||||||
|
const religionIds = spreadFolkReligions(religions, cells);
|
||||||
|
|
||||||
|
const queue = new FlatQueue<{cellId: number; religionId: number}>();
|
||||||
|
const cost: number[] = [];
|
||||||
|
|
||||||
|
const neutralInput = getInputNumber("neutralInput");
|
||||||
|
const maxExpansionCost = (cells.i.length / 25) * gauss(1, 0.3, 0.2, 2, 2) * neutralInput;
|
||||||
|
|
||||||
|
for (const religion of religions) {
|
||||||
|
if (!isReligion(religion as IReligion) || (religion as IReligion).type === "Folk") continue;
|
||||||
|
|
||||||
|
const {i: religionId, center: cellId} = religion;
|
||||||
|
religionIds[cellId] = religionId;
|
||||||
|
cost[cellId] = 1;
|
||||||
|
queue.push({cellId, religionId}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return religionIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// folk religions initially get all cells of their culture
|
||||||
|
function spreadFolkReligions(religions: TReligionData[], cells: TCellsData) {
|
||||||
|
const religionIds = new Uint16Array(cells.i.length);
|
||||||
|
|
||||||
|
const folkReligions = religions.filter(({type}) => type === "Folk");
|
||||||
|
const cultureToReligionMap = new Map<number, number>(folkReligions.map(({i, culture}) => [culture, i]));
|
||||||
|
|
||||||
|
for (const cellId of cells.i) {
|
||||||
|
const cultureId = cells.culture[cellId];
|
||||||
|
religionIds[cellId] = cultureToReligionMap.get(cultureId) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
return religionIds;
|
return religionIds;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ const {forms} = religionsData;
|
||||||
|
|
||||||
export function generateOrganizedReligions(
|
export function generateOrganizedReligions(
|
||||||
burgs: TBurgs,
|
burgs: TBurgs,
|
||||||
cultureIds: Uint16Array,
|
cells: Pick<IPack["cells"], "i" | "p" | "pop" | "culture">
|
||||||
cells: Pick<IPack["cells"], "i" | "p" | "pop">
|
|
||||||
): Pick<IReligion, "type" | "form" | "culture" | "center">[] {
|
): Pick<IReligion, "type" | "form" | "culture" | "center">[] {
|
||||||
const religionsNumber = getInputNumber("religionsInput");
|
const religionsNumber = getInputNumber("religionsInput");
|
||||||
if (religionsNumber === 0) return [];
|
if (religionsNumber === 0) return [];
|
||||||
|
|
@ -32,7 +31,7 @@ export function generateOrganizedReligions(
|
||||||
return religionCells.map((cellId, index) => {
|
return religionCells.map((cellId, index) => {
|
||||||
const type = getType(index);
|
const type = getType(index);
|
||||||
const form = rw<string>(forms[type]);
|
const form = rw<string>(forms[type]);
|
||||||
const cultureId = cultureIds[cellId];
|
const cultureId = cells.culture[cellId];
|
||||||
|
|
||||||
return {type, form, culture: cultureId, center: cellId};
|
return {type, form, culture: cultureId, center: cellId};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -109,14 +109,12 @@ export function generateReligionName(
|
||||||
data: IContext
|
data: IContext
|
||||||
): {name: string; expansion: IReligion["expansion"]} {
|
): {name: string; expansion: IReligion["expansion"]} {
|
||||||
context.current = data;
|
context.current = data;
|
||||||
const {stateId, cultureId} = data;
|
|
||||||
|
|
||||||
const method = getMethod(type);
|
const method = getMethod(type);
|
||||||
const name = method.getName() || fallbackMethod.getName();
|
const name = method.getName() || fallbackMethod.getName();
|
||||||
|
|
||||||
let expansion = method.expansion as IReligion["expansion"];
|
let expansion = method.expansion as IReligion["expansion"];
|
||||||
if (expansion === "state" && !stateId) expansion = "global";
|
if (expansion === "state" && !data.stateId) expansion = "global";
|
||||||
if (expansion === "culture" && !cultureId) expansion = "global";
|
else if (expansion === "culture" && !data.cultureId) expansion = "global";
|
||||||
|
|
||||||
return {name, expansion};
|
return {name, expansion};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,37 +4,32 @@ import {generateFolkReligions} from "./generateFolkReligions";
|
||||||
import {generateOrganizedReligions} from "./generateOrganizedReligions";
|
import {generateOrganizedReligions} from "./generateOrganizedReligions";
|
||||||
import {specifyReligions} from "./specifyReligions";
|
import {specifyReligions} from "./specifyReligions";
|
||||||
|
|
||||||
type TCellsData = Pick<IPack["cells"], "i" | "c" | "p" | "g" | "h" | "t" | "biome" | "pop" | "burg">;
|
type TCellsData = Pick<
|
||||||
|
IPack["cells"],
|
||||||
|
"i" | "c" | "p" | "g" | "h" | "t" | "biome" | "pop" | "culture" | "burg" | "state"
|
||||||
|
>;
|
||||||
|
|
||||||
export function generateReligions({
|
export function generateReligions({
|
||||||
states,
|
states,
|
||||||
cultures,
|
cultures,
|
||||||
burgs,
|
burgs,
|
||||||
cultureIds,
|
|
||||||
stateIds,
|
|
||||||
burgIds,
|
|
||||||
cells
|
cells
|
||||||
}: {
|
}: {
|
||||||
states: TStates;
|
states: TStates;
|
||||||
cultures: TCultures;
|
cultures: TCultures;
|
||||||
burgs: TBurgs;
|
burgs: TBurgs;
|
||||||
cultureIds: Uint16Array;
|
|
||||||
stateIds: Uint16Array;
|
|
||||||
burgIds: Uint16Array;
|
|
||||||
cells: TCellsData;
|
cells: TCellsData;
|
||||||
}) {
|
}) {
|
||||||
TIME && console.time("generateReligions");
|
TIME && console.time("generateReligions");
|
||||||
|
|
||||||
const folkReligions = generateFolkReligions(cultures);
|
const folkReligions = generateFolkReligions(cultures);
|
||||||
const basicReligions = generateOrganizedReligions(burgs, cultureIds, pick(cells, "i", "p", "pop"));
|
const basicReligions = generateOrganizedReligions(burgs, pick(cells, "i", "p", "pop", "culture"));
|
||||||
const {religions, religionIds} = specifyReligions(
|
const {religions, religionIds} = specifyReligions(
|
||||||
[...folkReligions, ...basicReligions],
|
[...folkReligions, ...basicReligions],
|
||||||
stateIds,
|
|
||||||
burgIds,
|
|
||||||
cultures,
|
cultures,
|
||||||
states,
|
states,
|
||||||
burgs,
|
burgs,
|
||||||
cells.p
|
pick(cells, "i", "c", "culture", "burg", "state")
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(religions);
|
console.log(religions);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import {unique} from "utils/arrayUtils";
|
|
||||||
import {getMixedColor, getRandomColor} from "utils/colorUtils";
|
import {getMixedColor, getRandomColor} from "utils/colorUtils";
|
||||||
import {findAll} from "utils/graphUtils";
|
|
||||||
import {each, gauss, rand} from "utils/probabilityUtils";
|
import {each, gauss, rand} from "utils/probabilityUtils";
|
||||||
import {isCulture} from "utils/typeUtils";
|
import {isCulture} from "utils/typeUtils";
|
||||||
import {expandReligions} from "./expandReligions";
|
import {expandReligions} from "./expandReligions";
|
||||||
|
|
@ -16,19 +14,17 @@ const expansionismMap = {
|
||||||
|
|
||||||
export function specifyReligions(
|
export function specifyReligions(
|
||||||
religionsData: Pick<IReligion, "type" | "form" | "culture" | "center">[],
|
religionsData: Pick<IReligion, "type" | "form" | "culture" | "center">[],
|
||||||
stateIds: Uint16Array,
|
|
||||||
burgIds: Uint16Array,
|
|
||||||
cultures: TCultures,
|
cultures: TCultures,
|
||||||
states: TStates,
|
states: TStates,
|
||||||
burgs: TBurgs,
|
burgs: TBurgs,
|
||||||
points: TPoints
|
cells: Pick<IPack["cells"], "i" | "c" | "culture" | "burg" | "state">
|
||||||
) {
|
): {religions: TReligions; religionIds: Uint16Array} {
|
||||||
const religions = religionsData.map(({type, form, culture: cultureId, center}, index) => {
|
const rawReligions = religionsData.map(({type, form, culture: cultureId, center}, index) => {
|
||||||
const supreme = getDeityName(cultures, cultureId);
|
const supreme = getDeityName(cultures, cultureId);
|
||||||
const deity = form === "Non-theism" || form === "Animism" ? null : supreme;
|
const deity = form === "Non-theism" || form === "Animism" ? null : supreme;
|
||||||
|
|
||||||
const stateId = stateIds[center];
|
const stateId = cells.state[center];
|
||||||
const burgId = burgIds[center];
|
const burgId = cells.burg[center];
|
||||||
|
|
||||||
const {name, expansion} = generateReligionName(type, {
|
const {name, expansion} = generateReligionName(type, {
|
||||||
cultureId,
|
cultureId,
|
||||||
|
|
@ -46,19 +42,26 @@ export function specifyReligions(
|
||||||
const culture = cultures[cultureId];
|
const culture = cultures[cultureId];
|
||||||
const color = isCulture(culture) ? getMixedColor(culture.color, 0.1, 0) : getRandomColor();
|
const color = isCulture(culture) ? getMixedColor(culture.color, 0.1, 0) : getRandomColor();
|
||||||
|
|
||||||
const origins: number[] = []; // define after religions expansion
|
return {i: index + 1, name, type, form, culture: cultureId, center, deity, expansion, expansionism, color};
|
||||||
return {i: index + 1, name, type, form, culture: cultureId, center, deity, expansion, expansionism, color, origins};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const religionIds = expandReligions(religions);
|
const religionIds = expandReligions(rawReligions, cells);
|
||||||
|
const names = renameOldReligions(rawReligions);
|
||||||
|
const origins = defineOrigins(religionIds, rawReligions, cells.c);
|
||||||
|
|
||||||
const names = renameOldReligions(religions);
|
return {religions: combineReligionsData(), religionIds};
|
||||||
const origins = defineOrigins(religionIds, religions, points);
|
|
||||||
|
|
||||||
return {
|
function combineReligionsData(): TReligions {
|
||||||
religions: religions.map((religion, index) => ({name: names[index], religion, origins: origins[index]})),
|
const noReligion: TNoReligion = {i: 0, name: "No religion"};
|
||||||
religionIds
|
|
||||||
};
|
const religions = rawReligions.map((religion, index) => ({
|
||||||
|
...religion,
|
||||||
|
name: names[index],
|
||||||
|
origins: origins[index]
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [noReligion, ...religions];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add 'Old' to names of folk religions which have organized competitors
|
// add 'Old' to names of folk religions which have organized competitors
|
||||||
|
|
@ -74,38 +77,58 @@ function renameOldReligions(religions: Pick<IReligion, "name" | "culture" | "typ
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const religionOriginsParamsMap = {
|
||||||
|
Organized: {clusterSize: 100, maxReligions: 2},
|
||||||
|
Cult: {clusterSize: 50, maxReligions: 3},
|
||||||
|
Heresy: {clusterSize: 50, maxReligions: 43}
|
||||||
|
};
|
||||||
|
|
||||||
function defineOrigins(
|
function defineOrigins(
|
||||||
religionIds: Uint16Array,
|
religionIds: Uint16Array,
|
||||||
religions: Pick<IReligion, "i" | "culture" | "type" | "expansion" | "center">[],
|
religions: Pick<IReligion, "i" | "culture" | "type" | "expansion" | "center">[],
|
||||||
points: TPoints
|
neighbors: number[][]
|
||||||
) {
|
) {
|
||||||
return religions.map(religion => {
|
return religions.map(religion => {
|
||||||
if (religion.type === "Folk") return [0];
|
if (religion.type === "Folk") return [0];
|
||||||
|
|
||||||
const {type, culture: cultureId, expansion, center} = religion;
|
const {i, type, culture: cultureId, expansion, center} = religion;
|
||||||
const [x, y] = points[center];
|
|
||||||
|
|
||||||
const folkReligion = religions.find(({culture, type}) => type === "Folk" || culture === cultureId);
|
const folkReligion = religions.find(({culture, type}) => type === "Folk" || culture === cultureId);
|
||||||
const isFolkBased = folkReligion && cultureId && expansion === "culture" && each(2)(center);
|
const isFolkBased = folkReligion && cultureId && expansion === "culture" && each(2)(center);
|
||||||
|
|
||||||
if (isFolkBased) return [folkReligion.i];
|
if (isFolkBased) return [folkReligion.i];
|
||||||
|
|
||||||
if (type === "Organized") {
|
const {clusterSize, maxReligions} = religionOriginsParamsMap[type];
|
||||||
const origins = getReligionsInRadius(religionIds, {x, y, r: 150 / religions.length, max: 2});
|
const origins = getReligionsInRadius(neighbors, center, religionIds, i, clusterSize, maxReligions);
|
||||||
return origins;
|
|
||||||
}
|
|
||||||
|
|
||||||
const origins = getReligionsInRadius(religionIds, {x, y, r: 300 / religions.length, max: rand(0, 4)});
|
|
||||||
return origins;
|
return origins;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReligionsInRadius(
|
function getReligionsInRadius(
|
||||||
|
neighbors: number[][],
|
||||||
|
center: number,
|
||||||
religionIds: Uint16Array,
|
religionIds: Uint16Array,
|
||||||
{x, y, r, max}: {x: number; y: number; r: number; max: number}
|
religionId: number,
|
||||||
|
clusterSize: number,
|
||||||
|
maxReligions: number
|
||||||
) {
|
) {
|
||||||
if (max === 0) return [0];
|
const religions = new Set<number>();
|
||||||
const cellsInRadius: number[] = []; // findAll(x, y, r);
|
const queue = [center];
|
||||||
const religions = unique(cellsInRadius.map(i => religionIds[i]).filter(r => r));
|
const checked = <{[key: number]: true}>{};
|
||||||
return religions.length ? religions.slice(0, max) : [0];
|
|
||||||
|
for (let size = 0; queue.length && size < clusterSize; size++) {
|
||||||
|
const cellId = queue.pop()!;
|
||||||
|
checked[center] = true;
|
||||||
|
|
||||||
|
for (const neibId of neighbors[cellId]) {
|
||||||
|
if (checked[neibId]) continue;
|
||||||
|
checked[neibId] = true;
|
||||||
|
|
||||||
|
const neibReligion = religionIds[neibId];
|
||||||
|
if (neibReligion && neibReligion !== religionId) religions.add(neibReligion);
|
||||||
|
queue.push(neibId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return religions.size ? [...religions].slice(0, maxReligions) : [0];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
src/types/pack/religions.d.ts
vendored
2
src/types/pack/religions.d.ts
vendored
|
|
@ -15,7 +15,7 @@ interface IReligion {
|
||||||
|
|
||||||
type TNoReligion = {
|
type TNoReligion = {
|
||||||
i: 0;
|
i: 0;
|
||||||
name: string;
|
name: "No religion";
|
||||||
};
|
};
|
||||||
|
|
||||||
type TReligions = [TNoReligion, ...IReligion[]];
|
type TReligions = [TNoReligion, ...IReligion[]];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
export const isState = (state: TNeutrals | IState): state is IState => state.i !== 0 && !(state as IState).removed;
|
export const isState = (state: TNeutrals | IState): state is IState => state.i !== 0 && !(state as IState).removed;
|
||||||
|
|
||||||
|
export const isNeutals = (neutrals: TNeutrals | IState): neutrals is TNeutrals => neutrals.i === 0;
|
||||||
|
|
||||||
export const isCulture = (culture: TWilderness | ICulture): culture is ICulture =>
|
export const isCulture = (culture: TWilderness | ICulture): culture is ICulture =>
|
||||||
culture.i !== 0 && !(culture as ICulture).removed;
|
culture.i !== 0 && !(culture as ICulture).removed;
|
||||||
|
|
||||||
export const isBurg = (burg: TNoBurg | IBurg): burg is IBurg => burg.i !== 0 && !(burg as IBurg).removed;
|
export const isBurg = (burg: TNoBurg | IBurg): burg is IBurg => burg.i !== 0 && !(burg as IBurg).removed;
|
||||||
|
|
||||||
|
export const isReligion = (religion: TNoReligion | IReligion): religion is IReligion =>
|
||||||
|
religion.i !== 0 && !(religion as IReligion).removed;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue