diff --git a/src/scripts/generation/pack/burgsAndStates/createStates.ts b/src/scripts/generation/pack/burgsAndStates/createStates.ts index 8f232734..73c5591b 100644 --- a/src/scripts/generation/pack/burgsAndStates/createStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/createStates.ts @@ -10,7 +10,7 @@ const {Names, COA} = window; type TCapitals = ReturnType; -export function createStates(capitals: TCapitals, cultures: TCultures) { +export function createStates(capitals: TCapitals, cultures: TCultures): TStates { TIME && console.time("createStates"); const colors = getColors(capitals.length); @@ -28,7 +28,7 @@ export function createStates(capitals: TCapitals, cultures: TCultures) { const shield = COA.getShield(cultureShield, null); 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"); diff --git a/src/scripts/generation/pack/burgsAndStates/expandStates.ts b/src/scripts/generation/pack/burgsAndStates/expandStates.ts index 98f1237d..1de4dcf8 100644 --- a/src/scripts/generation/pack/burgsAndStates/expandStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/expandStates.ts @@ -3,13 +3,8 @@ import FlatQueue from "flatqueue"; import {TIME} from "config/logging"; import {getInputNumber} from "utils/nodeUtils"; 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 {isState} from "utils/typeUtils"; - -type TCapitals = ReturnType; -type TStates = ReturnType; +import {isNeutals} from "utils/typeUtils"; // growth algorithm to assign cells to states export function expandStates( @@ -107,7 +102,7 @@ export function expandStates( function getState(stateId: number) { const state = states[stateId]; - if (!isState(state)) throw new Error("Neutrals cannot expand"); + if (isNeutals(state)) throw new Error("Neutrals cannot expand"); return state; } diff --git a/src/scripts/generation/pack/pack.ts b/src/scripts/generation/pack/pack.ts index 1746c41f..70f7e0fe 100644 --- a/src/scripts/generation/pack/pack.ts +++ b/src/scripts/generation/pack/pack.ts @@ -132,9 +132,6 @@ export function createPack(grid: IGrid): IPack { states, cultures, burgs, - cultureIds, - stateIds, - burgIds, cells: { i: cells.i, c: cells.c, @@ -144,7 +141,9 @@ export function createPack(grid: IGrid): IPack { t: distanceField, biome, pop: population, - burg: burgIds + culture: cultureIds, + burg: burgIds, + state: stateIds } }); diff --git a/src/scripts/generation/pack/religions/expandReligions.ts b/src/scripts/generation/pack/religions/expandReligions.ts index 314e1a59..12d1207b 100644 --- a/src/scripts/generation/pack/religions/expandReligions.ts +++ b/src/scripts/generation/pack/religions/expandReligions.ts @@ -1,7 +1,43 @@ -export function expandReligions( - religions: Pick[] -) { - const religionIds = new Uint16Array(); +import FlatQueue from "flatqueue"; +import {getInputNumber} from "utils/nodeUtils"; +import {gauss} from "utils/probabilityUtils"; +import {isReligion} from "utils/typeUtils"; + +type TReligionData = Pick; +type TCellsData = Pick; + +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(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; } diff --git a/src/scripts/generation/pack/religions/generateOrganizedReligions.ts b/src/scripts/generation/pack/religions/generateOrganizedReligions.ts index 6df0e5be..9049a0be 100644 --- a/src/scripts/generation/pack/religions/generateOrganizedReligions.ts +++ b/src/scripts/generation/pack/religions/generateOrganizedReligions.ts @@ -10,8 +10,7 @@ const {forms} = religionsData; export function generateOrganizedReligions( burgs: TBurgs, - cultureIds: Uint16Array, - cells: Pick + cells: Pick ): Pick[] { const religionsNumber = getInputNumber("religionsInput"); if (religionsNumber === 0) return []; @@ -32,7 +31,7 @@ export function generateOrganizedReligions( return religionCells.map((cellId, index) => { const type = getType(index); const form = rw(forms[type]); - const cultureId = cultureIds[cellId]; + const cultureId = cells.culture[cellId]; return {type, form, culture: cultureId, center: cellId}; }); diff --git a/src/scripts/generation/pack/religions/generateReligionName.ts b/src/scripts/generation/pack/religions/generateReligionName.ts index 330277ec..967bd820 100644 --- a/src/scripts/generation/pack/religions/generateReligionName.ts +++ b/src/scripts/generation/pack/religions/generateReligionName.ts @@ -109,14 +109,12 @@ export function generateReligionName( data: IContext ): {name: string; expansion: IReligion["expansion"]} { context.current = data; - const {stateId, cultureId} = data; - const method = getMethod(type); const name = method.getName() || fallbackMethod.getName(); let expansion = method.expansion as IReligion["expansion"]; - if (expansion === "state" && !stateId) expansion = "global"; - if (expansion === "culture" && !cultureId) expansion = "global"; + if (expansion === "state" && !data.stateId) expansion = "global"; + else if (expansion === "culture" && !data.cultureId) expansion = "global"; return {name, expansion}; } diff --git a/src/scripts/generation/pack/religions/generateReligions.ts b/src/scripts/generation/pack/religions/generateReligions.ts index 91a6eb75..fb073b42 100644 --- a/src/scripts/generation/pack/religions/generateReligions.ts +++ b/src/scripts/generation/pack/religions/generateReligions.ts @@ -4,37 +4,32 @@ import {generateFolkReligions} from "./generateFolkReligions"; import {generateOrganizedReligions} from "./generateOrganizedReligions"; import {specifyReligions} from "./specifyReligions"; -type TCellsData = Pick; +type TCellsData = Pick< + IPack["cells"], + "i" | "c" | "p" | "g" | "h" | "t" | "biome" | "pop" | "culture" | "burg" | "state" +>; export function generateReligions({ states, cultures, burgs, - cultureIds, - stateIds, - burgIds, cells }: { states: TStates; cultures: TCultures; burgs: TBurgs; - cultureIds: Uint16Array; - stateIds: Uint16Array; - burgIds: Uint16Array; cells: TCellsData; }) { TIME && console.time("generateReligions"); 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( [...folkReligions, ...basicReligions], - stateIds, - burgIds, cultures, states, burgs, - cells.p + pick(cells, "i", "c", "culture", "burg", "state") ); console.log(religions); diff --git a/src/scripts/generation/pack/religions/specifyReligions.ts b/src/scripts/generation/pack/religions/specifyReligions.ts index 786c7081..b3c10ea8 100644 --- a/src/scripts/generation/pack/religions/specifyReligions.ts +++ b/src/scripts/generation/pack/religions/specifyReligions.ts @@ -1,6 +1,4 @@ -import {unique} from "utils/arrayUtils"; import {getMixedColor, getRandomColor} from "utils/colorUtils"; -import {findAll} from "utils/graphUtils"; import {each, gauss, rand} from "utils/probabilityUtils"; import {isCulture} from "utils/typeUtils"; import {expandReligions} from "./expandReligions"; @@ -16,19 +14,17 @@ const expansionismMap = { export function specifyReligions( religionsData: Pick[], - stateIds: Uint16Array, - burgIds: Uint16Array, cultures: TCultures, states: TStates, burgs: TBurgs, - points: TPoints -) { - const religions = religionsData.map(({type, form, culture: cultureId, center}, index) => { + cells: Pick +): {religions: TReligions; religionIds: Uint16Array} { + const rawReligions = religionsData.map(({type, form, culture: cultureId, center}, index) => { const supreme = getDeityName(cultures, cultureId); const deity = form === "Non-theism" || form === "Animism" ? null : supreme; - const stateId = stateIds[center]; - const burgId = burgIds[center]; + const stateId = cells.state[center]; + const burgId = cells.burg[center]; const {name, expansion} = generateReligionName(type, { cultureId, @@ -46,19 +42,26 @@ export function specifyReligions( const culture = cultures[cultureId]; 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, origins}; + return {i: index + 1, name, type, form, culture: cultureId, center, deity, expansion, expansionism, color}; }); - const religionIds = expandReligions(religions); + const religionIds = expandReligions(rawReligions, cells); + const names = renameOldReligions(rawReligions); + const origins = defineOrigins(religionIds, rawReligions, cells.c); - const names = renameOldReligions(religions); - const origins = defineOrigins(religionIds, religions, points); + return {religions: combineReligionsData(), religionIds}; - return { - religions: religions.map((religion, index) => ({name: names[index], religion, origins: origins[index]})), - religionIds - }; + function combineReligionsData(): TReligions { + const noReligion: TNoReligion = {i: 0, name: "No religion"}; + + 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 @@ -74,38 +77,58 @@ function renameOldReligions(religions: Pick[], - points: TPoints + neighbors: number[][] ) { return religions.map(religion => { if (religion.type === "Folk") return [0]; - const {type, culture: cultureId, expansion, center} = religion; - const [x, y] = points[center]; + const {i, type, culture: cultureId, expansion, center} = religion; const folkReligion = religions.find(({culture, type}) => type === "Folk" || culture === cultureId); const isFolkBased = folkReligion && cultureId && expansion === "culture" && each(2)(center); if (isFolkBased) return [folkReligion.i]; - if (type === "Organized") { - const origins = getReligionsInRadius(religionIds, {x, y, r: 150 / religions.length, max: 2}); - return origins; - } - - const origins = getReligionsInRadius(religionIds, {x, y, r: 300 / religions.length, max: rand(0, 4)}); + const {clusterSize, maxReligions} = religionOriginsParamsMap[type]; + const origins = getReligionsInRadius(neighbors, center, religionIds, i, clusterSize, maxReligions); return origins; }); } function getReligionsInRadius( + neighbors: number[][], + center: number, 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 cellsInRadius: number[] = []; // findAll(x, y, r); - const religions = unique(cellsInRadius.map(i => religionIds[i]).filter(r => r)); - return religions.length ? religions.slice(0, max) : [0]; + const religions = new Set(); + const queue = [center]; + const checked = <{[key: number]: true}>{}; + + 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]; } diff --git a/src/types/pack/religions.d.ts b/src/types/pack/religions.d.ts index eb4c5bb1..d99b652c 100644 --- a/src/types/pack/religions.d.ts +++ b/src/types/pack/religions.d.ts @@ -15,7 +15,7 @@ interface IReligion { type TNoReligion = { i: 0; - name: string; + name: "No religion"; }; type TReligions = [TNoReligion, ...IReligion[]]; diff --git a/src/utils/typeUtils.ts b/src/utils/typeUtils.ts index ef1ebf1b..94ce34aa 100644 --- a/src/utils/typeUtils.ts +++ b/src/utils/typeUtils.ts @@ -1,6 +1,11 @@ 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 => 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 isReligion = (religion: TNoReligion | IReligion): religion is IReligion => + religion.i !== 0 && !(religion as IReligion).removed;