mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
refactor - define stateForms
This commit is contained in:
parent
2be3c68290
commit
cffb0a8ec3
10 changed files with 262 additions and 108 deletions
|
|
@ -197,7 +197,7 @@ const onHoverEventsMap: OnHoverEventMap = {
|
|||
statesLayer: ({packCellId}) => {
|
||||
const stateId = pack.cells.state[packCellId];
|
||||
const state = pack.states[stateId];
|
||||
const stateName = isState(state) ? state.fullName || state.name : state.name;
|
||||
const stateName = isState(state) ? state.fullName : state.name;
|
||||
|
||||
const provinceId = pack.cells.province[packCellId];
|
||||
const provinceName = provinceId ? `${pack.provinces[provinceId].fullName}, ` : "";
|
||||
|
|
|
|||
|
|
@ -1,2 +1,108 @@
|
|||
import {NAMEBASE as NB} from "config/namebases";
|
||||
|
||||
export const NO_BURG: TNoBurg = {i: 0, name: undefined};
|
||||
export const NEUTRALS: TNeutrals = {i: 0, name: "Neutrals"};
|
||||
|
||||
export enum AreaTiers {
|
||||
DUCHY = 0,
|
||||
GRAND_DUCHY = 1,
|
||||
PRINCIPALITY = 2,
|
||||
KINGDOM = 3,
|
||||
EMPIRE = 4
|
||||
}
|
||||
|
||||
export const StateForms = {
|
||||
monarchy: ["Duchy", "Grand Duchy", "Principality", "Kingdom", "Empire"] as const, // per area tier
|
||||
republic: {
|
||||
Republic: 75,
|
||||
Federation: 4,
|
||||
"Trade Company": 4,
|
||||
"Most Serene Republic": 2,
|
||||
Oligarchy: 2,
|
||||
Tetrarchy: 1,
|
||||
Triumvirate: 1,
|
||||
Diarchy: 1,
|
||||
Junta: 1
|
||||
},
|
||||
union: {
|
||||
Union: 3,
|
||||
League: 4,
|
||||
Confederation: 1,
|
||||
"United Kingdom": 1,
|
||||
"United Republic": 1,
|
||||
"United Provinces": 2,
|
||||
Commonwealth: 1,
|
||||
Heptarchy: 1
|
||||
},
|
||||
theocracy: {Theocracy: 20, Brotherhood: 1, Thearchy: 2, See: 1, "Holy State": 1},
|
||||
anarchy: {"Free Territory": 2, Council: 3, Commune: 1, Community: 1}
|
||||
};
|
||||
|
||||
type TMonarchyForms = typeof StateForms.monarchy[number];
|
||||
|
||||
// prettier-ignore
|
||||
export const culturalMonarchyFormsMap: {[key: number]: {[key in TMonarchyForms]?: string}} = {
|
||||
[NB.Ruthenian]: {Kingdom: "Tsardom", Empire: "Tsardom"},
|
||||
[NB.Greek]: {Duchy: "Despotate", "Grand Duchy": "Despotate"},
|
||||
[NB.Japanese]: {"Grand Duchy": "Shogunate", Kingdom: "Shogunate"},
|
||||
[NB.Turkish]: {Duchy: "Beylik", "Grand Duchy": "Horde", Principality: "Great Horde", Kingdom: "Khanate", Empire: "Sultanate"},
|
||||
[NB.Berber]: {Duchy: "Sheikhdom", "Grand Duchy": "Emirate", Principality: "Emirate", Empire: "Sultanate"},
|
||||
[NB.Arabic]: {Duchy: "Sheikhdom", "Grand Duchy": "Emirate", Principality: "Emirate", Empire: "Sultanate"},
|
||||
[NB.Iranian]: {Duchy: "Satrapy", "Grand Duchy": "Satrapy", Kingdom: "Shahdom"},
|
||||
[NB.Mongolian]: {Duchy: "Horde", "Grand Duchy": "Horde", Principality: "Ulus", Kingdom: "Khanate", Empire: "Khaganate"}
|
||||
};
|
||||
|
||||
const Catholic = {Duchy: "Diocese", "Grand Duchy": "Аrchdiocese"};
|
||||
const Orthodox = {
|
||||
Duchy: "Eparchy",
|
||||
"Grand Duchy": "Eparchy",
|
||||
Principality: "Exarchate",
|
||||
Kingdom: "Metropolia",
|
||||
Empire: "Patriarchate"
|
||||
};
|
||||
const Islamic = {
|
||||
Duchy: "Imamah",
|
||||
"Grand Duchy": "Imamah",
|
||||
Principality: "Imamah",
|
||||
Kingdom: "Caliphate",
|
||||
Empire: "Caliphate"
|
||||
};
|
||||
|
||||
export const culturalTheocracyFormsMap: {[key: number]: {[key in TMonarchyForms]?: string}} = {
|
||||
[NB.German]: Catholic,
|
||||
[NB.English]: Catholic,
|
||||
[NB.French]: Catholic,
|
||||
[NB.Italian]: Catholic,
|
||||
[NB.Castillian]: Catholic,
|
||||
[NB.Roman]: Catholic,
|
||||
[NB.Portuguese]: Catholic,
|
||||
[NB.Ruthenian]: Orthodox,
|
||||
[NB.Ruthenian]: Orthodox,
|
||||
[NB.Turkish]: Islamic,
|
||||
[NB.Nigerian]: Islamic,
|
||||
[NB.Berber]: Islamic,
|
||||
[NB.Arabic]: Islamic,
|
||||
[NB.Iranian]: Islamic,
|
||||
[NB.Swahili]: Islamic
|
||||
};
|
||||
|
||||
// state forms requiring Adjective + Name, all other forms use scheme Form + Of + Name
|
||||
export const adjectivalForms = [
|
||||
"Empire",
|
||||
"Sultanate",
|
||||
"Khaganate",
|
||||
"Shogunate",
|
||||
"Caliphate",
|
||||
"Despotate",
|
||||
"Theocracy",
|
||||
"Oligarchy",
|
||||
"Union",
|
||||
"Confederation",
|
||||
"Trade Company",
|
||||
"League",
|
||||
"Tetrarchy",
|
||||
"Triumvirate",
|
||||
"Diarchy",
|
||||
"Horde",
|
||||
"Marches"
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,14 +2,10 @@ import {TIME} from "config/logging";
|
|||
import {getInputNumber} from "utils/nodeUtils";
|
||||
import {rn} from "utils/numberUtils";
|
||||
import type {createCapitals} from "./createCapitals";
|
||||
import {defineStateName} from "./defineStateName";
|
||||
import {generateStateEmblem} from "./generateStateEmblem";
|
||||
|
||||
type TCapitals = ReturnType<typeof createCapitals>;
|
||||
export type TStateData = Pick<
|
||||
IState,
|
||||
"i" | "name" | "type" | "culture" | "center" | "expansionism" | "capital" | "coa"
|
||||
>;
|
||||
export type TStateData = Pick<IState, "i" | "type" | "culture" | "center" | "expansionism" | "capital" | "coa">;
|
||||
|
||||
export function createStateData(capitals: TCapitals, cultures: TCultures) {
|
||||
TIME && console.time("createStates");
|
||||
|
|
@ -17,16 +13,15 @@ export function createStateData(capitals: TCapitals, cultures: TCultures) {
|
|||
const powerInput = getInputNumber("powerInput");
|
||||
|
||||
const statesData: TStateData[] = capitals.map((capital, index) => {
|
||||
const {cell: cellId, culture: cultureId, name: capitalName} = capital;
|
||||
const {cell: cellId, culture: cultureId} = capital;
|
||||
const id = index + 1;
|
||||
const name = defineStateName(cellId, capitalName, cultureId, cultures);
|
||||
|
||||
if (cultureId === 0) throw new Error("Culture id cannot be 0");
|
||||
const {type, shield} = cultures[cultureId] as ICulture;
|
||||
const expansionism = rn(Math.random() * powerInput + 1, 1);
|
||||
|
||||
const coa = generateStateEmblem(type, shield);
|
||||
|
||||
return {i: id, name, type, center: cellId, expansionism, capital: id, culture: cultureId, coa};
|
||||
return {i: id, type, center: cellId, expansionism, capital: id, culture: cultureId, coa};
|
||||
});
|
||||
|
||||
TIME && console.timeEnd("createStates");
|
||||
|
|
|
|||
|
|
@ -1,48 +1,12 @@
|
|||
import {NAMEBASE} from "config/namebases";
|
||||
import * as d3 from "d3";
|
||||
|
||||
import {getInputNumber} from "utils/nodeUtils";
|
||||
import {P, rand, rw} from "utils/probabilityUtils";
|
||||
import type {TStateStatistics} from "./collectStatistics";
|
||||
import {AreaTiers, culturalMonarchyFormsMap, culturalTheocracyFormsMap, StateForms} from "./config";
|
||||
|
||||
const generic = {Monarchy: 25, Republic: 2, Union: 1};
|
||||
const naval = {Monarchy: 25, Republic: 8, Union: 3};
|
||||
|
||||
const republic = {
|
||||
Republic: 75,
|
||||
Federation: 4,
|
||||
"Trade Company": 4,
|
||||
"Most Serene Republic": 2,
|
||||
Oligarchy: 2,
|
||||
Tetrarchy: 1,
|
||||
Triumvirate: 1,
|
||||
Diarchy: 1,
|
||||
Junta: 1
|
||||
};
|
||||
|
||||
const union = {
|
||||
Union: 3,
|
||||
League: 4,
|
||||
Confederation: 1,
|
||||
"United Kingdom": 1,
|
||||
"United Republic": 1,
|
||||
"United Provinces": 2,
|
||||
Commonwealth: 1,
|
||||
Heptarchy: 1
|
||||
};
|
||||
|
||||
const theocracy = {Theocracy: 20, Brotherhood: 1, Thearchy: 2, See: 1, "Holy State": 1};
|
||||
|
||||
const anarchy = {"Free Territory": 2, Council: 3, Commune: 1, Community: 1};
|
||||
|
||||
const monarchy = ["Duchy", "Grand Duchy", "Principality", "Kingdom", "Empire"]; // per area tier
|
||||
|
||||
enum AreaTiers {
|
||||
DUCHY = 0,
|
||||
GRAND_DUCHY = 1,
|
||||
PRINCIPALITY = 2,
|
||||
KINGDOM = 3,
|
||||
EMPIRE = 4
|
||||
}
|
||||
|
||||
// create 5 area tiers, where 4 are the biggest, 0 the smallest
|
||||
// create 5 area tiers, 4 is the biggest, 0 the smallest
|
||||
export function createAreaTiers(statistics: TStateStatistics) {
|
||||
const stateAreas = Object.entries(statistics)
|
||||
.filter(([id]) => Number(id))
|
||||
|
|
@ -59,6 +23,80 @@ export function createAreaTiers(statistics: TStateStatistics) {
|
|||
};
|
||||
}
|
||||
|
||||
export function defineStateForm(type: TCultureType, areaTier: AreaTiers) {
|
||||
return {form: "testForm", formName: "testFormName"};
|
||||
export function defineStateForm(type: TCultureType, areaTier: AreaTiers, nameBase: number, burgsNumber: number) {
|
||||
const form = defineForm(type, areaTier);
|
||||
const formName = defineFormName(form, nameBase, areaTier, burgsNumber);
|
||||
|
||||
return {form, formName};
|
||||
}
|
||||
|
||||
const generic = {Monarchy: 25, Republic: 2, Union: 1};
|
||||
const naval = {Monarchy: 6, Republic: 2, Union: 1};
|
||||
|
||||
function defineForm(type: TCultureType, areaTier: AreaTiers) {
|
||||
const isAnarchy = P((1 - areaTier / 5) / 100); // [1% - 0.2%] chance
|
||||
if (isAnarchy) return "Anarchy";
|
||||
|
||||
// TODO: define Theocracies based on actual religion spread
|
||||
const religionsNumberModifier = Math.min(getInputNumber("religionsInput") / 10, 2.5); // [0-2.5]
|
||||
const isTheocracy = P(0.2 * religionsNumberModifier); // [0% - 50%] chance, av. 12%
|
||||
if (isTheocracy) return "Theocracy";
|
||||
|
||||
if (type === "Naval") return rw(naval);
|
||||
|
||||
return rw(generic);
|
||||
}
|
||||
|
||||
function defineFormName(
|
||||
form: ReturnType<typeof defineForm>,
|
||||
nameBase: number,
|
||||
areaTier: AreaTiers,
|
||||
burgsNumber: number
|
||||
) {
|
||||
if (form === "Monarchy") return defineMonarchyForm(nameBase, areaTier);
|
||||
if (form === "Republic") return defineRepublicForm(areaTier, burgsNumber);
|
||||
if (form === "Union") return rw(StateForms.union);
|
||||
if (form === "Theocracy") return defineTheocracyForm(nameBase, areaTier);
|
||||
if (form === "Anarchy") return rw(StateForms.anarchy);
|
||||
|
||||
return "test";
|
||||
}
|
||||
|
||||
// Default name depends on area tier, some name bases have special names for tiers
|
||||
function defineMonarchyForm(nameBase: number, areaTier: AreaTiers, diplomacy = [""], neighbors = []) {
|
||||
const form = StateForms.monarchy[areaTier];
|
||||
|
||||
// TODO: specific names for vassals
|
||||
const isVassal = diplomacy.includes("Vassal");
|
||||
if (isVassal) {
|
||||
if (areaTier === AreaTiers.DUCHY && neighbors.length > 1 && rand(6) < neighbors.length) return "Marches";
|
||||
if (nameBase === NAMEBASE.English && P(0.3)) return "Dominion";
|
||||
if (P(0.3)) return "Protectorate";
|
||||
}
|
||||
|
||||
if (culturalMonarchyFormsMap[nameBase]) {
|
||||
const culturalForm = culturalMonarchyFormsMap[nameBase][form];
|
||||
if (culturalForm) return culturalForm;
|
||||
}
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
// Default name is from weighted array, special case for small states with only 1 burg
|
||||
function defineRepublicForm(areaTier: AreaTiers, burgsNumber: number) {
|
||||
if (areaTier < AreaTiers.PRINCIPALITY && burgsNumber === 1) return P(0.7) ? "Free City" : "City-state";
|
||||
return rw(StateForms.republic);
|
||||
}
|
||||
|
||||
function defineTheocracyForm(nameBase: number, areaTier: AreaTiers) {
|
||||
const form = StateForms.monarchy[areaTier];
|
||||
|
||||
if (P(0.05)) return "Divine " + form; // 5%
|
||||
|
||||
if (culturalTheocracyFormsMap[nameBase]) {
|
||||
const culturalForm = culturalTheocracyFormsMap[nameBase][form];
|
||||
if (culturalForm) return culturalForm;
|
||||
}
|
||||
|
||||
return rw(StateForms.theocracy);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
import {getAdjective} from "utils/languageUtils";
|
||||
import {each} from "utils/probabilityUtils";
|
||||
import {adjectivalForms} from "./config";
|
||||
|
||||
const {Names} = window;
|
||||
|
||||
export function defineStateName(cellId: number, capitalName: string, cultureId: number, cultures: TCultures): string {
|
||||
const useCapitalName = capitalName.length < 9 && each(5)(cellId);
|
||||
const nameBase = cultures[cultureId].base;
|
||||
export function defineStateName(center: number, capitalName: string, nameBase: number, formName: string): string {
|
||||
if (["Free City", "City-state"].includes(formName)) return capitalName;
|
||||
|
||||
const useCapitalName = capitalName.length < 9 && each(5)(center);
|
||||
const basename: string = useCapitalName ? capitalName : Names.getBaseShort(nameBase);
|
||||
|
||||
return Names.getState(basename, basename);
|
||||
}
|
||||
|
||||
export function defineFullStateName(name: string, form: string) {
|
||||
return `${name} ${form}`;
|
||||
export function defineFullStateName(name: string, formName: string) {
|
||||
if (!formName) return name;
|
||||
if (!name && formName) return "The " + formName;
|
||||
|
||||
const isAdjectival = adjectivalForms.includes(formName) && !/-| /.test(name);
|
||||
return isAdjectival ? `${getAdjective(name)} ${formName}` : `${formName} of ${name}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,33 +10,35 @@ import {expandStates} from "./expandStates";
|
|||
import {specifyBurgs} from "./specifyBurgs";
|
||||
import {specifyStates} from "./specifyStates";
|
||||
|
||||
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: Pick<
|
||||
IPack["cells"],
|
||||
| "v"
|
||||
| "c"
|
||||
| "p"
|
||||
| "b"
|
||||
| "i"
|
||||
| "g"
|
||||
| "area"
|
||||
| "h"
|
||||
| "f"
|
||||
| "t"
|
||||
| "haven"
|
||||
| "harbor"
|
||||
| "r"
|
||||
| "fl"
|
||||
| "biome"
|
||||
| "s"
|
||||
| "pop"
|
||||
| "culture"
|
||||
>
|
||||
cells: TCellsData
|
||||
): {burgIds: Uint16Array; stateIds: Uint16Array; burgs: TBurgs; states: TStates} {
|
||||
const cellsNumber = cells.i.length;
|
||||
|
||||
|
|
@ -84,7 +86,7 @@ export function generateBurgsAndStates(
|
|||
const burgIds = assignBurgIds(burgs);
|
||||
|
||||
const statistics = collectStatistics({...cells, state: stateIds, burg: burgIds}, burgs);
|
||||
const states = specifyStates(statesData, statistics, stateIds, burgIds);
|
||||
const states = specifyStates(statesData, statistics, cultures, burgs);
|
||||
|
||||
return {burgIds, stateIds, burgs, states};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import {TIME} from "config/logging";
|
|||
import {getColors} from "utils/colorUtils";
|
||||
import {NEUTRALS} from "./config";
|
||||
import {createAreaTiers, defineStateForm} from "./defineStateForm";
|
||||
import {defineFullStateName} from "./defineStateName";
|
||||
import {defineFullStateName, defineStateName} from "./defineStateName";
|
||||
import {isBurg} from "utils/typeUtils";
|
||||
|
||||
import type {TStateStatistics} from "./collectStatistics";
|
||||
import type {TStateData} from "./createStateData";
|
||||
|
|
@ -10,28 +11,35 @@ import type {TStateData} from "./createStateData";
|
|||
export function specifyStates(
|
||||
statesData: TStateData[],
|
||||
statistics: TStateStatistics,
|
||||
stateIds: Uint16Array,
|
||||
burgIds: Uint16Array
|
||||
cultures: TCultures,
|
||||
burgs: TBurgs
|
||||
): TStates {
|
||||
TIME && console.time("specifyState");
|
||||
TIME && console.time("specifyStates");
|
||||
|
||||
const colors = getColors(statesData.length);
|
||||
const getAreaTier = createAreaTiers(statistics);
|
||||
const getNameBase = (cultureId: number) => cultures[cultureId].base;
|
||||
|
||||
const states: IState[] = statesData.map((stateData, index) => {
|
||||
const {i, type, name} = stateData;
|
||||
const {area, ...stats} = statistics[i];
|
||||
const {i, center, type, culture, capital} = stateData;
|
||||
const {area, burgs: burgsNumber, ...stats} = statistics[i];
|
||||
|
||||
const capitalBurg = burgs[capital];
|
||||
const capitalName = isBurg(capitalBurg) ? capitalBurg.name : null;
|
||||
if (!capitalName) throw new Error("State capital is not a burg");
|
||||
|
||||
const nameBase = getNameBase(culture);
|
||||
const areaTier = getAreaTier(area);
|
||||
const {form, formName} = defineStateForm(type, areaTier);
|
||||
const fullName = defineFullStateName(name, form);
|
||||
const {form, formName} = defineStateForm(type, areaTier, nameBase, burgsNumber);
|
||||
const name = defineStateName(center, capitalName, nameBase, formName);
|
||||
const fullName = defineFullStateName(name, formName);
|
||||
|
||||
const color = colors[index];
|
||||
|
||||
const state: IState = {...stateData, form, formName, fullName, color, area, ...stats};
|
||||
const state: IState = {name, ...stateData, form, formName, fullName, color, area, burgs: burgsNumber, ...stats};
|
||||
return state;
|
||||
});
|
||||
|
||||
TIME && console.timeEnd("specifyState");
|
||||
TIME && console.timeEnd("specifyStates");
|
||||
return [NEUTRALS, ...states];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue