refactor - define stateForms

This commit is contained in:
Azgaar 2022-09-03 19:52:22 +03:00
parent 2be3c68290
commit cffb0a8ec3
10 changed files with 262 additions and 108 deletions

View file

@ -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}, ` : "";

View file

@ -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"
];

View file

@ -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");

View file

@ -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);
}

View file

@ -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}`;
}

View file

@ -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};

View file

@ -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];
}