mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
refactor: generate relations
This commit is contained in:
parent
73431ec743
commit
136e71eae9
7 changed files with 85 additions and 66 deletions
|
|
@ -7,7 +7,7 @@ import {initLayers, renderLayer, restoreLayers} from "layers";
|
|||
// @ts-expect-error js module
|
||||
import {drawScaleBar, Rulers} from "modules/measurers";
|
||||
// @ts-expect-error js module
|
||||
import {unfog, downloadFile} from "modules/ui/editors";
|
||||
import {unfog} from "modules/ui/editors";
|
||||
// @ts-expect-error js module
|
||||
import {applyMapSize, randomizeOptions} from "modules/ui/options";
|
||||
// @ts-expect-error js module
|
||||
|
|
@ -76,8 +76,6 @@ async function generate(options?: IGenerationOptions) {
|
|||
// if (route === 2) drawPoint(pack.cells.p[index], {color: "black"});
|
||||
// });
|
||||
|
||||
downloadDiplomacyData();
|
||||
|
||||
WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`);
|
||||
// showStatistics();
|
||||
INFO && console.groupEnd();
|
||||
|
|
@ -86,19 +84,6 @@ async function generate(options?: IGenerationOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
function downloadDiplomacyData() {
|
||||
const states = pack.states.filter(s => s.i && !s.removed);
|
||||
const valid = states.map(s => s.i);
|
||||
|
||||
let data = "," + states.map(s => s.name).join(",") + "\n"; // headers
|
||||
states.forEach(s => {
|
||||
const rels = s.diplomacy.filter((v, i) => valid.includes(i));
|
||||
data += s.name + "," + rels.join(",") + "\n";
|
||||
});
|
||||
|
||||
// downloadFile(data, "relations.csv");
|
||||
}
|
||||
|
||||
function showGenerationError(error: Error) {
|
||||
clearMainTip();
|
||||
ERROR && console.error(error);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,16 @@ export function createAreaTiers(statistics: TStateStatistics) {
|
|||
};
|
||||
}
|
||||
|
||||
export function defineStateForm(type: TCultureType, areaTier: AreaTiers, nameBase: number, burgsNumber: number) {
|
||||
export function defineStateForm(
|
||||
type: TCultureType,
|
||||
areaTier: AreaTiers,
|
||||
nameBase: number,
|
||||
burgsNumber: number,
|
||||
relations: TRelation[],
|
||||
neighbors: number[]
|
||||
) {
|
||||
const form = defineForm(type, areaTier);
|
||||
const formName = defineFormName(form, nameBase, areaTier, burgsNumber);
|
||||
const formName = defineFormName(form, nameBase, areaTier, burgsNumber, relations, neighbors);
|
||||
|
||||
return {form, formName};
|
||||
}
|
||||
|
|
@ -51,9 +58,11 @@ function defineFormName(
|
|||
form: ReturnType<typeof defineForm>,
|
||||
nameBase: number,
|
||||
areaTier: AreaTiers,
|
||||
burgsNumber: number
|
||||
burgsNumber: number,
|
||||
relations: TRelation[],
|
||||
neighbors: number[]
|
||||
) {
|
||||
if (form === "Monarchy") return defineMonarchyForm(nameBase, areaTier);
|
||||
if (form === "Monarchy") return defineMonarchyForm(nameBase, areaTier, relations, neighbors);
|
||||
if (form === "Republic") return defineRepublicForm(areaTier, burgsNumber);
|
||||
if (form === "Union") return rw(StateForms.union);
|
||||
if (form === "Theocracy") return defineTheocracyForm(nameBase, areaTier);
|
||||
|
|
@ -63,11 +72,10 @@ function defineFormName(
|
|||
}
|
||||
|
||||
// Default name depends on area tier, some name bases have special names for tiers
|
||||
function defineMonarchyForm(nameBase: number, areaTier: AreaTiers, diplomacy = [""], neighbors = []) {
|
||||
function defineMonarchyForm(nameBase: number, areaTier: AreaTiers, relations: TRelation[], neighbors: number[]) {
|
||||
const form = StateForms.monarchy[areaTier];
|
||||
|
||||
// TODO: specific names for vassals
|
||||
const isVassal = diplomacy.includes("Vassal");
|
||||
const isVassal = relations.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";
|
||||
|
|
|
|||
|
|
@ -7,31 +7,12 @@ import {createCapitals} from "./createCapitals";
|
|||
import {createStateData} from "./createStateData";
|
||||
import {createTowns} from "./createTowns";
|
||||
import {expandStates} from "./expandStates";
|
||||
import {generateDiplomacy} from "./generateDiplomacy";
|
||||
import {generateRelations} from "./generateRelations";
|
||||
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"
|
||||
>;
|
||||
// prettier-ignore
|
||||
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,
|
||||
|
|
@ -87,7 +68,7 @@ export function generateBurgsAndStates(
|
|||
const burgIds = assignBurgIds(burgs);
|
||||
|
||||
const statistics = collectStatistics({...cells, state: stateIds, burg: burgIds}, burgs);
|
||||
const {chronicle, diplomacy} = generateDiplomacy(statesData, statistics, pick(cells, "f"));
|
||||
const diplomacy = generateRelations(statesData, statistics, pick(cells, "f"));
|
||||
const states = specifyStates(statesData, statistics, diplomacy, cultures, burgs);
|
||||
|
||||
return {burgIds, stateIds, burgs, states};
|
||||
|
|
|
|||
|
|
@ -1,34 +1,32 @@
|
|||
import * as d3 from "d3";
|
||||
|
||||
import {TIME} from "config/logging";
|
||||
import {TStateStatistics} from "./collectStatistics";
|
||||
import {TStateData} from "./createStateData";
|
||||
import {P, rw} from "utils/probabilityUtils";
|
||||
import {relations} from "./config";
|
||||
|
||||
export type TDiplomacy = {[key: number]: TRelations[]};
|
||||
import type {TStateStatistics} from "./collectStatistics";
|
||||
import type {TStateData} from "./createStateData";
|
||||
|
||||
export type TDiplomacy = {[key: number]: TRelation[]};
|
||||
|
||||
interface IDiplomacyData {
|
||||
i: number;
|
||||
type: TCultureType;
|
||||
center: number;
|
||||
expansionism: number;
|
||||
area: number;
|
||||
neighbors: number[];
|
||||
}
|
||||
|
||||
export function generateDiplomacy(
|
||||
export function generateRelations(
|
||||
statesData: TStateData[],
|
||||
statistics: TStateStatistics,
|
||||
cells: Pick<IPack["cells"], "f">
|
||||
) {
|
||||
TIME && console.time("generateDiplomacy");
|
||||
TIME && console.time("generateRelations");
|
||||
|
||||
const chronicle: string[] = [];
|
||||
const diplomacy = getBlankDiplomacyMatrix(statesData);
|
||||
|
||||
if (statesData.length < 2) {
|
||||
return {chronicle, diplomacy};
|
||||
}
|
||||
if (statesData.length < 2) return diplomacy;
|
||||
|
||||
const stateAreas = Object.values(statistics).map(({area}) => area);
|
||||
const averageStateArea = d3.mean(stateAreas)!;
|
||||
|
|
@ -81,8 +79,8 @@ export function generateDiplomacy(
|
|||
}
|
||||
}
|
||||
|
||||
TIME && console.timeEnd("generateDiplomacy");
|
||||
return {chronicle, diplomacy};
|
||||
TIME && console.timeEnd("generateRelations");
|
||||
return diplomacy;
|
||||
}
|
||||
|
||||
function getBlankDiplomacyMatrix(statesData: TStateData[]) {
|
||||
|
|
@ -95,9 +93,9 @@ function getBlankDiplomacyMatrix(statesData: TStateData[]) {
|
|||
}
|
||||
|
||||
function getDiplomacyData(stateData: TStateData, statistics: TStateStatistics): IDiplomacyData {
|
||||
const {i, type, center} = stateData;
|
||||
const {i, type, center, expansionism} = stateData;
|
||||
const {neighbors, area} = statistics[i];
|
||||
return {i, type, center, neighbors, area};
|
||||
return {i, type, center, expansionism, neighbors, area};
|
||||
}
|
||||
|
||||
function detectVassalState(from: IDiplomacyData, to: IDiplomacyData, averageStateArea: number) {
|
||||
35
src/scripts/generation/pack/burgsAndStates/simulateWars.ts
Normal file
35
src/scripts/generation/pack/burgsAndStates/simulateWars.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import {TIME} from "config/logging";
|
||||
|
||||
import type {TStateData} from "./createStateData";
|
||||
import type {TDiplomacy} from "./generateRelations";
|
||||
|
||||
export function simulateWars(statesData: TStateData[], diplomacy: TDiplomacy) {
|
||||
TIME && console.time("simulateWars");
|
||||
|
||||
// declare wars
|
||||
for (const {i} of statesData) {
|
||||
const relations = diplomacy[i];
|
||||
if (!relations.includes("Rival")) continue; // no rivals to attack
|
||||
if (relations.includes("Vassal")) continue; // not independent
|
||||
if (relations.includes("Enemy")) continue; // already at war
|
||||
|
||||
// select candidates to attack: rival independent states
|
||||
const candidates = relations
|
||||
.map((relation, index) => (relation === "Rival" && !diplomacy[index].includes("Vassal") ? index : 0))
|
||||
.filter(index => index);
|
||||
if (!candidates.length) continue;
|
||||
|
||||
const attacker = getDiplomacyData(statesData[i], statistics);
|
||||
const defender = getDiplomacyData(statesData[ra(candidates)], statistics);
|
||||
|
||||
const attackerPower = attacker.area * attacker.expansionism;
|
||||
const defenderPower = defender.area * defender.expansionism;
|
||||
if (attackerPower < defenderPower * gauss(1.6, 0.8, 0, 10, 2)) continue; // defender is too strong
|
||||
|
||||
const attackers = [attacker];
|
||||
const defenders = [defender];
|
||||
}
|
||||
|
||||
TIME && console.timeEnd("simulateWars");
|
||||
return null;
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import {isBurg} from "utils/typeUtils";
|
|||
|
||||
import type {TStateStatistics} from "./collectStatistics";
|
||||
import type {TStateData} from "./createStateData";
|
||||
import type {TDiplomacy} from "./generateDiplomacy";
|
||||
import type {TDiplomacy} from "./generateRelations";
|
||||
|
||||
export function specifyStates(
|
||||
statesData: TStateData[],
|
||||
|
|
@ -24,16 +24,17 @@ export function specifyStates(
|
|||
|
||||
const states: IState[] = statesData.map(stateData => {
|
||||
const {i, center, type, culture, capital} = stateData;
|
||||
const {area, burgs: burgsNumber, ...stats} = statistics[i];
|
||||
const {area, burgs: burgsNumber, neighbors, ...stats} = statistics[i];
|
||||
const color = colors[i];
|
||||
|
||||
const capitalBurg = burgs[capital];
|
||||
const capitalName = isBurg(capitalBurg) ? capitalBurg.name : null;
|
||||
if (!capitalName) throw new Error("State capital is not a burg");
|
||||
|
||||
const relations = diplomacy[i];
|
||||
const nameBase = getNameBase(culture);
|
||||
const areaTier = getAreaTier(area);
|
||||
const {form, formName} = defineStateForm(type, areaTier, nameBase, burgsNumber);
|
||||
const {form, formName} = defineStateForm(type, areaTier, nameBase, burgsNumber, relations, neighbors);
|
||||
const name = defineStateName(center, capitalName, nameBase, formName);
|
||||
const fullName = defineFullStateName(name, formName);
|
||||
|
||||
|
|
@ -47,7 +48,8 @@ export function specifyStates(
|
|||
area,
|
||||
burgs: burgsNumber,
|
||||
...stats,
|
||||
diplomacy: diplomacy[i]
|
||||
neighbors,
|
||||
relations
|
||||
};
|
||||
return state;
|
||||
});
|
||||
|
|
|
|||
14
src/types/pack/states.d.ts
vendored
14
src/types/pack/states.d.ts
vendored
|
|
@ -18,7 +18,7 @@ interface IState {
|
|||
rural: number;
|
||||
urban: number;
|
||||
neighbors: number[];
|
||||
diplomacy: TRelations[];
|
||||
relations: TRelation[];
|
||||
removed?: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -38,4 +38,14 @@ interface ICoa {
|
|||
t1: string;
|
||||
}
|
||||
|
||||
type TRelations = "Ally" | "Friendly" | "Neutral" | "Suspicion" | "Rival" | "Unknown" | "Suzerain" | "Vassal" | "x";
|
||||
type TRelation =
|
||||
| "Ally"
|
||||
| "Friendly"
|
||||
| "Neutral"
|
||||
| "Suspicion"
|
||||
| "Rival"
|
||||
| "Unknown"
|
||||
| "Suzerain"
|
||||
| "Vassal"
|
||||
| "Enemy"
|
||||
| "x";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue