mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
refactor: split states and burgs generators
This commit is contained in:
parent
e53c3a7773
commit
d7d79ad740
18 changed files with 445 additions and 358 deletions
|
|
@ -1892,7 +1892,7 @@ div.editorLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
#militaryOptionsTable input[type="number"] {
|
#militaryOptionsTable input[type="number"] {
|
||||||
width: 4em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#militaryOptionsTable button {
|
#militaryOptionsTable button {
|
||||||
|
|
|
||||||
30
index.html
30
index.html
|
|
@ -5321,6 +5321,7 @@
|
||||||
|
|
||||||
<div id="burgsBottom">
|
<div id="burgsBottom">
|
||||||
<button id="burgsOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
<button id="burgsOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
||||||
|
<button id="burgsGroupsEditorButton" data-tip="Edit burg groups" class="icon-cog"></button>
|
||||||
<button id="burgsChart" data-tip="Show burgs bubble chart" class="icon-chart-area"></button>
|
<button id="burgsChart" data-tip="Show burgs bubble chart" class="icon-chart-area"></button>
|
||||||
<button
|
<button
|
||||||
id="regenerateBurgNames"
|
id="regenerateBurgNames"
|
||||||
|
|
@ -5343,6 +5344,32 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="burgGroupsEditor" class="dialog stable" style="display: none">
|
||||||
|
<div class="table">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-tip="Unit icon">Icon</th>
|
||||||
|
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Unit name</th>
|
||||||
|
<th style="width: 5em" data-tip="Select allowed biomes">Biomes</th>
|
||||||
|
<th style="width: 5em" data-tip="Select allowed states">States</th>
|
||||||
|
<th style="width: 5em" data-tip="Select allowed cultures">Cultures</th>
|
||||||
|
<th style="width: 5em" data-tip="Select allowed religions">Religions</th>
|
||||||
|
<th data-tip="Conscription percentage for rural population">Rural</th>
|
||||||
|
<th data-tip="Conscription percentage for urban population">Urban</th>
|
||||||
|
<th data-tip="Average number of people in crew (used for total personnel calculation)">Crew</th>
|
||||||
|
<th data-tip="Unit military power (used for battle simulation)">Power</th>
|
||||||
|
<th data-tip="Unit type to apply special rules on forces recalculation">Type</th>
|
||||||
|
<th data-tip="Check if unit is separate and can be stacked only with units of the same type">
|
||||||
|
Separate
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="routesOverview" class="dialog stable" style="display: none">
|
<div id="routesOverview" class="dialog stable" style="display: none">
|
||||||
<div id="routesHeader" class="header" style="grid-template-columns: 17em 8em 8em">
|
<div id="routesHeader" class="header" style="grid-template-columns: 17em 8em 8em">
|
||||||
<div data-tip="Click to sort by route name" class="sortable alphabetically" data-sortby="name">
|
<div data-tip="Click to sort by route name" class="sortable alphabetically" data-sortby="name">
|
||||||
|
|
@ -8068,7 +8095,8 @@
|
||||||
<script src="modules/biomes.js?v=1.99.00"></script>
|
<script src="modules/biomes.js?v=1.99.00"></script>
|
||||||
<script src="modules/names-generator.js?v=1.87.14"></script>
|
<script src="modules/names-generator.js?v=1.87.14"></script>
|
||||||
<script src="modules/cultures-generator.js?v=1.99.05"></script>
|
<script src="modules/cultures-generator.js?v=1.99.05"></script>
|
||||||
<script src="modules/burgs-and-states.js?v=1.105.7"></script>
|
<script src="modules/burgs-generator.js?v=1.105.7"></script>
|
||||||
|
<script src="modules/states-generator.js?v=1.105.7"></script>
|
||||||
<script src="modules/provinces-generator.js?v=1.104.0"></script>
|
<script src="modules/provinces-generator.js?v=1.104.0"></script>
|
||||||
<script src="modules/routes-generator.js?v=1.104.10"></script>
|
<script src="modules/routes-generator.js?v=1.104.10"></script>
|
||||||
<script src="modules/religions-generator.js?v=1.99.05"></script>
|
<script src="modules/religions-generator.js?v=1.99.05"></script>
|
||||||
|
|
|
||||||
12
main.js
12
main.js
|
|
@ -194,7 +194,10 @@ let options = {
|
||||||
temperatureSouthPole: -15,
|
temperatureSouthPole: -15,
|
||||||
stateLabelsMode: "auto",
|
stateLabelsMode: "auto",
|
||||||
showBurgPreview: true,
|
showBurgPreview: true,
|
||||||
villageMaxPopulation: 2000
|
villageMaxPopulation: 2000,
|
||||||
|
burgs: {
|
||||||
|
groups: Burgs.getDefaultGroups()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mapCoordinates = {}; // map coordinates on globe
|
let mapCoordinates = {}; // map coordinates on globe
|
||||||
|
|
@ -658,13 +661,14 @@ async function generate(options) {
|
||||||
rankCells();
|
rankCells();
|
||||||
Cultures.generate();
|
Cultures.generate();
|
||||||
Cultures.expand();
|
Cultures.expand();
|
||||||
BurgsAndStates.generate();
|
Burgs.generate();
|
||||||
|
States.generate();
|
||||||
Routes.generate();
|
Routes.generate();
|
||||||
Religions.generate();
|
Religions.generate();
|
||||||
BurgsAndStates.defineStateForms();
|
States.defineStateForms();
|
||||||
Provinces.generate();
|
Provinces.generate();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
BurgsAndStates.defineBurgFeatures();
|
Burgs.specifyBurgs();
|
||||||
|
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
Features.specify();
|
Features.specify();
|
||||||
|
|
|
||||||
279
modules/burgs-generator.js
Normal file
279
modules/burgs-generator.js
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
window.Burgs = (() => {
|
||||||
|
const generate = () => {
|
||||||
|
TIME && console.time("generateBurgs");
|
||||||
|
const {cells} = pack;
|
||||||
|
|
||||||
|
let burgs = [0]; // burgs array
|
||||||
|
cells.burg = new Uint16Array(cells.i.length);
|
||||||
|
|
||||||
|
const populatedCells = cells.i.filter(i => cells.s[i] > 0 && cells.culture[i]);
|
||||||
|
if (!populatedCells.length) {
|
||||||
|
WARN && console.warn("There is no populated cells. Cannot generate states");
|
||||||
|
return burgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
let quadtree = d3.quadtree();
|
||||||
|
generateCapitals();
|
||||||
|
generateTowns();
|
||||||
|
|
||||||
|
pack.burgs = burgs;
|
||||||
|
TIME && console.timeEnd("generateBurgs");
|
||||||
|
|
||||||
|
function getCapitalsNumber() {
|
||||||
|
let number = +byId("statesNumber").value;
|
||||||
|
|
||||||
|
if (populatedCells.length < number * 10) {
|
||||||
|
WARN && console.warn(`Not enough populated cells. Generating only ${number} capitals/states`);
|
||||||
|
number = Math.floor(sorted.length / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTownsNumber() {
|
||||||
|
const manorsInput = byId("manorsInput");
|
||||||
|
const isAuto = manorsInput.value === "1000"; // '1000' is considered as auto
|
||||||
|
if (isAuto) return rn(populatedCells.length / 5 / (grid.points.length / 10000) ** 0.8);
|
||||||
|
|
||||||
|
return Math.min(manorsInput.valueAsNumber, sorted.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCapitals() {
|
||||||
|
const randomize = score => score * (0.5 + Math.random() * 0.5);
|
||||||
|
const score = new Int16Array(cells.s.map(randomize));
|
||||||
|
const sorted = populatedCells.sort((a, b) => score[b] - score[a]);
|
||||||
|
|
||||||
|
const capitalsNumber = getCapitalsNumber();
|
||||||
|
let spacing = (graphWidth + graphHeight) / 2 / capitalsNumber; // min distance between capitals
|
||||||
|
|
||||||
|
for (let i = 0; burgs.length <= capitalsNumber; i++) {
|
||||||
|
const cell = sorted[i];
|
||||||
|
const [x, y] = cells.p[cell];
|
||||||
|
|
||||||
|
if (quadtree.find(x, y, spacing) === undefined) {
|
||||||
|
burgs.push({cell, x, y});
|
||||||
|
quadtree.add([x, y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset if all cells were checked
|
||||||
|
if (i === sorted.length - 1) {
|
||||||
|
WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing");
|
||||||
|
quadtree = d3.quadtree();
|
||||||
|
i = -1;
|
||||||
|
burgs = [0];
|
||||||
|
spacing /= 1.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
burgs.forEach((burg, burgId) => {
|
||||||
|
if (!burgId) return;
|
||||||
|
burg.i = burgId;
|
||||||
|
burg.state = burgId;
|
||||||
|
burg.culture = cells.culture[burg.cell];
|
||||||
|
burg.name = Names.getCultureShort(burg.culture);
|
||||||
|
burg.feature = cells.f[burg.cell];
|
||||||
|
burg.capital = 1;
|
||||||
|
cells.burg[burg.cell] = burgId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateTowns() {
|
||||||
|
const randomize = score => score * gauss(1, 3, 0, 20, 3);
|
||||||
|
const score = new Int16Array(cells.s.map(randomize));
|
||||||
|
const sorted = populatedCells.sort((a, b) => score[b] - score[a]);
|
||||||
|
|
||||||
|
const burgsNumber = getTownsNumber();
|
||||||
|
let spacing = (graphWidth + graphHeight) / 150 / (burgsNumber ** 0.7 / 66); // min distance between towns
|
||||||
|
|
||||||
|
for (let added = 0; added < burgsNumber && spacing > 1; ) {
|
||||||
|
for (let i = 0; added < burgsNumber && i < sorted.length; i++) {
|
||||||
|
if (cells.burg[sorted[i]]) continue;
|
||||||
|
const cell = sorted[i];
|
||||||
|
const [x, y] = cells.p[cell];
|
||||||
|
|
||||||
|
const minSpacing = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make placement not uniform
|
||||||
|
if (quadtree.find(x, y, minSpacing) !== undefined) continue; // to close to existing burg
|
||||||
|
|
||||||
|
const burgId = burgs.length;
|
||||||
|
const culture = cells.culture[cell];
|
||||||
|
const name = Names.getCulture(culture);
|
||||||
|
const feature = cells.f[cell];
|
||||||
|
burgs.push({cell, x, y, i: burgId, state: 0, culture, name, feature, capital: 0});
|
||||||
|
added++;
|
||||||
|
cells.burg[cell] = burgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing *= 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultGroups = () => [
|
||||||
|
{name: "capitals", active: true, features: {capital: true}, preview: "watabou-city-generator"},
|
||||||
|
{name: "cities", active: true, percentile: 90, preview: "watabou-city-generator"},
|
||||||
|
{
|
||||||
|
name: "forts",
|
||||||
|
active: true,
|
||||||
|
features: {citadel: true, walls: false, plaza: false, port: false},
|
||||||
|
population: [0, 1],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "monasteries",
|
||||||
|
active: true,
|
||||||
|
features: {temple: true, walls: false, plaza: false, port: false},
|
||||||
|
population: [0, 1],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "caravanserais",
|
||||||
|
active: true,
|
||||||
|
features: {port: false},
|
||||||
|
population: [0, 1],
|
||||||
|
biomes: [1, 2, 3],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trading posts",
|
||||||
|
active: true,
|
||||||
|
features: {plaza: true},
|
||||||
|
population: [0, 1],
|
||||||
|
biomes: [1, 2, 3],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{name: "villages", active: true, population: [0.1, 2], preview: "watabou-village-generator"},
|
||||||
|
{
|
||||||
|
name: "hamlets",
|
||||||
|
active: true,
|
||||||
|
features: {plaza: true, walls: false, plaza: false},
|
||||||
|
population: [0, 0.1],
|
||||||
|
preview: "watabou-village-generator"
|
||||||
|
},
|
||||||
|
{name: "towns", active: true, isDefault: true, preview: "watabou-city-generator"}
|
||||||
|
];
|
||||||
|
|
||||||
|
// define burg coordinates, coa, port status and define details
|
||||||
|
const specifyBurgs = () => {
|
||||||
|
TIME && console.time("specifyBurgs");
|
||||||
|
const {cells, features} = pack;
|
||||||
|
const temp = grid.cells.temp;
|
||||||
|
|
||||||
|
for (const burg of pack.burgs) {
|
||||||
|
if (!burg.i || burg.lock) continue;
|
||||||
|
const i = burg.cell;
|
||||||
|
|
||||||
|
// asign port status to some coastline burgs with temp > 0 °C
|
||||||
|
const haven = cells.haven[i];
|
||||||
|
if (haven && temp[cells.g[i]] > 0) {
|
||||||
|
const f = cells.f[haven]; // water body id
|
||||||
|
// port is a capital with any harbor OR town with good harbor
|
||||||
|
const port = features[f].cells > 1 && ((burg.capital && cells.harbor[i]) || cells.harbor[i] === 1);
|
||||||
|
burg.port = port ? f : 0; // port is defined by water body id it lays on
|
||||||
|
} else burg.port = 0;
|
||||||
|
|
||||||
|
// define burg population (keep urbanization at about 10% rate)
|
||||||
|
burg.population = rn(Math.max(cells.s[i] / 8 + burg.i / 1000 + (i % 100) / 1000, 0.1), 3);
|
||||||
|
if (burg.capital) burg.population = rn(burg.population * 1.3, 3); // increase capital population
|
||||||
|
|
||||||
|
if (burg.port) {
|
||||||
|
burg.population = burg.population * 1.3; // increase port population
|
||||||
|
const [x, y] = getCloseToEdgePoint(i, haven);
|
||||||
|
burg.x = x;
|
||||||
|
burg.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add random factor
|
||||||
|
burg.population = rn(burg.population * gauss(2, 3, 0.6, 20, 3), 3);
|
||||||
|
|
||||||
|
// shift burgs on rivers semi-randomly and just a bit
|
||||||
|
if (!burg.port && cells.r[i]) {
|
||||||
|
const shift = Math.min(cells.fl[i] / 150, 1);
|
||||||
|
if (i % 2) burg.x = rn(burg.x + shift, 2);
|
||||||
|
else burg.x = rn(burg.x - shift, 2);
|
||||||
|
if (cells.r[i] % 2) burg.y = rn(burg.y + shift, 2);
|
||||||
|
else burg.y = rn(burg.y - shift, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// define emblem
|
||||||
|
const state = pack.states[burg.state];
|
||||||
|
const stateCOA = state.coa;
|
||||||
|
let kinship = 0.25;
|
||||||
|
if (burg.capital) kinship += 0.1;
|
||||||
|
else if (burg.port) kinship -= 0.1;
|
||||||
|
if (burg.culture !== state.culture) kinship -= 0.25;
|
||||||
|
burg.type = getType(i, burg.port);
|
||||||
|
const type = burg.capital && P(0.2) ? "Capital" : burg.type === "Generic" ? "City" : burg.type;
|
||||||
|
burg.coa = COA.generate(stateCOA, kinship, null, type);
|
||||||
|
burg.coa.shield = COA.getShield(burg.culture, burg.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// de-assign port status if it's the only one on feature
|
||||||
|
const ports = pack.burgs.filter(b => !b.removed && b.port > 0);
|
||||||
|
for (const f of features) {
|
||||||
|
if (!f.i || f.land || f.border) continue;
|
||||||
|
const featurePorts = ports.filter(b => b.port === f.i);
|
||||||
|
if (featurePorts.length === 1) featurePorts[0].port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.burgs.filter(b => b.i && !b.removed && !b.lock).forEach(defineBurgFeatures);
|
||||||
|
|
||||||
|
TIME && console.timeEnd("specifyBurgs");
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCloseToEdgePoint(cell1, cell2) {
|
||||||
|
const {cells, vertices} = pack;
|
||||||
|
|
||||||
|
const [x0, y0] = cells.p[cell1];
|
||||||
|
|
||||||
|
const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2));
|
||||||
|
const [x1, y1] = vertices.p[commonVertices[0]];
|
||||||
|
const [x2, y2] = vertices.p[commonVertices[1]];
|
||||||
|
const xEdge = (x1 + x2) / 2;
|
||||||
|
const yEdge = (y1 + y2) / 2;
|
||||||
|
|
||||||
|
const x = rn(x0 + 0.95 * (xEdge - x0), 2);
|
||||||
|
const y = rn(y0 + 0.95 * (yEdge - y0), 2);
|
||||||
|
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
|
||||||
|
const getType = (cellId, port) => {
|
||||||
|
const {cells, features} = pack;
|
||||||
|
|
||||||
|
if (port) return "Naval";
|
||||||
|
|
||||||
|
const haven = cells.haven[cellId];
|
||||||
|
if (haven !== undefined && features[cells.f[haven]].type === "lake") return "Lake";
|
||||||
|
|
||||||
|
if (cells.h[cellId] > 60) return "Highland";
|
||||||
|
|
||||||
|
if (cells.r[cellId] && cells.fl[cellId] >= 100) return "River";
|
||||||
|
|
||||||
|
const biome = cells.biome[cellId];
|
||||||
|
const population = cells.pop[cellId];
|
||||||
|
if (!cells.burg[cellId] || population <= 5) {
|
||||||
|
if (population < 5 && [1, 2, 3, 4].includes(biome)) return "Nomadic";
|
||||||
|
if (biome > 4 && biome < 10) return "Hunting";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Generic";
|
||||||
|
};
|
||||||
|
|
||||||
|
const defineBurgFeatures = burg => {
|
||||||
|
const {cells, states} = pack;
|
||||||
|
const pop = burg.population;
|
||||||
|
burg.citadel = Number(burg.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1));
|
||||||
|
burg.plaza = Number(pop > 20 || (pop > 10 && P(0.8)) || (pop > 4 && P(0.7)) || P(0.6));
|
||||||
|
burg.walls = Number(burg.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1));
|
||||||
|
burg.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && burg.walls && P(0.4)));
|
||||||
|
const religion = cells.religion[burg.cell];
|
||||||
|
const theocracy = states[burg.state].form === "Theocracy";
|
||||||
|
burg.temple = Number(
|
||||||
|
(religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {generate, getDefaultGroups, specifyBurgs, getType, defineBurgFeatures};
|
||||||
|
})();
|
||||||
|
|
@ -47,10 +47,10 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
|
|
||||||
// v1.0 added state relations, provinces, forms and full names
|
// v1.0 added state relations, provinces, forms and full names
|
||||||
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
|
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
|
||||||
BurgsAndStates.collectStatistics();
|
States.collectStatistics();
|
||||||
BurgsAndStates.generateCampaigns();
|
States.generateCampaigns();
|
||||||
BurgsAndStates.generateDiplomacy();
|
States.generateDiplomacy();
|
||||||
BurgsAndStates.defineStateForms();
|
States.defineStateForms();
|
||||||
Provinces.generate();
|
Provinces.generate();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
|
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
|
||||||
|
|
@ -260,7 +260,7 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
|
|
||||||
if (isOlderThan("1.22.0")) {
|
if (isOlderThan("1.22.0")) {
|
||||||
// v1.22 changed state neighbors from Set object to array
|
// v1.22 changed state neighbors from Set object to array
|
||||||
BurgsAndStates.collectStatistics();
|
States.collectStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOlderThan("1.3.0")) {
|
if (isOlderThan("1.3.0")) {
|
||||||
|
|
@ -273,7 +273,7 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
options = {winds, year, era, eraShort, military};
|
options = {winds, year, era, eraShort, military};
|
||||||
|
|
||||||
// v1.3 added campaings data for all states
|
// v1.3 added campaings data for all states
|
||||||
BurgsAndStates.generateCampaigns();
|
States.generateCampaigns();
|
||||||
|
|
||||||
// v1.3 added militry layer
|
// v1.3 added militry layer
|
||||||
armies = viewbox.insert("g", "#icons").attr("id", "armies");
|
armies = viewbox.insert("g", "#icons").attr("id", "armies");
|
||||||
|
|
@ -348,7 +348,7 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
// v1.5 added burg type value
|
// v1.5 added burg type value
|
||||||
pack.burgs.forEach(burg => {
|
pack.burgs.forEach(burg => {
|
||||||
if (!burg.i || burg.removed) return;
|
if (!burg.i || burg.removed) return;
|
||||||
burg.type = BurgsAndStates.getType(burg.cell, burg.port);
|
burg.type = Burgs.getType(burg.cell, burg.port);
|
||||||
});
|
});
|
||||||
|
|
||||||
// v1.5 added emblems
|
// v1.5 added emblems
|
||||||
|
|
@ -944,7 +944,7 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
|
|
||||||
if (isOlderThan("1.104.0")) {
|
if (isOlderThan("1.104.0")) {
|
||||||
// v1.104.00 separated pole of inaccessibility detection from layer rendering
|
// v1.104.00 separated pole of inaccessibility detection from layer rendering
|
||||||
BurgsAndStates.getPoles();
|
States.getPoles();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -967,5 +967,9 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
|
|
||||||
// v1.106.0 change burg groups and added customizable icons
|
// v1.106.0 change burg groups and added customizable icons
|
||||||
icons.selectAll("circle, use").remove();
|
icons.selectAll("circle, use").remove();
|
||||||
|
|
||||||
|
options.burgs = {
|
||||||
|
groups: States.getDefaultGroups()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ function addListeners() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshStatesEditor() {
|
function refreshStatesEditor() {
|
||||||
BurgsAndStates.collectStatistics();
|
States.collectStatistics();
|
||||||
statesEditorAddLines();
|
statesEditorAddLines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -839,10 +839,10 @@ function openRegenerationMenu() {
|
||||||
function recalculateStates(must) {
|
function recalculateStates(must) {
|
||||||
if (!must && !statesAutoChange.checked) return;
|
if (!must && !statesAutoChange.checked) return;
|
||||||
|
|
||||||
BurgsAndStates.expandStates();
|
States.expandStates();
|
||||||
Provinces.generate();
|
Provinces.generate();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
BurgsAndStates.getPoles();
|
States.getPoles();
|
||||||
|
|
||||||
if (layerIsOn("toggleStates")) drawStates();
|
if (layerIsOn("toggleStates")) drawStates();
|
||||||
if (layerIsOn("toggleBorders")) drawBorders();
|
if (layerIsOn("toggleBorders")) drawBorders();
|
||||||
|
|
@ -984,7 +984,7 @@ function applyStatesManualAssignent() {
|
||||||
|
|
||||||
if (affectedStates.length) {
|
if (affectedStates.length) {
|
||||||
refreshStatesEditor();
|
refreshStatesEditor();
|
||||||
BurgsAndStates.getPoles();
|
States.getPoles();
|
||||||
layerIsOn("toggleStates") ? drawStates() : toggleStates();
|
layerIsOn("toggleStates") ? drawStates() : toggleStates();
|
||||||
if (adjustLabels.checked) drawStateLabels([...new Set(affectedStates)]);
|
if (adjustLabels.checked) drawStateLabels([...new Set(affectedStates)]);
|
||||||
adjustProvinces([...new Set(affectedProvinces)]);
|
adjustProvinces([...new Set(affectedProvinces)]);
|
||||||
|
|
@ -1102,7 +1102,7 @@ function adjustProvinces(affectedProvinces) {
|
||||||
const color = getMixedColor(states[stateId].color);
|
const color = getMixedColor(states[stateId].color);
|
||||||
|
|
||||||
const kinship = nameByBurg ? 0.8 : 0.4;
|
const kinship = nameByBurg ? 0.8 : 0.4;
|
||||||
const type = BurgsAndStates.getType(center, burg?.port);
|
const type = Burgs.getType(center, burg?.port);
|
||||||
const coa = COA.generate(burg?.coa || states[stateId].coa, kinship, burg ? null : 0.9, type);
|
const coa = COA.generate(burg?.coa || states[stateId].coa, kinship, burg ? null : 0.9, type);
|
||||||
coa.shield = COA.getShield(culture, stateId);
|
coa.shield = COA.getShield(culture, stateId);
|
||||||
|
|
||||||
|
|
@ -1253,8 +1253,8 @@ function addState() {
|
||||||
coa,
|
coa,
|
||||||
pole
|
pole
|
||||||
});
|
});
|
||||||
BurgsAndStates.collectStatistics();
|
States.collectStatistics();
|
||||||
BurgsAndStates.defineStateForms([newState]);
|
States.defineStateForms([newState]);
|
||||||
adjustProvinces([cells.province[center]]);
|
adjustProvinces([cells.province[center]]);
|
||||||
|
|
||||||
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
||||||
|
|
@ -1419,7 +1419,7 @@ function openStateMergeDialog() {
|
||||||
unfog();
|
unfog();
|
||||||
debug.selectAll(".highlight").remove();
|
debug.selectAll(".highlight").remove();
|
||||||
|
|
||||||
BurgsAndStates.getPoles();
|
States.getPoles();
|
||||||
layerIsOn("toggleStates") ? drawStates() : toggleStates();
|
layerIsOn("toggleStates") ? drawStates() : toggleStates();
|
||||||
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
|
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
|
||||||
layerIsOn("toggleProvinces") && drawProvinces();
|
layerIsOn("toggleProvinces") && drawProvinces();
|
||||||
|
|
|
||||||
|
|
@ -592,7 +592,7 @@ window.Markers = (function () {
|
||||||
const {cells, states} = pack;
|
const {cells, states} = pack;
|
||||||
|
|
||||||
const state = states[cells.state[cell]];
|
const state = states[cells.state[cell]];
|
||||||
if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state);
|
if (!state.campaigns) state.campaigns = States.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);
|
||||||
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
|
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ window.Provinces = (function () {
|
||||||
const fullName = name + " " + formName;
|
const fullName = name + " " + formName;
|
||||||
const color = getMixedColor(s.color);
|
const color = getMixedColor(s.color);
|
||||||
const kinship = nameByBurg ? 0.8 : 0.4;
|
const kinship = nameByBurg ? 0.8 : 0.4;
|
||||||
const type = BurgsAndStates.getType(center, burg.port);
|
const type = Burgs.getType(center, burg.port);
|
||||||
const coa = COA.generate(stateBurgs[i].coa, kinship, null, type);
|
const coa = COA.generate(stateBurgs[i].coa, kinship, null, type);
|
||||||
coa.shield = COA.getShield(c, s.i);
|
coa.shield = COA.getShield(c, s.i);
|
||||||
|
|
||||||
|
|
@ -206,7 +206,7 @@ window.Provinces = (function () {
|
||||||
|
|
||||||
const dominion = colony ? P(0.95) : singleIsle || isleGroup ? P(0.7) : P(0.3);
|
const dominion = colony ? P(0.95) : singleIsle || isleGroup ? P(0.7) : P(0.3);
|
||||||
const kinship = dominion ? 0 : 0.4;
|
const kinship = dominion ? 0 : 0.4;
|
||||||
const type = BurgsAndStates.getType(center, burgs[burg]?.port);
|
const type = Burgs.getType(center, burgs[burg]?.port);
|
||||||
const coa = COA.generate(s.coa, kinship, dominion, type);
|
const coa = COA.generate(s.coa, kinship, dominion, type);
|
||||||
coa.shield = COA.getShield(c, s.i);
|
coa.shield = COA.getShield(c, s.i);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -851,13 +851,6 @@ window.Religions = (function () {
|
||||||
cells.religion[center] = i;
|
cells.religion[center] = i;
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateCultures() {
|
|
||||||
pack.religions = pack.religions.map((religion, index) => {
|
|
||||||
if (index === 0) return religion;
|
|
||||||
return {...religion, culture: pack.cells.culture[religion.center]};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// get supreme deity name
|
// get supreme deity name
|
||||||
const getDeityName = function (culture) {
|
const getDeityName = function (culture) {
|
||||||
if (culture === undefined) {
|
if (culture === undefined) {
|
||||||
|
|
@ -924,5 +917,5 @@ window.Religions = (function () {
|
||||||
return [trimVowels(random()) + "ism", "global"]; // else
|
return [trimVowels(random()) + "ism", "global"]; // else
|
||||||
}
|
}
|
||||||
|
|
||||||
return {generate, add, getDeityName, updateCultures, recalculate};
|
return {generate, add, getDeityName, recalculate};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1,283 +1,50 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
window.BurgsAndStates = (() => {
|
window.States = (() => {
|
||||||
const generate = () => {
|
const generate = () => {
|
||||||
const {cells, cultures} = pack;
|
TIME && console.time("generateStates");
|
||||||
const n = cells.i.length;
|
|
||||||
|
|
||||||
cells.burg = new Uint16Array(n); // cell burg
|
|
||||||
|
|
||||||
const burgs = (pack.burgs = placeCapitals());
|
|
||||||
pack.states = createStates();
|
pack.states = createStates();
|
||||||
|
|
||||||
placeTowns();
|
|
||||||
expandStates();
|
expandStates();
|
||||||
normalizeStates();
|
normalizeStates();
|
||||||
getPoles();
|
getPoles();
|
||||||
|
|
||||||
specifyBurgs();
|
|
||||||
|
|
||||||
collectStatistics();
|
collectStatistics();
|
||||||
assignColors();
|
assignColors();
|
||||||
|
|
||||||
generateCampaigns();
|
generateCampaigns();
|
||||||
generateDiplomacy();
|
generateDiplomacy();
|
||||||
|
|
||||||
function placeCapitals() {
|
TIME && console.timeEnd("generateStates");
|
||||||
TIME && console.time("placeCapitals");
|
|
||||||
let count = +byId("statesNumber").value;
|
|
||||||
let burgs = [0];
|
|
||||||
|
|
||||||
const rand = () => 0.5 + Math.random() * 0.5;
|
// for each capital create a state
|
||||||
const score = new Int16Array(cells.s.map(s => s * rand())); // cell score for capitals placement
|
|
||||||
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
|
|
||||||
|
|
||||||
if (sorted.length < count * 10) {
|
|
||||||
count = Math.floor(sorted.length / 10);
|
|
||||||
if (!count) {
|
|
||||||
WARN && console.warn("There is no populated cells. Cannot generate states");
|
|
||||||
return burgs;
|
|
||||||
} else {
|
|
||||||
WARN && console.warn(`Not enough populated cells (${sorted.length}). Will generate only ${count} states`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let burgsTree = d3.quadtree();
|
|
||||||
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
|
|
||||||
|
|
||||||
for (let i = 0; burgs.length <= count; i++) {
|
|
||||||
const cell = sorted[i];
|
|
||||||
const [x, y] = cells.p[cell];
|
|
||||||
|
|
||||||
if (burgsTree.find(x, y, spacing) === undefined) {
|
|
||||||
burgs.push({cell, x, y});
|
|
||||||
burgsTree.add([x, y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i === sorted.length - 1) {
|
|
||||||
WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing");
|
|
||||||
burgsTree = d3.quadtree();
|
|
||||||
i = -1;
|
|
||||||
burgs = [0];
|
|
||||||
spacing /= 1.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
burgs[0] = burgsTree;
|
|
||||||
TIME && console.timeEnd("placeCapitals");
|
|
||||||
return burgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each capital create a state
|
|
||||||
function createStates() {
|
function createStates() {
|
||||||
TIME && console.time("createStates");
|
|
||||||
const states = [{i: 0, name: "Neutrals"}];
|
const states = [{i: 0, name: "Neutrals"}];
|
||||||
const colors = getColors(burgs.length - 1);
|
|
||||||
const each5th = each(5);
|
const each5th = each(5);
|
||||||
|
|
||||||
burgs.forEach((b, i) => {
|
pack.burgs.forEach(burg => {
|
||||||
if (!i) return; // skip first element
|
if (!burg.i || !burg.capital) return;
|
||||||
|
|
||||||
// burgs data
|
|
||||||
b.i = b.state = i;
|
|
||||||
b.culture = cells.culture[b.cell];
|
|
||||||
b.name = Names.getCultureShort(b.culture);
|
|
||||||
b.feature = cells.f[b.cell];
|
|
||||||
b.capital = 1;
|
|
||||||
|
|
||||||
// states data
|
|
||||||
const expansionism = rn(Math.random() * byId("sizeVariety").value + 1, 1);
|
const expansionism = rn(Math.random() * byId("sizeVariety").value + 1, 1);
|
||||||
const basename = b.name.length < 9 && each5th(b.cell) ? b.name : Names.getCultureShort(b.culture);
|
const basename = burg.name.length < 9 && each5th(burg.cell) ? burg.name : Names.getCultureShort(burg.culture);
|
||||||
const name = Names.getState(basename, b.culture);
|
const name = Names.getState(basename, burg.culture);
|
||||||
const type = cultures[b.culture].type;
|
const type = pack.cultures[burg.culture].type;
|
||||||
|
|
||||||
const coa = COA.generate(null, null, null, type);
|
const coa = COA.generate(null, null, null, type);
|
||||||
coa.shield = COA.getShield(b.culture, null);
|
coa.shield = COA.getShield(burg.culture, null);
|
||||||
states.push({
|
states.push({
|
||||||
i,
|
i: burg.i,
|
||||||
color: colors[i - 1],
|
|
||||||
name,
|
name,
|
||||||
expansionism,
|
expansionism,
|
||||||
capital: i,
|
capital: burg.i,
|
||||||
type,
|
type,
|
||||||
center: b.cell,
|
center: burg.cell,
|
||||||
culture: b.culture,
|
culture: burg.culture,
|
||||||
coa
|
coa
|
||||||
});
|
});
|
||||||
cells.burg[b.cell] = i;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
TIME && console.timeEnd("createStates");
|
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
// place secondary settlements based on geo and economical evaluation
|
|
||||||
function placeTowns() {
|
|
||||||
TIME && console.time("placeTowns");
|
|
||||||
const score = new Int16Array(cells.s.map(s => s * gauss(1, 3, 0, 20, 3))); // a bit randomized cell score for towns placement
|
|
||||||
const sorted = cells.i
|
|
||||||
.filter(i => !cells.burg[i] && score[i] > 0 && cells.culture[i])
|
|
||||||
.sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
|
|
||||||
|
|
||||||
const desiredNumber =
|
|
||||||
manorsInput.value == 1000
|
|
||||||
? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8)
|
|
||||||
: manorsInput.valueAsNumber;
|
|
||||||
const burgsNumber = Math.min(desiredNumber, sorted.length); // towns to generate
|
|
||||||
let burgsAdded = 0;
|
|
||||||
|
|
||||||
const burgsTree = burgs[0];
|
|
||||||
let spacing = (graphWidth + graphHeight) / 150 / (burgsNumber ** 0.7 / 66); // min distance between towns
|
|
||||||
|
|
||||||
while (burgsAdded < burgsNumber && spacing > 1) {
|
|
||||||
for (let i = 0; burgsAdded < burgsNumber && i < sorted.length; i++) {
|
|
||||||
if (cells.burg[sorted[i]]) continue;
|
|
||||||
const cell = sorted[i];
|
|
||||||
const [x, y] = cells.p[cell];
|
|
||||||
const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make placement not uniform
|
|
||||||
if (burgsTree.find(x, y, s) !== undefined) continue; // to close to existing burg
|
|
||||||
const burg = burgs.length;
|
|
||||||
const culture = cells.culture[cell];
|
|
||||||
const name = Names.getCulture(culture);
|
|
||||||
burgs.push({cell, x, y, state: 0, i: burg, culture, name, capital: 0, feature: cells.f[cell]});
|
|
||||||
burgsTree.add([x, y]);
|
|
||||||
cells.burg[cell] = burg;
|
|
||||||
burgsAdded++;
|
|
||||||
}
|
|
||||||
spacing *= 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manorsInput.value != 1000 && burgsAdded < desiredNumber) {
|
|
||||||
ERROR && console.error(`Cannot place all burgs. Requested ${desiredNumber}, placed ${burgsAdded}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
burgs[0] = {name: undefined}; // do not store burgsTree anymore
|
|
||||||
TIME && console.timeEnd("placeTowns");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// define burg coordinates, coa, port status and define details
|
|
||||||
const specifyBurgs = () => {
|
|
||||||
TIME && console.time("specifyBurgs");
|
|
||||||
const {cells, features} = pack;
|
|
||||||
const temp = grid.cells.temp;
|
|
||||||
|
|
||||||
for (const b of pack.burgs) {
|
|
||||||
if (!b.i || b.lock) continue;
|
|
||||||
const i = b.cell;
|
|
||||||
|
|
||||||
// asign port status to some coastline burgs with temp > 0 °C
|
|
||||||
const haven = cells.haven[i];
|
|
||||||
if (haven && temp[cells.g[i]] > 0) {
|
|
||||||
const f = cells.f[haven]; // water body id
|
|
||||||
// port is a capital with any harbor OR town with good harbor
|
|
||||||
const port = features[f].cells > 1 && ((b.capital && cells.harbor[i]) || cells.harbor[i] === 1);
|
|
||||||
b.port = port ? f : 0; // port is defined by water body id it lays on
|
|
||||||
} else b.port = 0;
|
|
||||||
|
|
||||||
// define burg population (keep urbanization at about 10% rate)
|
|
||||||
b.population = rn(Math.max(cells.s[i] / 8 + b.i / 1000 + (i % 100) / 1000, 0.1), 3);
|
|
||||||
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
|
|
||||||
|
|
||||||
if (b.port) {
|
|
||||||
b.population = b.population * 1.3; // increase port population
|
|
||||||
const [x, y] = getCloseToEdgePoint(i, haven);
|
|
||||||
b.x = x;
|
|
||||||
b.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add random factor
|
|
||||||
b.population = rn(b.population * gauss(2, 3, 0.6, 20, 3), 3);
|
|
||||||
|
|
||||||
// shift burgs on rivers semi-randomly and just a bit
|
|
||||||
if (!b.port && cells.r[i]) {
|
|
||||||
const shift = Math.min(cells.fl[i] / 150, 1);
|
|
||||||
if (i % 2) b.x = rn(b.x + shift, 2);
|
|
||||||
else b.x = rn(b.x - shift, 2);
|
|
||||||
if (cells.r[i] % 2) b.y = rn(b.y + shift, 2);
|
|
||||||
else b.y = rn(b.y - shift, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// define emblem
|
|
||||||
const state = pack.states[b.state];
|
|
||||||
const stateCOA = state.coa;
|
|
||||||
let kinship = 0.25;
|
|
||||||
if (b.capital) kinship += 0.1;
|
|
||||||
else if (b.port) kinship -= 0.1;
|
|
||||||
if (b.culture !== state.culture) kinship -= 0.25;
|
|
||||||
b.type = getType(i, b.port);
|
|
||||||
const type = b.capital && P(0.2) ? "Capital" : b.type === "Generic" ? "City" : b.type;
|
|
||||||
b.coa = COA.generate(stateCOA, kinship, null, type);
|
|
||||||
b.coa.shield = COA.getShield(b.culture, b.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// de-assign port status if it's the only one on feature
|
|
||||||
const ports = pack.burgs.filter(b => !b.removed && b.port > 0);
|
|
||||||
for (const f of features) {
|
|
||||||
if (!f.i || f.land || f.border) continue;
|
|
||||||
const featurePorts = ports.filter(b => b.port === f.i);
|
|
||||||
if (featurePorts.length === 1) featurePorts[0].port = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TIME && console.timeEnd("specifyBurgs");
|
|
||||||
};
|
|
||||||
|
|
||||||
function getCloseToEdgePoint(cell1, cell2) {
|
|
||||||
const {cells, vertices} = pack;
|
|
||||||
|
|
||||||
const [x0, y0] = cells.p[cell1];
|
|
||||||
|
|
||||||
const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2));
|
|
||||||
const [x1, y1] = vertices.p[commonVertices[0]];
|
|
||||||
const [x2, y2] = vertices.p[commonVertices[1]];
|
|
||||||
const xEdge = (x1 + x2) / 2;
|
|
||||||
const yEdge = (y1 + y2) / 2;
|
|
||||||
|
|
||||||
const x = rn(x0 + 0.95 * (xEdge - x0), 2);
|
|
||||||
const y = rn(y0 + 0.95 * (yEdge - y0), 2);
|
|
||||||
|
|
||||||
return [x, y];
|
|
||||||
}
|
|
||||||
|
|
||||||
const getType = (cellId, port) => {
|
|
||||||
const {cells, features, burgs} = pack;
|
|
||||||
|
|
||||||
if (port) return "Naval";
|
|
||||||
|
|
||||||
const haven = cells.haven[cellId];
|
|
||||||
if (haven !== undefined && features[cells.f[haven]].type === "lake") return "Lake";
|
|
||||||
|
|
||||||
if (cells.h[cellId] > 60) return "Highland";
|
|
||||||
|
|
||||||
if (cells.r[cellId] && cells.fl[cellId] >= 100) return "River";
|
|
||||||
|
|
||||||
const biome = cells.biome[cellId];
|
|
||||||
const population = cells.pop[cellId];
|
|
||||||
if (!cells.burg[cellId] || population <= 5) {
|
|
||||||
if (population < 5 && [1, 2, 3, 4].includes(biome)) return "Nomadic";
|
|
||||||
if (biome > 4 && biome < 10) return "Hunting";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Generic";
|
|
||||||
};
|
|
||||||
|
|
||||||
const defineBurgFeatures = burg => {
|
|
||||||
const {cells} = pack;
|
|
||||||
|
|
||||||
pack.burgs
|
|
||||||
.filter(b => (burg ? b.i == burg.i : b.i && !b.removed && !b.lock))
|
|
||||||
.forEach(b => {
|
|
||||||
const pop = b.population;
|
|
||||||
b.citadel = Number(b.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1));
|
|
||||||
b.plaza = Number(pop > 20 || (pop > 10 && P(0.8)) || (pop > 4 && P(0.7)) || P(0.6));
|
|
||||||
b.walls = Number(b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1));
|
|
||||||
b.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4)));
|
|
||||||
const religion = cells.religion[b.cell];
|
|
||||||
const theocracy = pack.states[b.state].form === "Theocracy";
|
|
||||||
b.temple = Number(
|
|
||||||
(religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// expand cultures across the map (Dijkstra-like algorithm)
|
// expand cultures across the map (Dijkstra-like algorithm)
|
||||||
|
|
@ -407,25 +174,6 @@ window.BurgsAndStates = (() => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Resets the cultures of all burgs and states to their cell or center cell's (respectively) culture
|
|
||||||
const updateCultures = () => {
|
|
||||||
TIME && console.time("updateCulturesForBurgsAndStates");
|
|
||||||
|
|
||||||
// Assign the culture associated with the burgs cell
|
|
||||||
pack.burgs = pack.burgs.map((burg, index) => {
|
|
||||||
if (index === 0) return burg;
|
|
||||||
return {...burg, culture: pack.cells.culture[burg.cell]};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assign the culture associated with the states' center cell
|
|
||||||
pack.states = pack.states.map((state, index) => {
|
|
||||||
if (index === 0) return state;
|
|
||||||
return {...state, culture: pack.cells.culture[state.center]};
|
|
||||||
});
|
|
||||||
|
|
||||||
TIME && console.timeEnd("updateCulturesForBurgsAndStates");
|
|
||||||
};
|
|
||||||
|
|
||||||
// calculate states data like area, population etc.
|
// calculate states data like area, population etc.
|
||||||
const collectStatistics = () => {
|
const collectStatistics = () => {
|
||||||
TIME && console.time("collectStatistics");
|
TIME && console.time("collectStatistics");
|
||||||
|
|
@ -468,22 +216,22 @@ window.BurgsAndStates = (() => {
|
||||||
const assignColors = () => {
|
const assignColors = () => {
|
||||||
TIME && console.time("assignColors");
|
TIME && console.time("assignColors");
|
||||||
const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2;
|
const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2;
|
||||||
|
const states = pack.states;
|
||||||
|
|
||||||
// assign basic color using greedy coloring algorithm
|
// assign basic color using greedy coloring algorithm
|
||||||
pack.states.forEach(s => {
|
states.forEach(state => {
|
||||||
if (!s.i || s.removed || s.lock) return;
|
if (!state.i || state.removed || state.lock) return;
|
||||||
const neibs = s.neighbors;
|
state.color = colors.find(color => state.neighbors.every(neibStateId => states[neibStateId].color !== color));
|
||||||
s.color = colors.find(c => neibs.every(n => pack.states[n].color !== c));
|
if (!state.color) state.color = getRandomColor();
|
||||||
if (!s.color) s.color = getRandomColor();
|
|
||||||
colors.push(colors.shift());
|
colors.push(colors.shift());
|
||||||
});
|
});
|
||||||
|
|
||||||
// randomize each already used color a bit
|
// randomize each already used color a bit
|
||||||
colors.forEach(c => {
|
colors.forEach(c => {
|
||||||
const sameColored = pack.states.filter(s => s.color === c && !s.lock);
|
const sameColored = states.filter(state => state.color === c && state.i && !state.lock);
|
||||||
sameColored.forEach((s, d) => {
|
sameColored.forEach((state, index) => {
|
||||||
if (!d) return;
|
if (!index) return;
|
||||||
s.color = getMixedColor(s.color);
|
state.color = getMixedColor(state.color);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -869,15 +617,11 @@ window.BurgsAndStates = (() => {
|
||||||
normalizeStates,
|
normalizeStates,
|
||||||
getPoles,
|
getPoles,
|
||||||
assignColors,
|
assignColors,
|
||||||
specifyBurgs,
|
|
||||||
defineBurgFeatures,
|
|
||||||
getType,
|
|
||||||
collectStatistics,
|
collectStatistics,
|
||||||
generateCampaign,
|
generateCampaign,
|
||||||
generateCampaigns,
|
generateCampaigns,
|
||||||
generateDiplomacy,
|
generateDiplomacy,
|
||||||
defineStateForms,
|
defineStateForms,
|
||||||
getFullName,
|
getFullName
|
||||||
updateCultures
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
@ -238,7 +238,7 @@ window.Submap = (function () {
|
||||||
? pack.burgs[s.capital].cell // capital is the best bet
|
? pack.burgs[s.capital].cell // capital is the best bet
|
||||||
: pack.cells.state.findIndex(x => x === i); // otherwise use the first valid cell
|
: pack.cells.state.findIndex(x => x === i); // otherwise use the first valid cell
|
||||||
});
|
});
|
||||||
BurgsAndStates.getPoles();
|
States.getPoles();
|
||||||
|
|
||||||
// transfer provinces, mark provinces without land as removed.
|
// transfer provinces, mark provinces without land as removed.
|
||||||
stage("Porting provinces");
|
stage("Porting provinces");
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
byId("burgsOverviewRefresh").addEventListener("click", refreshBurgsEditor);
|
byId("burgsOverviewRefresh").addEventListener("click", refreshBurgsEditor);
|
||||||
|
byId("burgsGroupsEditorButton").addEventListener("click", openBurgGroupsEditor);
|
||||||
byId("burgsChart").addEventListener("click", showBurgsChart);
|
byId("burgsChart").addEventListener("click", showBurgsChart);
|
||||||
byId("burgsFilterState").addEventListener("change", burgsOverviewAddLines);
|
byId("burgsFilterState").addEventListener("change", burgsOverviewAddLines);
|
||||||
byId("burgsFilterCulture").addEventListener("change", burgsOverviewAddLines);
|
byId("burgsFilterCulture").addEventListener("change", burgsOverviewAddLines);
|
||||||
|
|
@ -302,6 +303,30 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
||||||
if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed");
|
if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openBurgGroupsEditor() {
|
||||||
|
$("#burgGroupsEditor").dialog({
|
||||||
|
title: "Edit Burgs Groups",
|
||||||
|
resizable: false,
|
||||||
|
position: {my: "center", at: "center", of: "svg"},
|
||||||
|
buttons: {
|
||||||
|
Apply: applyMilitaryOptions,
|
||||||
|
Restore: restoreDefaultUnits,
|
||||||
|
Cancel: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
open: function () {
|
||||||
|
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
||||||
|
buttons[0].addEventListener("mousemove", () =>
|
||||||
|
tip("Apply military units settings. <span style='color:#cb5858'>All forces will be recalculated!</span>")
|
||||||
|
);
|
||||||
|
buttons[1].addEventListener("mousemove", () => tip("Add new military unit to the table"));
|
||||||
|
buttons[2].addEventListener("mousemove", () => tip("Restore default military units and settings"));
|
||||||
|
buttons[3].addEventListener("mousemove", () => tip("Close the window without saving the changes"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function showBurgsChart() {
|
function showBurgsChart() {
|
||||||
// build hierarchy tree
|
// build hierarchy tree
|
||||||
const states = pack.states.map(s => {
|
const states = pack.states.map(s => {
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,7 @@ function editDiplomacy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function regenerateRelations() {
|
function regenerateRelations() {
|
||||||
BurgsAndStates.generateDiplomacy();
|
States.generateDiplomacy();
|
||||||
refreshDiplomacyEditor();
|
refreshDiplomacyEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ function addBurg(point) {
|
||||||
const feature = cells.f[cellId];
|
const feature = cells.f[cellId];
|
||||||
|
|
||||||
const population = Math.max(cells.s[cellId] / 3 + i / 1000 + (cellId % 100) / 1000, 0.1);
|
const population = Math.max(cells.s[cellId] / 3 + i / 1000 + (cellId % 100) / 1000, 0.1);
|
||||||
const type = BurgsAndStates.getType(cellId, false);
|
const type = Burgs.getType(cellId, false);
|
||||||
|
|
||||||
// generate emblem
|
// generate emblem
|
||||||
const coa = COA.generate(states[state].coa, 0.25, null, type);
|
const coa = COA.generate(states[state].coa, 0.25, null, type);
|
||||||
|
|
@ -186,7 +186,7 @@ function addBurg(point) {
|
||||||
.attr("dy", `${townSize * -1.5}px`)
|
.attr("dy", `${townSize * -1.5}px`)
|
||||||
.text(name);
|
.text(name);
|
||||||
|
|
||||||
BurgsAndStates.defineBurgFeatures(burg);
|
Burgs.defineBurgFeatures(burg);
|
||||||
|
|
||||||
const newRoute = Routes.connect(cellId);
|
const newRoute = Routes.connect(cellId);
|
||||||
if (newRoute && layerIsOn("toggleRoutes")) {
|
if (newRoute && layerIsOn("toggleRoutes")) {
|
||||||
|
|
|
||||||
|
|
@ -243,13 +243,14 @@ function editHeightmap(options) {
|
||||||
Cultures.generate();
|
Cultures.generate();
|
||||||
Cultures.expand();
|
Cultures.expand();
|
||||||
|
|
||||||
BurgsAndStates.generate();
|
Burgs.generate();
|
||||||
|
States.generate();
|
||||||
Routes.generate();
|
Routes.generate();
|
||||||
Religions.generate();
|
Religions.generate();
|
||||||
BurgsAndStates.defineStateForms();
|
States.defineStateForms();
|
||||||
Provinces.generate();
|
Provinces.generate();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
BurgsAndStates.defineBurgFeatures();
|
Burgs.specifyBurgs();
|
||||||
|
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
Features.specify();
|
Features.specify();
|
||||||
|
|
|
||||||
|
|
@ -371,8 +371,8 @@ function editProvinces() {
|
||||||
layerIsOn("toggleStates") ? drawStates() : toggleStates();
|
layerIsOn("toggleStates") ? drawStates() : toggleStates();
|
||||||
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
|
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
|
||||||
|
|
||||||
BurgsAndStates.collectStatistics();
|
States.collectStatistics();
|
||||||
BurgsAndStates.defineStateForms(newStates);
|
States.defineStateForms(newStates);
|
||||||
drawStateLabels(allStates);
|
drawStateLabels(allStates);
|
||||||
|
|
||||||
// redraw emblems
|
// redraw emblems
|
||||||
|
|
@ -1030,7 +1030,7 @@ function editProvinces() {
|
||||||
// generate emblem
|
// generate emblem
|
||||||
const kinship = burg ? 0.8 : 0.4;
|
const kinship = burg ? 0.8 : 0.4;
|
||||||
const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa;
|
const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa;
|
||||||
const type = BurgsAndStates.getType(center, parent.port);
|
const type = Burgs.getType(center, parent.port);
|
||||||
const coa = COA.generate(parent, kinship, P(0.1), type);
|
const coa = COA.generate(parent, kinship, P(0.1), type);
|
||||||
coa.shield = COA.getShield(c, state);
|
coa.shield = COA.getShield(c, state);
|
||||||
COArenderer.add("province", province, coa, point[0], point[1]);
|
COArenderer.add("province", province, coa, point[0], point[1]);
|
||||||
|
|
|
||||||
|
|
@ -280,16 +280,6 @@ function addStylePreset() {
|
||||||
"font-size",
|
"font-size",
|
||||||
"font-family"
|
"font-family"
|
||||||
],
|
],
|
||||||
"#burgIcons > #cities": [
|
|
||||||
"opacity",
|
|
||||||
"fill",
|
|
||||||
"fill-opacity",
|
|
||||||
"size",
|
|
||||||
"stroke",
|
|
||||||
"stroke-width",
|
|
||||||
"stroke-dasharray",
|
|
||||||
"stroke-linecap"
|
|
||||||
],
|
|
||||||
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
||||||
"#burgLabels > #towns": [
|
"#burgLabels > #towns": [
|
||||||
"opacity",
|
"opacity",
|
||||||
|
|
@ -300,16 +290,6 @@ function addStylePreset() {
|
||||||
"font-size",
|
"font-size",
|
||||||
"font-family"
|
"font-family"
|
||||||
],
|
],
|
||||||
"#burgIcons > #towns": [
|
|
||||||
"opacity",
|
|
||||||
"fill",
|
|
||||||
"fill-opacity",
|
|
||||||
"size",
|
|
||||||
"stroke",
|
|
||||||
"stroke-width",
|
|
||||||
"stroke-dasharray",
|
|
||||||
"stroke-linecap"
|
|
||||||
],
|
|
||||||
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
||||||
"#labels > #states": [
|
"#labels > #states": [
|
||||||
"opacity",
|
"opacity",
|
||||||
|
|
@ -352,6 +332,20 @@ function addStylePreset() {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const burgIconsAttributes = [
|
||||||
|
"opacity",
|
||||||
|
"fill",
|
||||||
|
"fill-opacity",
|
||||||
|
"size",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap"
|
||||||
|
];
|
||||||
|
options.burgs.groups.forEach(group => {
|
||||||
|
attributes[`#burgIcons > g[data-name='${group}']`] = burgIconsAttributes;
|
||||||
|
});
|
||||||
|
|
||||||
for (const selector in attributes) {
|
for (const selector in attributes) {
|
||||||
const el = document.querySelector(selector);
|
const el = document.querySelector(selector);
|
||||||
if (!el) continue;
|
if (!el) continue;
|
||||||
|
|
|
||||||
|
|
@ -154,14 +154,14 @@ function regenerateStates() {
|
||||||
if (!newStates) return;
|
if (!newStates) return;
|
||||||
|
|
||||||
pack.states = newStates;
|
pack.states = newStates;
|
||||||
BurgsAndStates.expandStates();
|
States.expandStates();
|
||||||
BurgsAndStates.normalizeStates();
|
States.normalizeStates();
|
||||||
BurgsAndStates.getPoles();
|
States.getPoles();
|
||||||
BurgsAndStates.collectStatistics();
|
States.collectStatistics();
|
||||||
BurgsAndStates.assignColors();
|
States.assignColors();
|
||||||
BurgsAndStates.generateCampaigns();
|
States.generateCampaigns();
|
||||||
BurgsAndStates.generateDiplomacy();
|
States.generateDiplomacy();
|
||||||
BurgsAndStates.defineStateForms();
|
States.defineStateForms();
|
||||||
Provinces.generate(true);
|
Provinces.generate(true);
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
|
|
||||||
|
|
@ -443,8 +443,7 @@ function regenerateBurgs() {
|
||||||
if (f.port) f.port = 0; // reset features ports counter
|
if (f.port) f.port = 0; // reset features ports counter
|
||||||
});
|
});
|
||||||
|
|
||||||
BurgsAndStates.specifyBurgs();
|
Burgs.specifyBurgs();
|
||||||
BurgsAndStates.defineBurgFeatures();
|
|
||||||
regenerateRoutes();
|
regenerateRoutes();
|
||||||
|
|
||||||
drawBurgIcons();
|
drawBurgIcons();
|
||||||
|
|
@ -503,7 +502,7 @@ function regenerateEmblems() {
|
||||||
const nameByBurg = province.burg && province.name.slice(0, 3) === parent.name.slice(0, 3);
|
const nameByBurg = province.burg && province.name.slice(0, 3) === parent.name.slice(0, 3);
|
||||||
const kinship = dominion ? 0 : nameByBurg ? 0.8 : 0.4;
|
const kinship = dominion ? 0 : nameByBurg ? 0.8 : 0.4;
|
||||||
const culture = pack.cells.culture[province.center];
|
const culture = pack.cells.culture[province.center];
|
||||||
const type = BurgsAndStates.getType(province.center, parent.port);
|
const type = Burgs.getType(province.center, parent.port);
|
||||||
province.coa = COA.generate(parent.coa, kinship, dominion, type);
|
province.coa = COA.generate(parent.coa, kinship, dominion, type);
|
||||||
province.coa.shield = COA.getShield(culture, province.state);
|
province.coa.shield = COA.getShield(culture, province.state);
|
||||||
});
|
});
|
||||||
|
|
@ -521,10 +520,26 @@ function regenerateReligions() {
|
||||||
function regenerateCultures() {
|
function regenerateCultures() {
|
||||||
Cultures.generate();
|
Cultures.generate();
|
||||||
Cultures.expand();
|
Cultures.expand();
|
||||||
BurgsAndStates.updateCultures();
|
|
||||||
Religions.updateCultures();
|
// update culture for states
|
||||||
if (!layerIsOn("toggleCultures")) toggleCultures();
|
pack.states = pack.states.map(state => {
|
||||||
else drawCultures();
|
if (!state.i || state.removed) return state;
|
||||||
|
return {...state, culture: pack.cells.culture[state.center]};
|
||||||
|
});
|
||||||
|
|
||||||
|
// update culture for burgs
|
||||||
|
pack.burgs = pack.burgs.map(burg => {
|
||||||
|
if (!burg.i || burg.removed) return burg;
|
||||||
|
return {...burg, culture: pack.cells.culture[burg.cell]};
|
||||||
|
});
|
||||||
|
|
||||||
|
// update culture for religions
|
||||||
|
pack.religions = pack.religions.map(religion => {
|
||||||
|
if (!religion.i || religion.removed) return religion;
|
||||||
|
return {...religion, culture: pack.cells.culture[religion.center]};
|
||||||
|
});
|
||||||
|
|
||||||
|
layerIsOn("toggleCultures") ? drawCultures() : toggleCultures();
|
||||||
refreshAllEditors();
|
refreshAllEditors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue