mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
feat: split burgs to groups
This commit is contained in:
parent
f51f78a7a6
commit
63898d8fd8
15 changed files with 543 additions and 383 deletions
|
|
@ -5237,7 +5237,7 @@
|
||||||
</slider-input>
|
</slider-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-tip="Set urbanization rate: burgs population relative to all population">
|
<div data-tip="Set urban population modifier. Change to increase or descrese burgs population">
|
||||||
<slider-input id="urbanizationInput" data-stored="urbanization" min=".01" max="5" step=".01" value="1">
|
<slider-input id="urbanizationInput" data-stored="urbanization" min=".01" max="5" step=".01" value="1">
|
||||||
<label>Urbanization rate:</label>
|
<label>Urbanization rate:</label>
|
||||||
</slider-input>
|
</slider-input>
|
||||||
|
|
|
||||||
92
main.js
92
main.js
|
|
@ -108,15 +108,7 @@ terrs.append("g").attr("id", "landHeights");
|
||||||
|
|
||||||
labels.append("g").attr("id", "states");
|
labels.append("g").attr("id", "states");
|
||||||
labels.append("g").attr("id", "addedLabels");
|
labels.append("g").attr("id", "addedLabels");
|
||||||
|
|
||||||
let burgLabels = labels.append("g").attr("id", "burgLabels");
|
let burgLabels = labels.append("g").attr("id", "burgLabels");
|
||||||
burgIcons.append("g").attr("id", "cities");
|
|
||||||
burgLabels.append("g").attr("id", "cities");
|
|
||||||
anchors.append("g").attr("id", "cities");
|
|
||||||
|
|
||||||
burgIcons.append("g").attr("id", "towns");
|
|
||||||
burgLabels.append("g").attr("id", "towns");
|
|
||||||
anchors.append("g").attr("id", "towns");
|
|
||||||
|
|
||||||
// population groups
|
// population groups
|
||||||
population.append("g").attr("id", "rural");
|
population.append("g").attr("id", "rural");
|
||||||
|
|
@ -162,6 +154,29 @@ let customization = 0;
|
||||||
let biomesData = Biomes.getDefault();
|
let biomesData = Biomes.getDefault();
|
||||||
let nameBases = Names.getNameBases(); // cultures-related data
|
let nameBases = Names.getNameBases(); // cultures-related data
|
||||||
|
|
||||||
|
// default options, based on Earth data
|
||||||
|
let options = {
|
||||||
|
pinNotes: false,
|
||||||
|
winds: [225, 45, 225, 315, 135, 315],
|
||||||
|
temperatureEquator: 27,
|
||||||
|
temperatureNorthPole: -30,
|
||||||
|
temperatureSouthPole: -15,
|
||||||
|
stateLabelsMode: "auto",
|
||||||
|
showBurgPreview: true,
|
||||||
|
villageMaxPopulation: 2000,
|
||||||
|
burgs: {
|
||||||
|
groups: Burgs.getDefaultGroups()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create groups for each burg type
|
||||||
|
{
|
||||||
|
for (const {name} of options.burgs.groups) {
|
||||||
|
burgIcons.append("g").attr("id", name);
|
||||||
|
burgLabels.append("g").attr("id", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme
|
let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme
|
||||||
const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation
|
const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation
|
||||||
|
|
||||||
|
|
@ -185,21 +200,6 @@ const onZoom = debounce(function () {
|
||||||
}, 50);
|
}, 50);
|
||||||
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoom);
|
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoom);
|
||||||
|
|
||||||
// default options, based on Earth data
|
|
||||||
let options = {
|
|
||||||
pinNotes: false,
|
|
||||||
winds: [225, 45, 225, 315, 135, 315],
|
|
||||||
temperatureEquator: 27,
|
|
||||||
temperatureNorthPole: -30,
|
|
||||||
temperatureSouthPole: -15,
|
|
||||||
stateLabelsMode: "auto",
|
|
||||||
showBurgPreview: true,
|
|
||||||
villageMaxPopulation: 2000,
|
|
||||||
burgs: {
|
|
||||||
groups: Burgs.getDefaultGroups()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mapCoordinates = {}; // map coordinates on globe
|
let mapCoordinates = {}; // map coordinates on globe
|
||||||
let populationRate = +byId("populationRateInput").value;
|
let populationRate = +byId("populationRateInput").value;
|
||||||
let distanceScale = +byId("distanceScaleInput").value;
|
let distanceScale = +byId("distanceScaleInput").value;
|
||||||
|
|
@ -668,7 +668,7 @@ async function generate(options) {
|
||||||
States.defineStateForms();
|
States.defineStateForms();
|
||||||
Provinces.generate();
|
Provinces.generate();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
Burgs.specifyBurgs();
|
Burgs.specify();
|
||||||
|
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
Features.specify();
|
Features.specify();
|
||||||
|
|
@ -1180,36 +1180,44 @@ function rankCells() {
|
||||||
cells.s = new Int16Array(cells.i.length); // cell suitability array
|
cells.s = new Int16Array(cells.i.length); // cell suitability array
|
||||||
cells.pop = new Float32Array(cells.i.length); // cell population array
|
cells.pop = new Float32Array(cells.i.length); // cell population array
|
||||||
|
|
||||||
const flMean = d3.median(cells.fl.filter(f => f)) || 0,
|
const meanFlux = d3.median(cells.fl.filter(f => f)) || 0;
|
||||||
flMax = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux
|
const maxFlux = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux
|
||||||
const areaMean = d3.mean(cells.area); // to adjust population by cell area
|
const meanArea = d3.mean(cells.area); // to adjust population by cell area
|
||||||
|
|
||||||
|
const scoreMap = {
|
||||||
|
estuary: 15,
|
||||||
|
ocean_coast: 5,
|
||||||
|
save_harbor: 20,
|
||||||
|
freshwater: 30,
|
||||||
|
salt: 10,
|
||||||
|
frozen: 1,
|
||||||
|
dry: -5,
|
||||||
|
sinkhole: -5,
|
||||||
|
lava: -30
|
||||||
|
};
|
||||||
|
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
if (cells.h[i] < 20) continue; // no population in water
|
if (cells.h[i] < 20) continue; // no population in water
|
||||||
let s = +biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability
|
let score = biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability
|
||||||
if (!s) continue; // uninhabitable biomes has 0 suitability
|
if (!score) continue; // uninhabitable biomes has 0 suitability
|
||||||
if (flMean) s += normalize(cells.fl[i] + cells.conf[i], flMean, flMax) * 250; // big rivers and confluences are valued
|
|
||||||
s -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not;
|
if (meanFlux) score += normalize(cells.fl[i] + cells.conf[i], meanFlux, maxFlux) * 250; // big rivers and confluences are valued
|
||||||
|
score -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not;
|
||||||
|
|
||||||
if (cells.t[i] === 1) {
|
if (cells.t[i] === 1) {
|
||||||
if (cells.r[i]) s += 15; // estuary is valued
|
if (cells.r[i]) score += scoreMap.estuary;
|
||||||
const feature = features[cells.f[cells.haven[i]]];
|
const feature = features[cells.f[cells.haven[i]]];
|
||||||
if (feature.type === "lake") {
|
if (feature.type === "lake") {
|
||||||
if (feature.group === "freshwater") s += 30;
|
score += scoreMap[feature.water] || 0;
|
||||||
else if (feature.group == "salt") s += 10;
|
|
||||||
else if (feature.group == "frozen") s += 1;
|
|
||||||
else if (feature.group == "dry") s -= 5;
|
|
||||||
else if (feature.group == "sinkhole") s -= 5;
|
|
||||||
else if (feature.group == "lava") s -= 30;
|
|
||||||
} else {
|
} else {
|
||||||
s += 5; // ocean coast is valued
|
score += scoreMap.ocean_coast;
|
||||||
if (cells.harbor[i] === 1) s += 20; // safe sea harbor is valued
|
if (cells.harbor[i] === 1) score += scoreMap.save_harbor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cells.s[i] = s / 5; // general population rate
|
cells.s[i] = score / 5; // general population rate
|
||||||
// cell rural population is suitability adjusted by cell area
|
// cell rural population is suitability adjusted by cell area
|
||||||
cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / areaMean : 0;
|
cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / meanArea : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME && console.timeEnd("rankCells");
|
TIME && console.timeEnd("rankCells");
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ window.Burgs = (() => {
|
||||||
let quadtree = d3.quadtree();
|
let quadtree = d3.quadtree();
|
||||||
generateCapitals();
|
generateCapitals();
|
||||||
generateTowns();
|
generateTowns();
|
||||||
|
shiftBurgs();
|
||||||
|
|
||||||
pack.burgs = burgs;
|
pack.burgs = burgs;
|
||||||
TIME && console.timeEnd("generateBurgs");
|
TIME && console.timeEnd("generateBurgs");
|
||||||
|
|
@ -108,137 +109,93 @@ window.Burgs = (() => {
|
||||||
spacing *= 0.5;
|
spacing *= 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// define port status and shift ports and burgs on rivers
|
||||||
|
function shiftBurgs() {
|
||||||
|
const {cells, features} = pack;
|
||||||
|
const temp = grid.cells.temp;
|
||||||
|
|
||||||
|
// port is a capital with any harbor OR any burg with a safe harbor
|
||||||
|
const featurePorts = {};
|
||||||
|
for (const burg of burgs) {
|
||||||
|
if (!burg.i || burg.lock) continue;
|
||||||
|
const i = burg.cell;
|
||||||
|
|
||||||
|
const haven = cells.haven[i];
|
||||||
|
const harbor = cells.harbor[i];
|
||||||
|
|
||||||
|
if (haven !== undefined && temp[cells.g[i]] > 0) {
|
||||||
|
const featureId = cells.f[haven];
|
||||||
|
const canBePort = features[featureId].cells > 1 && ((burg.capital && harbor) || harbor === 1);
|
||||||
|
if (canBePort) {
|
||||||
|
if (!featurePorts[featureId]) featurePorts[featureId] = [];
|
||||||
|
featurePorts[featureId].push(burg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// shift ports to the edge of the water body. Only bodies with 2+ ports are considered
|
||||||
|
Object.entries(featurePorts).forEach(([featureId, burgs]) => {
|
||||||
|
if (burgs.length < 2) return;
|
||||||
|
burgs.forEach(burg => {
|
||||||
|
burg.port = featureId;
|
||||||
|
const haven = cells.haven[burg.cell];
|
||||||
|
const [x, y] = getCloseToEdgePoint(burg.cell, haven);
|
||||||
|
burg.x = x;
|
||||||
|
burg.y = y;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// shift non-port river burgs a bit
|
||||||
|
for (const burg of burgs) {
|
||||||
|
if (!burg.i || burg.lock || burg.port || !cells.r[burg.cell]) continue;
|
||||||
|
const cellId = burg.cell;
|
||||||
|
const shift = Math.min(cells.fl[cellId] / 150, 1);
|
||||||
|
burg.x = cellId % 2 ? rn(burg.x + shift, 2) : rn(burg.x - shift, 2);
|
||||||
|
burg.y = cells.r[cellId] % 2 ? rn(burg.y + shift, 2) : rn(burg.y - shift, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 getDefaultGroups = () => [
|
const specify = () => {
|
||||||
{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");
|
TIME && console.time("specifyBurgs");
|
||||||
const {cells, features} = pack;
|
|
||||||
const temp = grid.cells.temp;
|
|
||||||
|
|
||||||
for (const burg of pack.burgs) {
|
pack.burgs.forEach(burg => {
|
||||||
if (!burg.i || burg.lock) continue;
|
if (!burg.i || burg.removed || burg.lock) return;
|
||||||
const i = burg.cell;
|
definePopulation(burg);
|
||||||
|
defineEmblem(burg);
|
||||||
|
defineFeatures(burg);
|
||||||
|
});
|
||||||
|
|
||||||
// asign port status to some coastline burgs with temp > 0 °C
|
const populations = pack.burgs
|
||||||
const haven = cells.haven[i];
|
.filter(b => b.i && !b.removed)
|
||||||
if (haven && temp[cells.g[i]] > 0) {
|
.map(b => b.population)
|
||||||
const f = cells.f[haven]; // water body id
|
.sort((a, b) => a - b); // ascending
|
||||||
// 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)
|
pack.burgs.forEach(burg => {
|
||||||
burg.population = rn(Math.max(cells.s[i] / 8 + burg.i / 1000 + (i % 100) / 1000, 0.1), 3);
|
if (!burg.i || burg.removed || burg.lock) return;
|
||||||
if (burg.capital) burg.population = rn(burg.population * 1.3, 3); // increase capital population
|
defineGroup(burg, populations);
|
||||||
|
});
|
||||||
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");
|
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 getType = (cellId, port) => {
|
||||||
const {cells, features} = pack;
|
const {cells, features} = pack;
|
||||||
|
|
||||||
|
|
@ -261,19 +218,180 @@ window.Burgs = (() => {
|
||||||
return "Generic";
|
return "Generic";
|
||||||
};
|
};
|
||||||
|
|
||||||
const defineBurgFeatures = burg => {
|
function definePopulation(burg) {
|
||||||
const {cells, states} = pack;
|
const cellId = burg.cell;
|
||||||
|
let population = pack.cells.s[cellId] / 5;
|
||||||
|
if (burg.capital) population *= 1.5;
|
||||||
|
const connectivityRate = Routes.getConnectivityRate(cellId);
|
||||||
|
if (connectivityRate) population *= connectivityRate;
|
||||||
|
population *= gauss(1, 1, 0.25, 4, 5); // randomize
|
||||||
|
population += ((burg.i % 100) - (cellId % 100)) / 1000; // unround
|
||||||
|
burg.population = rn(Math.max(population, 0.01), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineEmblem(burg) {
|
||||||
|
burg.type = getType(burg.cell, burg.port);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineFeatures(burg) {
|
||||||
const pop = burg.population;
|
const pop = burg.population;
|
||||||
burg.citadel = Number(burg.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1));
|
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.plaza = Number(
|
||||||
|
Routes.isCrossroad(burg.cell) || (Routes.hasRoad(burg.cell) && P(0.7)) || pop > 20 || (pop > 10 && P(0.8))
|
||||||
|
);
|
||||||
burg.walls = Number(burg.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1));
|
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)));
|
burg.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && burg.walls && P(0.4)));
|
||||||
const religion = cells.religion[burg.cell];
|
const religion = pack.cells.religion[burg.cell];
|
||||||
const theocracy = states[burg.state].form === "Theocracy";
|
const theocracy = pack.states[burg.state].form === "Theocracy";
|
||||||
burg.temple = Number(
|
burg.temple = Number(
|
||||||
(religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
|
(religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
return {generate, getDefaultGroups, specifyBurgs, getType, defineBurgFeatures};
|
const getDefaultGroups = () => [
|
||||||
|
{name: "capitals", active: true, features: {capital: true}, preview: "watabou-city-generator"},
|
||||||
|
{name: "cities", active: true, percentile: 90, population: [5, Infinity], 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, 0.8],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "caravanserais",
|
||||||
|
active: true,
|
||||||
|
features: {port: false, plaza: true},
|
||||||
|
population: [0, 0.8],
|
||||||
|
biomes: [1, 2, 3],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trading_posts",
|
||||||
|
active: true,
|
||||||
|
features: {plaza: true},
|
||||||
|
population: [0, 0.8],
|
||||||
|
biomes: [5, 6, 7, 8, 9, 10, 11, 12],
|
||||||
|
preview: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "villages",
|
||||||
|
active: true,
|
||||||
|
population: [0.1, 2],
|
||||||
|
features: {walls: false},
|
||||||
|
preview: "watabou-village-generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hamlets",
|
||||||
|
active: true,
|
||||||
|
features: {walls: false, plaza: false},
|
||||||
|
population: [0, 0.1],
|
||||||
|
preview: "watabou-village-generator"
|
||||||
|
},
|
||||||
|
{name: "towns", active: true, isDefault: true, preview: "watabou-city-generator"}
|
||||||
|
];
|
||||||
|
|
||||||
|
function defineGroup(burg, populations) {
|
||||||
|
for (const group of options.burgs.groups) {
|
||||||
|
if (!group.active) continue;
|
||||||
|
|
||||||
|
if (group.population) {
|
||||||
|
const [min, max] = group.population;
|
||||||
|
const isFit = burg.population >= min && burg.population <= max;
|
||||||
|
if (!isFit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.features) {
|
||||||
|
const isFit = Object.entries(group.features).every(([feature, value]) => Boolean(burg[feature]) === value);
|
||||||
|
if (!isFit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.biomes) {
|
||||||
|
const isFit = group.biomes.includes(pack.cells.biome[burg.cell]);
|
||||||
|
if (!isFit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.percentile) {
|
||||||
|
const index = populations.indexOf(burg.population);
|
||||||
|
const isFit = index >= Math.floor((populations.length * group.percentile) / 100);
|
||||||
|
if (!isFit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply fitting or default group
|
||||||
|
burg.group = group.name;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add([x, y]) {
|
||||||
|
const {cells} = pack;
|
||||||
|
|
||||||
|
const burgId = pack.burgs.length;
|
||||||
|
const cellId = findCell(x, y);
|
||||||
|
const culture = cells.culture[cellId];
|
||||||
|
const name = Names.getCulture(culture);
|
||||||
|
const state = cells.state[cellId];
|
||||||
|
const feature = cells.f[cellId];
|
||||||
|
|
||||||
|
const burg = {
|
||||||
|
cell: cellId,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
i: burgId,
|
||||||
|
state,
|
||||||
|
culture,
|
||||||
|
name,
|
||||||
|
feature,
|
||||||
|
capital: 0,
|
||||||
|
port: 0
|
||||||
|
};
|
||||||
|
definePopulation(burg);
|
||||||
|
defineEmblem(burg);
|
||||||
|
defineFeatures(burg);
|
||||||
|
|
||||||
|
const populations = pack.burgs
|
||||||
|
.filter(b => b.i && !b.removed)
|
||||||
|
.map(b => b.population)
|
||||||
|
.sort((a, b) => a - b); // ascending
|
||||||
|
defineGroup(burg, populations);
|
||||||
|
|
||||||
|
pack.burgs.push(burg);
|
||||||
|
cells.burg[cellId] = burgId;
|
||||||
|
|
||||||
|
const newRoute = Routes.connect(cellId);
|
||||||
|
if (newRoute && layerIsOn("toggleRoutes")) {
|
||||||
|
const path = Routes.getPath(newRoute);
|
||||||
|
routes
|
||||||
|
.select("#" + newRoute.group)
|
||||||
|
.append("path")
|
||||||
|
.attr("d", path)
|
||||||
|
.attr("id", "route" + newRoute.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBurgIcon(burg);
|
||||||
|
drawBurgLabel(burg);
|
||||||
|
|
||||||
|
return burgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {generate, getDefaultGroups, specify, getType, add};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -968,8 +968,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();
|
||||||
|
|
||||||
|
const groups = Array.from(document.querySelectorAll("#burgIcons > g")).map(g => g.id);
|
||||||
options.burgs = {
|
options.burgs = {
|
||||||
groups: Burgs.getDefaultGroups()
|
groups: groups.map(name => ({name, active: true, preview: null}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1184,7 +1184,7 @@ function addState() {
|
||||||
if (burg && burgs[burg].capital)
|
if (burg && burgs[burg].capital)
|
||||||
return tip("Existing capital cannot be selected as a new state capital! Select other cell", false, "error");
|
return tip("Existing capital cannot be selected as a new state capital! Select other cell", false, "error");
|
||||||
|
|
||||||
if (!burg) burg = addBurg(point); // add new burg
|
if (!burg) burg = Burgs.add(point);
|
||||||
|
|
||||||
const oldState = cells.state[center];
|
const oldState = cells.state[center];
|
||||||
const newState = states.length;
|
const newState = states.length;
|
||||||
|
|
|
||||||
|
|
@ -5,65 +5,47 @@ function drawBurgIcons() {
|
||||||
|
|
||||||
icons.selectAll("circle, use").remove(); // cleanup
|
icons.selectAll("circle, use").remove(); // cleanup
|
||||||
|
|
||||||
// capitals
|
for (const {name} of options.burgs.groups) {
|
||||||
const capitals = pack.burgs.filter(b => b.capital && !b.removed);
|
const burgsInGroup = pack.burgs.filter(b => b.group === name && !b.removed);
|
||||||
const capitalIcons = burgIcons.select("#cities");
|
if (!burgsInGroup.length) continue;
|
||||||
const capitalIcon = capitalIcons.attr("data-icon") || "#icon-circle";
|
|
||||||
const capitalAnchors = anchors.selectAll("#cities");
|
|
||||||
const capitalAnchorsSize = capitalAnchors.attr("size") || 2;
|
|
||||||
|
|
||||||
capitalIcons
|
const g = burgIcons.select("#" + name);
|
||||||
.selectAll("use")
|
if (g.empty()) continue;
|
||||||
.data(capitals)
|
|
||||||
.enter()
|
|
||||||
.append("use")
|
|
||||||
.attr("id", d => "burg" + d.i)
|
|
||||||
.attr("href", capitalIcon)
|
|
||||||
.attr("data-id", d => d.i)
|
|
||||||
.attr("x", d => d.x)
|
|
||||||
.attr("y", d => d.y);
|
|
||||||
|
|
||||||
capitalAnchors
|
const icon = g.attr("data-icon") || "#icon-circle";
|
||||||
.selectAll("use")
|
g.selectAll("use")
|
||||||
.data(capitals.filter(c => c.port))
|
.data(burgsInGroup)
|
||||||
.enter()
|
.enter()
|
||||||
.append("use")
|
.append("use")
|
||||||
.attr("xlink:href", "#icon-anchor")
|
.attr("href", icon)
|
||||||
.attr("data-id", d => d.i)
|
.attr("id", d => "burg" + d.i)
|
||||||
.attr("x", d => rn(d.x - capitalAnchorsSize * 0.47, 2))
|
.attr("data-id", d => d.i)
|
||||||
.attr("y", d => rn(d.y - capitalAnchorsSize * 0.47, 2))
|
.attr("x", d => d.x)
|
||||||
.attr("width", capitalAnchorsSize)
|
.attr("y", d => d.y);
|
||||||
.attr("height", capitalAnchorsSize);
|
|
||||||
|
|
||||||
// towns
|
// capitalAnchors
|
||||||
const towns = pack.burgs.filter(b => b.i && !b.capital && !b.removed);
|
// .selectAll("use")
|
||||||
const townIcons = burgIcons.select("#towns");
|
// .data(capitals.filter(c => c.port))
|
||||||
const townIcon = townIcons.attr("data-icon") || "#icon-circle";
|
// .enter()
|
||||||
const townsAnchors = anchors.selectAll("#towns");
|
// .append("use")
|
||||||
const townsAnchorsSize = townsAnchors.attr("size") || 1;
|
// .attr("xlink:href", "#icon-anchor")
|
||||||
|
// .attr("data-id", d => d.i)
|
||||||
townIcons
|
// .attr("x", d => rn(d.x - capitalAnchorsSize * 0.47, 2))
|
||||||
.selectAll("use")
|
// .attr("y", d => rn(d.y - capitalAnchorsSize * 0.47, 2))
|
||||||
.data(towns)
|
// .attr("width", capitalAnchorsSize)
|
||||||
.enter()
|
// .attr("height", capitalAnchorsSize);
|
||||||
.append("use")
|
}
|
||||||
.attr("id", d => "burg" + d.i)
|
|
||||||
.attr("href", townIcon)
|
|
||||||
.attr("data-id", d => d.i)
|
|
||||||
.attr("x", d => d.x)
|
|
||||||
.attr("y", d => d.y);
|
|
||||||
|
|
||||||
townsAnchors
|
|
||||||
.selectAll("use")
|
|
||||||
.data(towns.filter(c => c.port))
|
|
||||||
.enter()
|
|
||||||
.append("use")
|
|
||||||
.attr("xlink:href", "#icon-anchor")
|
|
||||||
.attr("data-id", d => d.i)
|
|
||||||
.attr("x", d => rn(d.x - townsAnchorsSize * 0.47, 2))
|
|
||||||
.attr("y", d => rn(d.y - townsAnchorsSize * 0.47, 2))
|
|
||||||
.attr("width", townsAnchorsSize)
|
|
||||||
.attr("height", townsAnchorsSize);
|
|
||||||
|
|
||||||
TIME && console.timeEnd("drawBurgIcons");
|
TIME && console.timeEnd("drawBurgIcons");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawBurgIcon(burg) {
|
||||||
|
burgIcons
|
||||||
|
.select("#" + burg.group)
|
||||||
|
.append("use")
|
||||||
|
.attr("href", "#icon-circle")
|
||||||
|
.attr("id", "burg" + burg.i)
|
||||||
|
.attr("data-id", burg.i)
|
||||||
|
.attr("x", burg.x)
|
||||||
|
.attr("y", burg.y);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,35 +5,37 @@ function drawBurgLabels() {
|
||||||
|
|
||||||
burgLabels.selectAll("text").remove(); // cleanup
|
burgLabels.selectAll("text").remove(); // cleanup
|
||||||
|
|
||||||
const capitals = pack.burgs.filter(b => b.capital && !b.removed);
|
for (const {name} of options.burgs.groups) {
|
||||||
const capitalSize = burgIcons.select("#cities").attr("size") || 1;
|
const burgsInGroup = pack.burgs.filter(b => b.group === name && !b.removed);
|
||||||
burgLabels
|
if (!burgsInGroup.length) continue;
|
||||||
.select("#cities")
|
|
||||||
.selectAll("text")
|
|
||||||
.data(capitals)
|
|
||||||
.enter()
|
|
||||||
.append("text")
|
|
||||||
.attr("id", d => "burgLabel" + d.i)
|
|
||||||
.attr("data-id", d => d.i)
|
|
||||||
.attr("x", d => d.x)
|
|
||||||
.attr("y", d => d.y)
|
|
||||||
.attr("dy", `${capitalSize * -1.5}px`)
|
|
||||||
.text(d => d.name);
|
|
||||||
|
|
||||||
const towns = pack.burgs.filter(b => b.i && !b.capital && !b.removed);
|
const labelGroup = burgLabels.select("#" + name);
|
||||||
const townSize = burgIcons.select("#towns").attr("size") || 0.5;
|
if (labelGroup.empty()) continue;
|
||||||
burgLabels
|
|
||||||
.select("#towns")
|
labelGroup
|
||||||
.selectAll("text")
|
.selectAll("text")
|
||||||
.data(towns)
|
.data(burgsInGroup)
|
||||||
.enter()
|
.enter()
|
||||||
.append("text")
|
.append("text")
|
||||||
.attr("id", d => "burgLabel" + d.i)
|
.attr("id", d => "burgLabel" + d.i)
|
||||||
.attr("data-id", d => d.i)
|
.attr("data-id", d => d.i)
|
||||||
.attr("x", d => d.x)
|
.attr("x", d => d.x)
|
||||||
.attr("y", d => d.y)
|
.attr("y", d => d.y)
|
||||||
.attr("dy", `${townSize * -2}px`)
|
.attr("dy", "-0.4em")
|
||||||
.text(d => d.name);
|
.text(d => d.name);
|
||||||
|
}
|
||||||
|
|
||||||
TIME && console.timeEnd("drawBurgLabels");
|
TIME && console.timeEnd("drawBurgLabels");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawBurgLabel(burg) {
|
||||||
|
burgLabels
|
||||||
|
.select("#" + burg.group)
|
||||||
|
.append("text")
|
||||||
|
.attr("id", "burgLabel" + burg.i)
|
||||||
|
.attr("data-id", burg.i)
|
||||||
|
.attr("x", burg.x)
|
||||||
|
.attr("y", burg.y)
|
||||||
|
.attr("dy", "-0.4em")
|
||||||
|
.text(burg.name);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -537,6 +537,26 @@ window.Routes = (function () {
|
||||||
return roadConnections.length > 2;
|
return roadConnections.length > 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const connectivityRates = {
|
||||||
|
roads: 0.2,
|
||||||
|
trails: 0.1,
|
||||||
|
searoutes: 0.2,
|
||||||
|
default: 0.1
|
||||||
|
};
|
||||||
|
|
||||||
|
function getConnectivityRate(cellId) {
|
||||||
|
const connections = pack.cells.routes[cellId];
|
||||||
|
if (!connections) return 0;
|
||||||
|
|
||||||
|
const connectivity = Object.values(connections).reduce((acc, routeId) => {
|
||||||
|
const route = pack.routes.find(route => route.i === routeId);
|
||||||
|
const rate = connectivityRates[route.group] || connectivityRates.default;
|
||||||
|
return acc + rate;
|
||||||
|
}, 0.8);
|
||||||
|
|
||||||
|
return connectivity;
|
||||||
|
}
|
||||||
|
|
||||||
// name generator data
|
// name generator data
|
||||||
const models = {
|
const models = {
|
||||||
roads: {burg_suffix: 3, prefix_suffix: 6, the_descriptor_prefix_suffix: 2, the_descriptor_burg_suffix: 1},
|
roads: {burg_suffix: 3, prefix_suffix: 6, the_descriptor_prefix_suffix: 2, the_descriptor_burg_suffix: 1},
|
||||||
|
|
@ -749,6 +769,7 @@ window.Routes = (function () {
|
||||||
getRoute,
|
getRoute,
|
||||||
hasRoad,
|
hasRoad,
|
||||||
isCrossroad,
|
isCrossroad,
|
||||||
|
getConnectivityRate,
|
||||||
generateName,
|
generateName,
|
||||||
getPath,
|
getPath,
|
||||||
getLength,
|
getLength,
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
||||||
if (pack.cells.burg[cell])
|
if (pack.cells.burg[cell])
|
||||||
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
|
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
|
||||||
|
|
||||||
addBurg(point); // add new burg
|
Burgs.add(point); // add new burg
|
||||||
|
|
||||||
if (d3.event.shiftKey === false) {
|
if (d3.event.shiftKey === false) {
|
||||||
exitAddBurgMode();
|
exitAddBurgMode();
|
||||||
|
|
|
||||||
|
|
@ -128,78 +128,6 @@ function applySorting(headers) {
|
||||||
.forEach(line => list.appendChild(line));
|
.forEach(line => list.appendChild(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
function addBurg(point) {
|
|
||||||
const {cells, states} = pack;
|
|
||||||
const x = rn(point[0], 2);
|
|
||||||
const y = rn(point[1], 2);
|
|
||||||
|
|
||||||
const cellId = findCell(x, y);
|
|
||||||
const i = pack.burgs.length;
|
|
||||||
const culture = cells.culture[cellId];
|
|
||||||
const name = Names.getCulture(culture);
|
|
||||||
const state = cells.state[cellId];
|
|
||||||
const feature = cells.f[cellId];
|
|
||||||
|
|
||||||
const population = Math.max(cells.s[cellId] / 3 + i / 1000 + (cellId % 100) / 1000, 0.1);
|
|
||||||
const type = Burgs.getType(cellId, false);
|
|
||||||
|
|
||||||
// generate emblem
|
|
||||||
const coa = COA.generate(states[state].coa, 0.25, null, type);
|
|
||||||
coa.shield = COA.getShield(culture, state);
|
|
||||||
COArenderer.add("burg", i, coa, x, y);
|
|
||||||
|
|
||||||
const burg = {
|
|
||||||
name,
|
|
||||||
cell: cellId,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
state,
|
|
||||||
i,
|
|
||||||
culture,
|
|
||||||
feature,
|
|
||||||
capital: 0,
|
|
||||||
port: 0,
|
|
||||||
temple: 0,
|
|
||||||
population,
|
|
||||||
coa,
|
|
||||||
type
|
|
||||||
};
|
|
||||||
pack.burgs.push(burg);
|
|
||||||
cells.burg[cellId] = i;
|
|
||||||
|
|
||||||
const townSize = burgIcons.select("#towns").attr("size") || 0.5;
|
|
||||||
burgIcons
|
|
||||||
.select("#towns")
|
|
||||||
.append("circle")
|
|
||||||
.attr("id", "burg" + i)
|
|
||||||
.attr("data-id", i)
|
|
||||||
.attr("cx", x)
|
|
||||||
.attr("cy", y)
|
|
||||||
.attr("r", townSize);
|
|
||||||
burgLabels
|
|
||||||
.select("#towns")
|
|
||||||
.append("text")
|
|
||||||
.attr("id", "burgLabel" + i)
|
|
||||||
.attr("data-id", i)
|
|
||||||
.attr("x", x)
|
|
||||||
.attr("y", y)
|
|
||||||
.attr("dy", `${townSize * -1.5}px`)
|
|
||||||
.text(name);
|
|
||||||
|
|
||||||
Burgs.defineBurgFeatures(burg);
|
|
||||||
|
|
||||||
const newRoute = Routes.connect(cellId);
|
|
||||||
if (newRoute && layerIsOn("toggleRoutes")) {
|
|
||||||
routes
|
|
||||||
.select("#" + newRoute.group)
|
|
||||||
.append("path")
|
|
||||||
.attr("d", Routes.getPath(newRoute))
|
|
||||||
.attr("id", "route" + newRoute.i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function moveBurgToGroup(id, g) {
|
function moveBurgToGroup(id, g) {
|
||||||
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
|
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
|
||||||
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
|
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,7 @@ function editHeightmap(options) {
|
||||||
States.defineStateForms();
|
States.defineStateForms();
|
||||||
Provinces.generate();
|
Provinces.generate();
|
||||||
Provinces.getPoles();
|
Provinces.getPoles();
|
||||||
Burgs.specifyBurgs();
|
Burgs.specify();
|
||||||
|
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
Features.specify();
|
Features.specify();
|
||||||
|
|
|
||||||
|
|
@ -342,8 +342,8 @@ function addStylePreset() {
|
||||||
"stroke-dasharray",
|
"stroke-dasharray",
|
||||||
"stroke-linecap"
|
"stroke-linecap"
|
||||||
];
|
];
|
||||||
options.burgs.groups.forEach(group => {
|
options.burgs.groups.forEach(({name}) => {
|
||||||
attributes[`#burgIcons > g[data-name='${group}']`] = burgIconsAttributes;
|
attributes[`#burgIcons > g.${name}`] = burgIconsAttributes;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const selector in attributes) {
|
for (const selector in attributes) {
|
||||||
|
|
|
||||||
|
|
@ -1125,39 +1125,6 @@ styleScaleBar.on("input", function (event) {
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateElements() {
|
function updateElements() {
|
||||||
// burgIcons to desired size
|
|
||||||
burgIcons.selectAll("g").each(function () {
|
|
||||||
const size = +this.getAttribute("size");
|
|
||||||
d3.select(this)
|
|
||||||
.selectAll("circle")
|
|
||||||
.each(function () {
|
|
||||||
this.setAttribute("r", size);
|
|
||||||
});
|
|
||||||
burgLabels
|
|
||||||
.select("g#" + this.id)
|
|
||||||
.selectAll("text")
|
|
||||||
.each(function () {
|
|
||||||
this.setAttribute("dy", `${size * -1.5}px`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// anchor icons to desired size
|
|
||||||
anchors.selectAll("g").each(function (d) {
|
|
||||||
const size = +this.getAttribute("size");
|
|
||||||
d3.select(this)
|
|
||||||
.selectAll("use")
|
|
||||||
.each(function () {
|
|
||||||
const id = +this.dataset.id;
|
|
||||||
const x = pack.burgs[id].x,
|
|
||||||
y = pack.burgs[id].y;
|
|
||||||
this.setAttribute("x", rn(x - size * 0.47, 2));
|
|
||||||
this.setAttribute("y", rn(y - size * 0.47, 2));
|
|
||||||
this.setAttribute("width", size);
|
|
||||||
this.setAttribute("height", size);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// redraw elements
|
|
||||||
if (layerIsOn("toggleHeight")) drawHeightmap();
|
if (layerIsOn("toggleHeight")) drawHeightmap();
|
||||||
if (legend.selectAll("*").size() && window.redrawLegend) redrawLegend();
|
if (legend.selectAll("*").size() && window.redrawLegend) redrawLegend();
|
||||||
oceanLayers.selectAll("path").remove();
|
oceanLayers.selectAll("path").remove();
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@ function regenerateBurgs() {
|
||||||
.filter(s => s.i && !s.removed && !s.capital)
|
.filter(s => s.i && !s.removed && !s.capital)
|
||||||
.forEach(s => {
|
.forEach(s => {
|
||||||
const [x, y] = cells.p[s.center];
|
const [x, y] = cells.p[s.center];
|
||||||
const burgId = addBurg([x, y]);
|
const burgId = Burgs.add([x, y]);
|
||||||
s.capital = burgId;
|
s.capital = burgId;
|
||||||
s.center = pack.burgs[burgId].cell;
|
s.center = pack.burgs[burgId].cell;
|
||||||
pack.burgs[burgId].capital = 1;
|
pack.burgs[burgId].capital = 1;
|
||||||
|
|
@ -443,7 +443,7 @@ function regenerateBurgs() {
|
||||||
if (f.port) f.port = 0; // reset features ports counter
|
if (f.port) f.port = 0; // reset features ports counter
|
||||||
});
|
});
|
||||||
|
|
||||||
Burgs.specifyBurgs();
|
Burgs.specify();
|
||||||
regenerateRoutes();
|
regenerateRoutes();
|
||||||
|
|
||||||
drawBurgIcons();
|
drawBurgIcons();
|
||||||
|
|
|
||||||
|
|
@ -328,7 +328,7 @@
|
||||||
"data-y": 93,
|
"data-y": 93,
|
||||||
"data-columns": 8
|
"data-columns": 8
|
||||||
},
|
},
|
||||||
"#burgLabels > #cities": {
|
"#burgLabels > g#capitals": {
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#3e3e4b",
|
"fill": "#3e3e4b",
|
||||||
"text-shadow": "white 0px 0px 4px",
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
|
@ -337,7 +337,7 @@
|
||||||
"font-size": 7,
|
"font-size": 7,
|
||||||
"font-family": "Almendra SC"
|
"font-family": "Almendra SC"
|
||||||
},
|
},
|
||||||
"#burgIcons > #cities": {
|
"#burgIcons > g#capitals": {
|
||||||
"data-icon": "#icon-square",
|
"data-icon": "#icon-square",
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#ffffff",
|
"fill": "#ffffff",
|
||||||
|
|
@ -349,14 +349,154 @@
|
||||||
"stroke-linecap": "butt",
|
"stroke-linecap": "butt",
|
||||||
"stroke-linejoin": "round"
|
"stroke-linejoin": "round"
|
||||||
},
|
},
|
||||||
"#anchors > #cities": {
|
"#burgLabels > g#cities": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 5,
|
||||||
|
"font-size": 5,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#cities": {
|
||||||
|
"data-icon": "#icon-circle",
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#ffffff",
|
"fill": "#ffffff",
|
||||||
"size": 2,
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 1.5,
|
||||||
"stroke": "#3e3e4b",
|
"stroke": "#3e3e4b",
|
||||||
"stroke-width": 1.2
|
"stroke-width": 8,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
},
|
},
|
||||||
"#burgLabels > #towns": {
|
"#burgLabels > g#forts": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 2,
|
||||||
|
"font-size": 2,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#forts": {
|
||||||
|
"data-icon": "#icon-triangle",
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 0.7,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 10,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"#burgLabels > g#monasteries": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 2,
|
||||||
|
"font-size": 2,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#monasteries": {
|
||||||
|
"data-icon": "#icon-triangle",
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 0.7,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 10,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"#burgLabels > g#caravanserais": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 2,
|
||||||
|
"font-size": 2,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#caravanserais": {
|
||||||
|
"data-icon": "#icon-triangle",
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 0.7,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 10,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"#burgLabels > g#trading_posts": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 2,
|
||||||
|
"font-size": 2,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#trading_posts": {
|
||||||
|
"data-icon": "#icon-triangle",
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 0.7,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 10,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"#burgLabels > g#villages": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 3,
|
||||||
|
"font-size": 3,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#villages": {
|
||||||
|
"data-icon": "#icon-circle",
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 0.7,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 12,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"#burgLabels > g#hamlets": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"letter-spacing": 0,
|
||||||
|
"data-size": 2,
|
||||||
|
"font-size": 2,
|
||||||
|
"font-family": "Almendra SC"
|
||||||
|
},
|
||||||
|
"#burgIcons > g#hamlets": {
|
||||||
|
"data-icon": "#icon-circle",
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"font-size": 0.5,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 12,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"#burgLabels > g#towns": {
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#3e3e4b",
|
"fill": "#3e3e4b",
|
||||||
"text-shadow": "white 0px 0px 4px",
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
|
@ -365,25 +505,18 @@
|
||||||
"font-size": 4,
|
"font-size": 4,
|
||||||
"font-family": "Almendra SC"
|
"font-family": "Almendra SC"
|
||||||
},
|
},
|
||||||
"#burgIcons > #towns": {
|
"#burgIcons > g#towns": {
|
||||||
"data-icon": "#icon-circle",
|
"data-icon": "#icon-circle",
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#ffffff",
|
"fill": "#ffffff",
|
||||||
"fill-opacity": 0.7,
|
"fill-opacity": 0.7,
|
||||||
"font-size": 1,
|
"font-size": 1,
|
||||||
"stroke": "#3e3e4b",
|
"stroke": "#3e3e4b",
|
||||||
"stroke-width": 10,
|
"stroke-width": 12,
|
||||||
"stroke-dasharray": null,
|
"stroke-dasharray": null,
|
||||||
"stroke-linecap": "butt",
|
"stroke-linecap": "butt",
|
||||||
"stroke-linejoin": "round"
|
"stroke-linejoin": "round"
|
||||||
},
|
},
|
||||||
"#anchors > #towns": {
|
|
||||||
"opacity": 1,
|
|
||||||
"fill": "#ffffff",
|
|
||||||
"size": 1,
|
|
||||||
"stroke": "#3e3e4b",
|
|
||||||
"stroke-width": 1.2
|
|
||||||
},
|
|
||||||
"#labels > #states": {
|
"#labels > #states": {
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#3e3e4b",
|
"fill": "#3e3e4b",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue