refactor: generate historical conflicts

This commit is contained in:
Azgaar 2022-09-05 21:00:09 +03:00
parent a2192fb984
commit 9b3a3f2e48
11 changed files with 81 additions and 28 deletions

View file

@ -4,13 +4,15 @@
let grid = {}; // initial graph based on jittered square grid and data let grid = {}; // initial graph based on jittered square grid and data
let pack = {}; // packed graph and data let pack = {}; // packed graph and data
let notes = [];
let events = {};
let seed; let seed;
let mapId; let mapId;
let mapHistory = []; let mapHistory = [];
let elSelected; let elSelected;
let notes = [];
let customization = 0; let customization = 0;
let rulers; let rulers;

View file

@ -559,6 +559,7 @@ window.Markers = (function () {
const {cells, states} = pack; const {cells, states} = pack;
const state = states[cells.state[cell]]; const state = states[cells.state[cell]];
// TODO: use events
if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state); if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state);
const campaign = ra(state.campaigns); const campaign = ra(state.campaigns);
const date = generateDate(campaign.start, campaign.end); const date = generateDate(campaign.start, campaign.end);

View file

@ -149,11 +149,8 @@ window.Names = (function () {
// generate short name for base // generate short name for base
const getBaseShort = function (base) { const getBaseShort = function (base) {
if (nameBases[base] === undefined) { if (nameBases[base] === undefined) {
tip( const message = `Namebase ${base} does not exist. Please upload custom namebases of change the base in Cultures Editor`;
`Namebase ${base} does not exist. Please upload custom namebases of change the base in Cultures Editor`, tip(message, false, "error");
false,
"error"
);
base = 1; base = 1;
} }
const min = nameBases[base].min - 1; const min = nameBases[base].min - 1;

View file

@ -53,13 +53,14 @@ async function generate(options?: IGenerationOptions) {
window.mapCoordinates = calculateMapCoordinates(); window.mapCoordinates = calculateMapCoordinates();
const newGrid = await createGrid(grid, precreatedGraph); const newGrid = await createGrid(grid, precreatedGraph);
const newPack = createPack(newGrid); const {pack: newPack, conflicts} = createPack(newGrid);
// TODO: draw default ruler // TODO: draw default ruler
// redefine global grid and pack // redefine global grid and pack
grid = newGrid; grid = newGrid;
pack = newPack; pack = newPack;
events = {conflicts};
// temp rendering for debug // temp rendering for debug
// renderLayer("cells"); // renderLayer("cells");

View file

@ -113,3 +113,15 @@ export const relations = {
farStates: {Friendly: 1, Neutral: 12, Suspicion: 2}, farStates: {Friendly: 1, Neutral: 12, Suspicion: 2},
navalToNaval: {Neutral: 2, Suspicion: 2, Rival: 1} navalToNaval: {Neutral: 2, Suspicion: 2, Rival: 1}
}; };
export const conflictTypes = {
War: 6,
Conflict: 2,
Campaign: 4,
Invasion: 2,
Rebellion: 2,
Conquest: 2,
Intervention: 1,
Expedition: 1,
Crusade: 1
};

View file

@ -21,7 +21,7 @@ export function generateBurgsAndStates(
rivers: Omit<IRiver, "name" | "basin" | "type">[], rivers: Omit<IRiver, "name" | "basin" | "type">[],
vertices: IGraphVertices, vertices: IGraphVertices,
cells: TCellsData cells: TCellsData
): {burgIds: Uint16Array; stateIds: Uint16Array; burgs: TBurgs; states: TStates} { ): {burgIds: Uint16Array; stateIds: Uint16Array; burgs: TBurgs; states: TStates; conflicts: IConflict[]} {
const cellsNumber = cells.i.length; const cellsNumber = cells.i.length;
const scoredCellIds = getScoredCellIds(); const scoredCellIds = getScoredCellIds();
@ -31,7 +31,8 @@ export function generateBurgsAndStates(
burgIds: new Uint16Array(cellsNumber), burgIds: new Uint16Array(cellsNumber),
stateIds: new Uint16Array(cellsNumber), stateIds: new Uint16Array(cellsNumber),
burgs: [NO_BURG], burgs: [NO_BURG],
states: [NEUTRALS] states: [NEUTRALS],
conflicts: []
}; };
} }
@ -69,9 +70,9 @@ export function generateBurgsAndStates(
const statistics = collectStatistics({...cells, state: stateIds, burg: burgIds}, burgs); const statistics = collectStatistics({...cells, state: stateIds, burg: burgIds}, burgs);
const diplomacy = generateRelations(statesData, statistics, pick(cells, "f")); const diplomacy = generateRelations(statesData, statistics, pick(cells, "f"));
const states = specifyStates(statesData, statistics, diplomacy, cultures, burgs); const {states, conflicts} = specifyStates(statesData, statistics, diplomacy, cultures, burgs);
return {burgIds, stateIds, burgs, states}; return {burgIds, stateIds, burgs, states, conflicts};
function getScoredCellIds() { function getScoredCellIds() {
const score = new Int16Array(cells.s.map(s => s * Math.random())); const score = new Int16Array(cells.s.map(s => s * Math.random()));

View file

@ -1,12 +1,22 @@
import * as d3 from "d3"; import * as d3 from "d3";
import {TIME} from "config/logging"; import {TIME} from "config/logging";
import {list, trimVowels} from "utils/languageUtils"; import {getAdjective, list, trimVowels} from "utils/languageUtils";
import {gauss, ra} from "utils/probabilityUtils"; import {gauss, P, ra, rw} from "utils/probabilityUtils";
import {conflictTypes} from "./config";
export function generateConflicts(states: IState[]) { const {Names} = window;
export function generateConflicts(states: IState[], cultures: TCultures): IConflict[] {
TIME && console.time("generateConflicts"); TIME && console.time("generateConflicts");
const historicalWars = generateHistoricalConflicts(states, cultures);
const ongoingWars = generateOngoingConflicts(states);
TIME && console.timeEnd("generateConflicts");
return [...historicalWars, ...ongoingWars].sort((a, b) => a.start - b.start);
}
function generateOngoingConflicts(states: IState[]): IConflict[] {
const statesMap = new Map(states.map(state => [state.i, state])); const statesMap = new Map(states.map(state => [state.i, state]));
const wars: IConflict[] = []; const wars: IConflict[] = [];
@ -37,7 +47,6 @@ export function generateConflicts(states: IState[]) {
wars.push(war); wars.push(war);
} }
TIME && console.timeEnd("generateConflicts");
return wars; return wars;
function simulateWar(attacker: IState, defender: IState): IConflict { function simulateWar(attacker: IState, defender: IState): IConflict {
@ -134,7 +143,7 @@ export function generateConflicts(states: IState[]) {
const name = `${attacker.name}-${trimVowels(defender.name)}ian War`; const name = `${attacker.name}-${trimVowels(defender.name)}ian War`;
const parties = {attackers: attackers.map(({i}) => i), defenders: defenders.map(({i}) => i)}; const parties = {attackers: attackers.map(({i}) => i), defenders: defenders.map(({i}) => i)};
const start = options.year - gauss(2, 2, 0, 5); const start = options.year - gauss(2, 2, 0, 5);
return {type: "conflict", name, start, parties, description: history.join(". ")}; return {name, start, parties, description: history.join(". ")};
} }
function getStatePower(state: IState) { function getStatePower(state: IState) {
@ -156,3 +165,31 @@ export function generateConflicts(states: IState[]) {
return "minor"; return "minor";
} }
} }
function generateHistoricalConflicts(states: IState[], cultures: TCultures): IConflict[] {
const statesMap = new Map(states.map(state => [state.i, state]));
const isConflict = (conflict: IConflict | null): conflict is IConflict => conflict !== null;
const getNameBase = (cultureId: number) => cultures[cultureId].base;
return states.map(generateConflicts).flat();
function generateConflicts(state: IState): IConflict[] {
const conflicts = state.neighbors
.map((neighbor, index) => {
if (index && P(0.8)) return null;
const enemy = statesMap.get(neighbor);
if (!enemy) return null;
const properName = P(0.8) ? enemy.name : Names.getBaseShort(getNameBase(enemy.culture));
const name = getAdjective(properName) + " " + rw(conflictTypes);
const start = gauss(options.year - 100, 150, 1, options.year - 6);
const end = start + gauss(4, 5, 1, options.year - start - 1);
const parties = {attackers: [state.i], defenders: [enemy.i]};
const conflict: IConflict = {name, start, end, parties};
return conflict;
})
.filter(isConflict);
return conflicts;
}
}

View file

@ -16,7 +16,7 @@ export function specifyStates(
diplomacy: TDiplomacy, diplomacy: TDiplomacy,
cultures: TCultures, cultures: TCultures,
burgs: TBurgs burgs: TBurgs
): TStates { ): {states: TStates; conflicts: IConflict[]} {
TIME && console.time("specifyStates"); TIME && console.time("specifyStates");
const colors = defineStateColors(statistics); const colors = defineStateColors(statistics);
@ -56,10 +56,8 @@ export function specifyStates(
}; };
}); });
const wars = generateConflicts(states); // mutates states const conflicts = generateConflicts(states, cultures); // mutates states
console.log(wars);
console.log(states);
TIME && console.timeEnd("specifyStates"); TIME && console.timeEnd("specifyStates");
return [NEUTRALS, ...states]; return {states: [NEUTRALS, ...states], conflicts};
} }

View file

@ -18,7 +18,7 @@ import {generateReligions} from "./religions/generateReligions";
const {LAND_COAST, WATER_COAST, DEEPER_WATER} = DISTANCE_FIELD; const {LAND_COAST, WATER_COAST, DEEPER_WATER} = DISTANCE_FIELD;
const {Biomes} = window; const {Biomes} = window;
export function createPack(grid: IGrid): IPack { export function createPack(grid: IGrid): {pack: IPack; conflicts: IConflict[]} {
const {temp, prec} = grid.cells; const {temp, prec} = grid.cells;
const {vertices, cells} = repackGrid(grid); const {vertices, cells} = repackGrid(grid);
@ -97,7 +97,7 @@ export function createPack(grid: IGrid): IPack {
pop: population pop: population
}); });
const {burgIds, stateIds, burgs, states} = generateBurgsAndStates( const {burgIds, stateIds, burgs, states, conflicts} = generateBurgsAndStates(
cultures, cultures,
mergedFeatures, mergedFeatures,
temp, temp,
@ -199,7 +199,7 @@ export function createPack(grid: IGrid): IPack {
religions religions
}; };
return pack; return {pack, conflicts};
} }
// repack grid cells: discart deep water cells, add land cells along the coast // repack grid cells: discart deep water cells, add land cells along the coast

View file

@ -1,13 +1,15 @@
interface IEvents {
conflicts: IConflict[];
}
interface IEvent { interface IEvent {
type: string;
name: string; name: string;
start: number; start: number;
end?: number; // undefined for ongoing events end?: number; // undefined for ongoing events
description: string; description?: string;
} }
interface IConflict extends IEvent { interface IConflict extends IEvent {
type: "conflict";
parties: { parties: {
attackers: number[]; attackers: number[];
defenders: number[]; defenders: number[];

View file

@ -1,11 +1,13 @@
declare let grid: IGrid; declare let grid: IGrid;
declare let pack: IPack; declare let pack: IPack;
declare let notes: INote[];
declare let events: IEvents;
declare let seed: string; declare let seed: string;
declare let mapId: number; declare let mapId: number;
declare let mapHistory: IMapHistoryEntry[]; declare let mapHistory: IMapHistoryEntry[];
declare let notes: INote[];
declare let customization: number; declare let customization: number;
declare let rulers: Rulers; declare let rulers: Rulers;