diff --git a/index.html b/index.html
index 4a36f3d4..9ac253c8 100644
--- a/index.html
+++ b/index.html
@@ -3329,6 +3329,22 @@
+
-
-
+
@@ -8044,7 +8034,7 @@
-
+
@@ -8061,11 +8051,11 @@
-
+
-
+
@@ -8082,12 +8072,12 @@
-
+
-
+
@@ -8103,7 +8093,7 @@
-
+
diff --git a/main.js b/main.js
index 0fa7f790..efc115cf 100644
--- a/main.js
+++ b/main.js
@@ -185,12 +185,13 @@ const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoomDebouced);
// default options, based on Earth data
let options = {
pinNotes: false,
- showMFCGMap: true,
winds: [225, 45, 225, 315, 135, 315],
temperatureEquator: 27,
temperatureNorthPole: -30,
temperatureSouthPole: -15,
- stateLabelsMode: "auto"
+ stateLabelsMode: "auto",
+ showBurgPreview: true,
+ villageMaxPopulation: 2000
};
let mapCoordinates = {}; // map coordinates on globe
diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js
index 390165dc..f4f6463a 100644
--- a/modules/burgs-and-states.js
+++ b/modules/burgs-and-states.js
@@ -252,13 +252,15 @@ window.BurgsAndStates = (function () {
.filter(b => (newburg ? b.i == newburg.i : b.i && !b.removed))
.forEach(b => {
const pop = b.population;
- b.citadel = b.capital || (pop > 50 && P(0.75)) || P(0.5) ? 1 : 0;
- b.plaza = pop > 50 || (pop > 30 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.25) ? 1 : 0;
- b.walls = b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.2) ? 1 : 0;
- b.shanty = pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4)) ? 1 : 0;
+ 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 = (religion && theocracy) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5)) ? 1 : 0;
+ b.temple = Number(
+ (religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
+ );
});
};
@@ -860,7 +862,7 @@ window.BurgsAndStates = (function () {
}
if (base === 31 && (form === "Empire" || form === "Kingdom")) return "Khanate"; // Mongolian
- if (base === 16 && (form === "Principality" )) return "Beylik"; // Turkic
+ if (base === 16 && form === "Principality") return "Beylik"; // Turkic
if (base === 5 && (form === "Empire" || form === "Kingdom")) return "Tsardom"; // Ruthenian
if (base === 16 && (form === "Empire" || form === "Kingdom")) return "Khaganate"; // Turkic
if (base === 12 && (form === "Kingdom" || form === "Grand Duchy")) return "Shogunate"; // Japanese
diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js
index 51c5ee4c..a77ddd45 100644
--- a/modules/dynamic/auto-update.js
+++ b/modules/dynamic/auto-update.js
@@ -827,4 +827,20 @@ export function resolveVersionConflicts(version) {
});
});
}
+
+ if (version < 1.97) {
+ // v1.97.00 changed MFCG link to an arbitrary preview URL
+ options.villageMaxPopulation = 2000;
+ options.showBurgPreview = options.showMFCGMap;
+ delete options.showMFCGMap;
+
+ pack.burgs.forEach(burg => {
+ if (!burg.i || burg.removed) return;
+
+ if (burg.MFCG) {
+ burg.link = getBurgLink(burg);
+ delete burg.MFCG;
+ }
+ });
+ }
}
diff --git a/modules/io/load.js b/modules/io/load.js
index 3f6abf40..d07679d4 100644
--- a/modules/io/load.js
+++ b/modules/io/load.js
@@ -456,7 +456,7 @@ async function parseLoadedData(data, mapVersion) {
{
// dynamically import and run auto-update script
const versionNumber = parseFloat(params[0]);
- const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.96.00");
+ const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.97.00");
resolveVersionConflicts(versionNumber);
}
diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js
index dd34b38a..a90a11d8 100644
--- a/modules/ui/burg-editor.js
+++ b/modules/ui/burg-editor.js
@@ -21,38 +21,37 @@ function editBurg(id) {
modules.editBurg = true;
// add listeners
- document.getElementById("burgGroupShow").addEventListener("click", showGroupSection);
- document.getElementById("burgGroupHide").addEventListener("click", hideGroupSection);
- document.getElementById("burgSelectGroup").addEventListener("change", changeGroup);
- document.getElementById("burgInputGroup").addEventListener("change", createNewGroup);
- document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput);
- document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
+ byId("burgGroupShow").addEventListener("click", showGroupSection);
+ byId("burgGroupHide").addEventListener("click", hideGroupSection);
+ byId("burgSelectGroup").addEventListener("change", changeGroup);
+ byId("burgInputGroup").addEventListener("change", createNewGroup);
+ byId("burgAddGroup").addEventListener("click", toggleNewGroupInput);
+ byId("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
- document.getElementById("burgName").addEventListener("input", changeName);
- document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom);
- document.getElementById("burgType").addEventListener("input", changeType);
- document.getElementById("burgCulture").addEventListener("input", changeCulture);
- document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture);
- document.getElementById("burgPopulation").addEventListener("change", changePopulation);
+ byId("burgName").addEventListener("input", changeName);
+ byId("burgNameReRandom").addEventListener("click", generateNameRandom);
+ byId("burgType").addEventListener("input", changeType);
+ byId("burgCulture").addEventListener("input", changeCulture);
+ byId("burgNameReCulture").addEventListener("click", generateNameCulture);
+ byId("burgPopulation").addEventListener("change", changePopulation);
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature));
- document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed);
- document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed);
- document.getElementById("addCustomMFCGBurgLink").addEventListener("click", addCustomMfcgLink);
+ byId("burgLinkOpen").addEventListener("click", openBurgLink);
+ byId("burgLinkEdit").addEventListener("click", changeBurgLink);
- document.getElementById("burgStyleShow").addEventListener("click", showStyleSection);
- document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection);
- document.getElementById("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle);
- document.getElementById("burgEditIconStyle").addEventListener("click", editGroupIconStyle);
- document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
+ byId("burgStyleShow").addEventListener("click", showStyleSection);
+ byId("burgStyleHide").addEventListener("click", hideStyleSection);
+ byId("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle);
+ byId("burgEditIconStyle").addEventListener("click", editGroupIconStyle);
+ byId("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
- document.getElementById("burgEmblem").addEventListener("click", openEmblemEdit);
- document.getElementById("burgToggleMFCGMap").addEventListener("click", toggleMFCGMap);
- document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit);
- document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg);
- document.getElementById("burglLegend").addEventListener("click", editBurgLegend);
- document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton);
- document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg);
- document.getElementById("burgTemperatureGraph").addEventListener("click", showTemperatureGraph);
+ byId("burgEmblem").addEventListener("click", openEmblemEdit);
+ byId("burgTogglePreview").addEventListener("click", toggleBurgPreview);
+ byId("burgEditEmblem").addEventListener("click", openEmblemEdit);
+ byId("burgRelocate").addEventListener("click", toggleRelocateBurg);
+ byId("burglLegend").addEventListener("click", editBurgLegend);
+ byId("burgLock").addEventListener("click", toggleBurgLockButton);
+ byId("burgRemove").addEventListener("click", removeSelectedBurg);
+ byId("burgTemperatureGraph").addEventListener("click", showTemperatureGraph);
function updateBurgValues() {
const id = +elSelected.attr("data-id");
@@ -60,46 +59,46 @@ function editBurg(id) {
const province = pack.cells.province[b.cell];
const provinceName = province ? pack.provinces[province].fullName + ", " : "";
const stateName = pack.states[b.state].fullName || pack.states[b.state].name;
- document.getElementById("burgProvinceAndState").innerHTML = provinceName + stateName;
+ byId("burgProvinceAndState").innerHTML = provinceName + stateName;
- document.getElementById("burgName").value = b.name;
- document.getElementById("burgType").value = b.type || "Generic";
- document.getElementById("burgPopulation").value = rn(b.population * populationRate * urbanization);
- document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
+ byId("burgName").value = b.name;
+ byId("burgType").value = b.type || "Generic";
+ byId("burgPopulation").value = rn(b.population * populationRate * urbanization);
+ byId("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
// update list and select culture
- const cultureSelect = document.getElementById("burgCulture");
+ const cultureSelect = byId("burgCulture");
cultureSelect.options.length = 0;
const cultures = pack.cultures.filter(c => !c.removed);
cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture)));
const temperature = grid.cells.temp[pack.cells.g[b.cell]];
- document.getElementById("burgTemperature").innerHTML = convertTemperature(temperature);
- document.getElementById("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature);
- document.getElementById("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
+ byId("burgTemperature").innerHTML = convertTemperature(temperature);
+ byId("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature);
+ byId("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
// toggle features
- if (b.capital) document.getElementById("burgCapital").classList.remove("inactive");
- else document.getElementById("burgCapital").classList.add("inactive");
- if (b.port) document.getElementById("burgPort").classList.remove("inactive");
- else document.getElementById("burgPort").classList.add("inactive");
- if (b.citadel) document.getElementById("burgCitadel").classList.remove("inactive");
- else document.getElementById("burgCitadel").classList.add("inactive");
- if (b.walls) document.getElementById("burgWalls").classList.remove("inactive");
- else document.getElementById("burgWalls").classList.add("inactive");
- if (b.plaza) document.getElementById("burgPlaza").classList.remove("inactive");
- else document.getElementById("burgPlaza").classList.add("inactive");
- if (b.temple) document.getElementById("burgTemple").classList.remove("inactive");
- else document.getElementById("burgTemple").classList.add("inactive");
- if (b.shanty) document.getElementById("burgShanty").classList.remove("inactive");
- else document.getElementById("burgShanty").classList.add("inactive");
+ if (b.capital) byId("burgCapital").classList.remove("inactive");
+ else byId("burgCapital").classList.add("inactive");
+ if (b.port) byId("burgPort").classList.remove("inactive");
+ else byId("burgPort").classList.add("inactive");
+ if (b.citadel) byId("burgCitadel").classList.remove("inactive");
+ else byId("burgCitadel").classList.add("inactive");
+ if (b.walls) byId("burgWalls").classList.remove("inactive");
+ else byId("burgWalls").classList.add("inactive");
+ if (b.plaza) byId("burgPlaza").classList.remove("inactive");
+ else byId("burgPlaza").classList.add("inactive");
+ if (b.temple) byId("burgTemple").classList.remove("inactive");
+ else byId("burgTemple").classList.add("inactive");
+ if (b.shanty) byId("burgShanty").classList.remove("inactive");
+ else byId("burgShanty").classList.add("inactive");
//toggle lock
updateBurgLockIcon();
// select group
const group = elSelected.node().parentNode.id;
- const select = document.getElementById("burgSelectGroup");
+ const select = byId("burgSelectGroup");
select.options.length = 0; // remove all options
burgLabels.selectAll("g").each(function () {
@@ -109,20 +108,13 @@ function editBurg(id) {
// set emlem image
const coaID = "burgCOA" + id;
COArenderer.trigger(coaID, b.coa);
- document.getElementById("burgEmblem").setAttribute("href", "#" + coaID);
+ byId("burgEmblem").setAttribute("href", "#" + coaID);
- if (options.showMFCGMap) {
- document.getElementById("mfcgPreviewSection").style.display = "block";
- updateMFCGFrame(b);
-
- if (b.link) {
- document.getElementById("mfcgBurgSeedSection").style.display = "none";
- } else {
- document.getElementById("mfcgBurgSeedSection").style.display = "inline-block";
- document.getElementById("mfcgBurgSeed").value = getBurgSeed(b);
- }
+ if (options.showBurgPreview) {
+ byId("burgPreviewSection").style.display = "block";
+ updateBurgPreview(b);
} else {
- document.getElementById("mfcgPreviewSection").style.display = "none";
+ byId("burgPreviewSection").style.display = "none";
}
}
@@ -141,15 +133,15 @@ function editBurg(id) {
function showGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
- document.getElementById("burgGroupSection").style.display = "inline-block";
+ byId("burgGroupSection").style.display = "inline-block";
}
function hideGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
- document.getElementById("burgGroupSection").style.display = "none";
- document.getElementById("burgInputGroup").style.display = "none";
- document.getElementById("burgInputGroup").value = "";
- document.getElementById("burgSelectGroup").style.display = "inline-block";
+ byId("burgGroupSection").style.display = "none";
+ byId("burgInputGroup").style.display = "none";
+ byId("burgInputGroup").value = "";
+ byId("burgSelectGroup").style.display = "inline-block";
}
function changeGroup() {
@@ -178,7 +170,7 @@ function editBurg(id) {
.replace(/ /g, "_")
.replace(/[^\w\s]/gi, "");
- if (document.getElementById(group)) {
+ if (byId(group)) {
tip("Element with this id already exists. Please provide a unique name", false, "error");
return;
}
@@ -206,10 +198,10 @@ function editBurg(id) {
// just rename if only 1 element left
const count = elSelected.node().parentNode.childElementCount;
if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) {
- document.getElementById("burgSelectGroup").selectedOptions[0].remove();
- document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true));
+ byId("burgSelectGroup").selectedOptions[0].remove();
+ byId("burgSelectGroup").options.add(new Option(group, group, false, true));
toggleNewGroupInput();
- document.getElementById("burgInputGroup").value = "";
+ byId("burgInputGroup").value = "";
labelG.id = group;
iconG.id = group;
if (anchor) anchorG.id = group;
@@ -217,9 +209,9 @@ function editBurg(id) {
}
// create new groups
- document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true));
+ byId("burgSelectGroup").options.add(new Option(group, group, false, true));
toggleNewGroupInput();
- document.getElementById("burgInputGroup").value = "";
+ byId("burgInputGroup").value = "";
addBurgsGroup(group);
moveBurgToGroup(id, group);
@@ -300,7 +292,10 @@ function editBurg(id) {
function changePopulation() {
const id = +elSelected.attr("data-id");
+ const burg = pack.burgs[id];
+
pack.burgs[id].population = rn(burgPopulation.value / populationRate / urbanization, 4);
+ updateBurgPreview(burg);
}
function toggleFeature() {
@@ -314,9 +309,9 @@ function editBurg(id) {
if (burg[feature]) this.classList.remove("inactive");
else if (!burg[feature]) this.classList.add("inactive");
- if (burg.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block";
- else document.getElementById("burgEditAnchorStyle").style.display = "none";
- updateMFCGFrame(burg);
+ if (burg.port) byId("burgEditAnchorStyle").style.display = "inline-block";
+ else byId("burgEditAnchorStyle").style.display = "none";
+ updateBurgPreview(burg);
}
function toggleBurgLockButton() {
@@ -331,22 +326,22 @@ function editBurg(id) {
const id = +elSelected.attr("data-id");
const b = pack.burgs[id];
if (b.lock) {
- document.getElementById("burgLock").classList.remove("icon-lock-open");
- document.getElementById("burgLock").classList.add("icon-lock");
+ byId("burgLock").classList.remove("icon-lock-open");
+ byId("burgLock").classList.add("icon-lock");
} else {
- document.getElementById("burgLock").classList.remove("icon-lock");
- document.getElementById("burgLock").classList.add("icon-lock-open");
+ byId("burgLock").classList.remove("icon-lock");
+ byId("burgLock").classList.add("icon-lock-open");
}
}
function showStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
- document.getElementById("burgStyleSection").style.display = "inline-block";
+ byId("burgStyleSection").style.display = "inline-block";
}
function hideStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
- document.getElementById("burgStyleSection").style.display = "none";
+ byId("burgStyleSection").style.display = "none";
}
function editGroupLabelStyle() {
@@ -364,39 +359,38 @@ function editBurg(id) {
editStyle("anchors", g);
}
- function updateMFCGFrame(burg) {
- const mfcgURL = getMFCGlink(burg);
- document.getElementById("mfcgPreview").setAttribute("src", mfcgURL + "&preview=1");
- document.getElementById("mfcgLink").setAttribute("href", mfcgURL);
+ function updateBurgPreview(burg) {
+ const src = getBurgLink(burg) + "&preview=1";
+
+ // recreate object to force reload (Chrome bug)
+ const container = byId("burgPreviewObject");
+ container.innerHTML = "";
+ const object = document.createElement("object");
+ object.style.width = "100%";
+ object.data = src;
+ container.insertBefore(object, null);
}
- function changeSeed() {
+ function openBurgLink() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
- const burgSeed = +this.value;
- burg.MFCG = burgSeed;
- updateMFCGFrame(burg);
+
+ openURL(getBurgLink(burg));
}
- function randomizeSeed() {
+ function changeBurgLink() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
- const burgSeed = rand(1e9 - 1);
- burg.MFCG = burgSeed;
- updateMFCGFrame(burg);
- document.getElementById("mfcgBurgSeed").value = burgSeed;
- }
- function addCustomMfcgLink() {
- const id = +elSelected.attr("data-id");
- const burg = pack.burgs[id];
- const message =
- "Enter custom link to the burg map. It can be a link to Medieval Fantasy City Generator or other tool. Keep empty to use MFCG seed";
- prompt(message, {default: burg.link || "", required: false}, link => {
- if (link) burg.link = link;
- else delete burg.link;
- updateMFCGFrame(burg);
- });
+ prompt(
+ "Provide custom link to the burg map. It can be a link to Medieval Fantasy City Generator, a different tool, or just an image. Leave empty to use the default map",
+ {default: getBurgLink(burg), required: false},
+ link => {
+ if (link) burg.link = link;
+ else delete burg.link;
+ updateBurgPreview(burg);
+ }
+ );
}
function openEmblemEdit() {
@@ -405,16 +399,16 @@ function editBurg(id) {
editEmblem("burg", "burgCOA" + id, burg);
}
- function toggleMFCGMap() {
- options.showMFCGMap = !options.showMFCGMap;
- document.getElementById("mfcgPreviewSection").style.display = options.showMFCGMap ? "block" : "none";
- document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap ? "icon-map" : "icon-map-o";
+ function toggleBurgPreview() {
+ options.showBurgPreview = !options.showBurgPreview;
+ byId("burgPreviewSection").style.display = options.showBurgPreview ? "block" : "none";
+ byId("burgTogglePreview").className = options.showBurgPreview ? "icon-map" : "icon-map-o";
}
function toggleRelocateBurg() {
- const toggler = document.getElementById("toggleCells");
- document.getElementById("burgRelocate").classList.toggle("pressed");
- if (document.getElementById("burgRelocate").classList.contains("pressed")) {
+ const toggler = byId("toggleCells");
+ byId("burgRelocate").classList.toggle("pressed");
+ if (byId("burgRelocate").classList.contains("pressed")) {
viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick);
tip("Click on map to relocate burg. Hold Shift for continuous move", true);
if (!layerIsOn("toggleCells")) {
@@ -534,7 +528,7 @@ function editBurg(id) {
}
function closeBurgEditor() {
- document.getElementById("burgRelocate").classList.remove("pressed");
+ byId("burgRelocate").classList.remove("pressed");
burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false);
unselect();
}
diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js
index d0ad25c2..561722ce 100644
--- a/modules/ui/burgs-overview.js
+++ b/modules/ui/burgs-overview.js
@@ -514,7 +514,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
data += b.temple ? "temple," : ",";
data += b.shanty ? "shanty town," : ",";
data += b.coa ? JSON.stringify(b.coa).replace(/"/g, "").replace(/,/g, ";") + "," : ",";
- data += getMFCGlink(b);
+ data += getBurgLink(b);
data += "\n";
});
diff --git a/modules/ui/editors.js b/modules/ui/editors.js
index 7ebed41d..58c1c18b 100644
--- a/modules/ui/editors.js
+++ b/modules/ui/editors.js
@@ -288,16 +288,20 @@ function togglePort(burg) {
.attr("height", size);
}
-function getBurgSeed(burg) {
- return burg.MFCG || Number(`${seed}${String(burg.i).padStart(4, 0)}`);
-}
-
-function getMFCGlink(burg) {
+function getBurgLink(burg) {
if (burg.link) return burg.link;
+ const population = burg.population * populationRate * urbanization;
+ if (population >= options.villageMaxPopulation || burg.citadel || burg.walls || burg.temple || burg.shanty)
+ return createMfcgLink(burg);
+
+ return createVillageGeneratorLink(burg);
+}
+
+function createMfcgLink(burg) {
const {cells} = pack;
const {i, name, population: burgPopulation, cell} = burg;
- const seed = getBurgSeed(burg);
+ const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0);
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 6, 100);
@@ -305,11 +309,19 @@ function getMFCGlink(burg) {
const river = cells.r[cell] ? 1 : 0;
const coast = Number(burg.port > 0);
- const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : null;
+ const sea = (() => {
+ if (!coast || !cells.haven[cell]) return null;
+
+ // calculate see direction: 0 = south, 0.5 = west, 1 = north, 1.5 = east
+ const p1 = cells.p[cell];
+ const p2 = cells.p[cells.haven[cell]];
+ let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
+ if (deg < 0) deg += 360;
+ return rn(normalize(deg, 0, 360) * 2, 2);
+ })();
- const biome = cells.biome[cell];
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
- const farms = +arableBiomes.includes(biome);
+ const farms = +arableBiomes.includes(cells.biome[cell]);
const citadel = +burg.citadel;
const urban_castle = +(citadel && each(2)(i));
@@ -321,19 +333,12 @@ function getMFCGlink(burg) {
const temple = +burg.temple;
const shantytown = +burg.shanty;
- function getSeaDirections(i) {
- const p1 = cells.p[i];
- const p2 = cells.p[cells.haven[i]];
- let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
- if (deg < 0) deg += 360;
- return rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east
- }
-
- const parameters = {
+ const url = new URL("https://watabou.github.io/city-generator/");
+ url.search = new URLSearchParams({
name,
population,
size,
- seed,
+ seed: burgSeed,
river,
coast,
farms,
@@ -345,14 +350,60 @@ function getMFCGlink(burg) {
walls,
shantytown,
gates: -1
- };
- const url = new URL("https://watabou.github.io/city-generator/");
- url.search = new URLSearchParams(parameters);
+ });
if (sea) url.searchParams.append("sea", sea);
return url.toString();
}
+function createVillageGeneratorLink(burg) {
+ const {cells, features} = pack;
+ const {i, population, cell} = burg;
+
+ const pop = rn(population * populationRate * urbanization);
+ const burgSeed = seed + String(i).padStart(4, 0);
+ const tags = [];
+
+ if (cells.r[cell] && cells.haven[cell]) tags.push("estuary");
+ else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district");
+ else if (burg.port) tags.push("coast");
+ else if (cells.conf[cell]) tags.push("confluence");
+ else if (cells.r[cell]) tags.push("river");
+ else if (pop < 200 && each(4)(cell)) tags.push("pond");
+
+ const roadsAround = cells.c[cell].filter(c => cells.h[c] >= 20 && cells.road[c]).length;
+ if (roadsAround > 1) tags.push("highway");
+ else if (roadsAround === 1) tags.push("dead end");
+ else tags.push("isolated");
+
+ const biome = cells.biome[cell];
+ const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
+ if (!arableBiomes.includes(biome)) tags.push("uncultivated");
+ else if (each(6)(cell)) tags.push("farmland");
+
+ const temp = grid.cells.temp[cells.g[cell]];
+ if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards");
+
+ if (!burg.plaza) tags.push("no square");
+
+ if (pop < 100) tags.push("sparse");
+ else if (pop > 300) tags.push("dense");
+
+ const width = (() => {
+ if (pop > 1500) return 1600;
+ if (pop > 1000) return 1400;
+ if (pop > 500) return 1000;
+ if (pop > 200) return 800;
+ if (pop > 100) return 600;
+ return 400;
+ })();
+ const height = rn(width / 2.2);
+
+ const url = new URL("https://watabou.github.io/village-generator/");
+ url.search = new URLSearchParams({pop, name: "", seed: burgSeed, width, height, tags});
+ return url.toString();
+}
+
// draw legend box
function drawLegend(name, data) {
legend.selectAll("*").remove(); // fully redraw every time
diff --git a/versioning.js b/versioning.js
index bf163fcc..6b7cc11d 100644
--- a/versioning.js
+++ b/versioning.js
@@ -1,7 +1,7 @@
"use strict";
// version and caching control
-const version = "1.96.07"; // generator version, update each time
+const version = "1.97.00"; // generator version, update each time
{
document.title += " v" + version;
@@ -28,6 +28,7 @@ const version = "1.96.07"; // generator version, update each time
Latest changes:
+ - Preview villages map
- Ability to render ocean heightmap
- Scale bar styling features
- Vignette visual layer and vignette styling options