diff --git a/index.css b/index.css
index fc66801c..59ad26b5 100644
--- a/index.css
+++ b/index.css
@@ -516,7 +516,7 @@ p {
}
#customizeOptions {
- margin: 2px 0;
+ margin: 2px 0 6px 0;
}
#tooltip {
diff --git a/index.html b/index.html
index 23f5b13f..a239d0a0 100644
--- a/index.html
+++ b/index.html
@@ -31,8 +31,8 @@
-
-
+
+
@@ -502,7 +502,7 @@
@@ -680,7 +680,7 @@
-
+
diff --git a/script.js b/script.js
index 4a9303fc..d1b56021 100644
--- a/script.js
+++ b/script.js
@@ -1235,23 +1235,28 @@ function fantasyMap() {
}
// recalculate Voronoi Graph to pack cells
- function reGraph() {
+ function reGraph(noChange) {
console.time("reGraph");
const tempCells = [], newPoints = []; // to store new data
// get average precipitation based on graph size
const avPrec = precInput.value / 5000;
+ const smallLakesMax = 500;
+ let smallLakes = 0;
const evaporation = 2;
cells.map(function(i) {
let height = Math.trunc(i.height * 100) / 100;
+ const pit = i.pit;
const ctype = i.ctype;
if (ctype !== -1 && ctype !== -2 && height < 0.2) return; // exclude all depp ocean points
const x = rn(i.data[0], 1), y = rn(i.data[1], 1);
const fn = i.fn;
const harbor = i.harbor;
let lake = i.lake;
- if (!lake && i.pit > evaporation && ctype !== 2) {
+ // mark potential cells for small lakes to add additional point there
+ // not for custom map if "changeHeights" is not alkowed
+ if (!noChange && smallLakes < smallLakesMax && !lake && pit > evaporation && ctype !== 2) {
lake = 2;
- height = Math.trunc(height * 100 - i.pit) / 100;
+ smallLakes++;
}
if (height > 1) height = 1;
if (height < 0) height = 0;
@@ -1260,7 +1265,7 @@ function fantasyMap() {
let copy = $.grep(newPoints, function(e) {return (e[0] == x && e[1] == y);});
if (!copy.length) {
newPoints.push([x, y]);
- tempCells.push({index:tempCells.length, data:[x, y], height, ctype, fn, harbor, lake, region, culture});
+ tempCells.push({index:tempCells.length, data:[x, y], height, pit, ctype, fn, harbor, lake, region, culture});
}
// add additional points for cells along coast
if (ctype === 2 || ctype === -1) {
@@ -1274,13 +1279,12 @@ function fantasyMap() {
copy = $.grep(newPoints, function(e) {return e[0] === x1 && e[1] === y1;});
if (copy.length) return;
newPoints.push([x1, y1]);
- tempCells.push({index:tempCells.length, data:[x1, y1], height, ctype, fn, harbor, lake, region, culture});
+ tempCells.push({index:tempCells.length, data:[x1, y1], height, pit, ctype, fn, harbor, lake, region, culture});
};
});
}
if (lake === 2) { // add potential small lakes
//debug.append("circle").attr("r", 0.3).attr("cx", x).attr("cy", y).attr("fill", "blue");
- height = Math.trunc(height * 100 + 1) / 100;
polygons[i.index].forEach(function(e) {
if (Math.random() > 0.8) return;
let rnd = Math.random() * 0.6 + 0.8;
@@ -1291,7 +1295,7 @@ function fantasyMap() {
if (copy.length) return;
//debug.append("circle").attr("r", 0.2).attr("cx", x1).attr("cy", y1).attr("fill", "red");
newPoints.push([x1, y1]);
- tempCells.push({index:tempCells.length, data:[x1, y1], height, ctype, fn, region, culture});
+ tempCells.push({index:tempCells.length, data:[x1, y1], height, pit, ctype, fn, region, culture});
});
}
});
@@ -1389,7 +1393,7 @@ function fantasyMap() {
let landCells = 0;
cells.map(function(c) {
heights.push(c.height);
- if (c.height >= 0.2) {landCells++;}
+ if (c.height >= 0.2) landCells++;
});
history = history.slice(0, historyStage);
history[historyStage] = heights;
@@ -1399,8 +1403,6 @@ function fantasyMap() {
var elevationAverage = rn(d3.mean(heights), 2);
var landRatio = rn(landCells / cells.length * 100);
landmassCounter.innerHTML = landCells + " (" + landRatio + "%); Average Elevation: " + elevationAverage;
- if (landCells > 100) {$("#getMap").attr("disabled", false).removeClass("buttonoff").addClass("glow");}
- else {$("#getMap").attr("disabled", true).addClass("buttonoff").removeClass("glow");}
// if perspective is displayed, update it
if ($("#perspectivePanel").is(":visible")) {drawPerspective();}
}
@@ -1822,7 +1824,12 @@ function fantasyMap() {
}
function restoreCustomHeights() {
- land.forEach(function(l) {if (l.pit) rn(l.height -= l.pit / 50, 2);});
+ land.forEach(function(l) {
+ if (l.pit) {
+ l.height = Math.trunc(l.height * 100 - l.pit * 2) / 100;
+ if (l.height < 0.2) l.height = 0.2;
+ }
+ });
}
function flux() {
@@ -3012,15 +3019,7 @@ function fantasyMap() {
$("#burgSelectGroup").change(function() {
const id = +elSelected.attr("data-id");
const g = this.value;
- $("#burgIcons [data-id=" + id + "]").detach().appendTo($("#burgIcons > #"+g));
- $("#burgLabels [data-id=" + id + "]").detach().appendTo($("#burgLabels > #"+g));
- // special case for port icons (anchors)
- if (g === "towns" || g === "capitals") {
- const el = $("#icons g[id*='anchors'] [data-id=" + id + "]");
- if (!el.length) return;
- const to = g === "towns" ? $("#town-anchors") : $("#capital-anchors");
- el.detach().appendTo(to);
- }
+ moveBurgToGroup(id, g);
});
$("#burgInputGroup").change(function() {
@@ -3215,22 +3214,14 @@ function fantasyMap() {
if (states[state] === undefined) return;
const capital = states[manors[id].region] ? id === states[manors[id].region].capital ? 0 : 1 : 1;
if (capital && states[state].capital !== "select") {
- // move oldCapital to burg
+ // move oldCapital to a town group
const oldCapital = states[state].capital;
- $("#burgIcons [data-id=" + oldCapital + "]").detach().appendTo($("#burgIcons > #towns"));
- $("#burgLabels [data-id=" + oldCapital + "]").detach().appendTo($("#burgLabels > #towns"));
- $("#icons #capital-anchors [data-id=" + oldCapital + "]").detach().appendTo($("#town-anchors"));
+ moveBurgToGroup(oldCapital, "towns");
}
states[state].capital = capital ? id : "select";
d3.select("#burgToggleCapital").classed("pressed", capital);
const g = capital ? "capitals" : "towns";
- $("#burgIcons [data-id=" + id + "]").detach().appendTo($("#burgIcons > #"+g));
- $("#burgLabels [data-id=" + id + "]").detach().appendTo($("#burgLabels > #"+g));
- const el = $("#icons g[id*='anchors'] [data-id=" + id + "]");
- updateCountryEditors();
- if (!el.length) return;
- const to = g === "towns" ? $("#town-anchors") : $("#capital-anchors");
- el.detach().appendTo(to);
+ moveBurgToGroup(id, g);
});
$("#burgTogglePort").click(function() {
@@ -3315,14 +3306,14 @@ function fantasyMap() {
}
const x = rn(point[0], 2), y = rn(point[1], 2);
- burgIcons.select("circle[data-id='"+i+"']").attr("cx", x).attr("cy", y);
- burgLabels.select("text[data-id='"+i+"']").attr("x", x).attr("y", y);
+ burgIcons.select("circle[data-id='"+i+"']").attr("transform", null).attr("cx", x).attr("cy", y);
+ burgLabels.select("text[data-id='"+i+"']").attr("transform", null).attr("x", x).attr("y", y);
const anchor = icons.select("use[data-id='"+i+"']");
if (anchor.size()) {
const size = anchor.attr("width");
const xa = rn(x - size * 0.47, 2);
const ya = rn(y - size * 0.47, 2);
- icons.select("use[data-id='"+i+"']").attr("x", xa).attr("y", ya);
+ anchor.attr("transform", null).attr("x", xa).attr("y", ya);
}
cells[index].manor = i;
cells[manors[i].cell].manor = undefined;
@@ -3359,6 +3350,24 @@ function fantasyMap() {
});
}
+ // generic function to move any burg to any group
+ function moveBurgToGroup(id, g) {
+ $("#burgLabels [data-id=" + id + "]").detach().appendTo($("#burgLabels > #"+g));
+ $("#burgIcons [data-id=" + id + "]").detach().appendTo($("#burgIcons > #"+g));
+ const rSize = $("#burgIcons > #"+g).attr("size");
+ $("#burgIcons [data-id=" + id + "]").attr("r", rSize);
+ const el = $("#icons g[id*='anchors'] [data-id=" + id + "]");
+ if (el.length) {
+ const to = g === "towns" ? $("#town-anchors") : $("#capital-anchors");
+ el.detach().appendTo(to);
+ const useSize = to.attr("size");
+ const x = rn(manors[id].x - useSize * 0.47, 2);
+ const y = rn(manors[id].y - useSize * 0.47, 2);
+ el.attr("x", x).attr("y", y).attr("width", useSize).attr("height", useSize);
+ }
+ updateCountryEditors();
+ }
+
// generate cultures for a new map based on options and namesbase
function generateCultures() {
const count = +culturesInput.value;
@@ -4822,26 +4831,36 @@ function fantasyMap() {
}
// Complete the map for the "customize" mode
- function getMap(keepData) {
+ function getMap() {
+ if (customization !== 1) {
+ tip('Nothing to complete! Click on "Edit" or "Clear all" to enter a heightmap customizaton mode', null, "error");
+ return;
+ }
+ land = $.grep(cells, function(e) {return e.height >= 0.2;});
+ if (land.length < 100) {
+ tip("Insufficient land area! Please add more land cells to complete the map", null, "error");
+ return;
+ }
exitCustomization();
console.time("TOTAL");
markFeatures();
- // if (changeHeights.checked) reduceClosedLakes();
drawOcean();
elevateLakes();
resolveDepressionsPrimary();
- reGraph();
+ const noChange = !changeHeights.checked;
+ reGraph(noChange);
resolveDepressionsSecondary();
flux();
addLakes();
- if (!changeHeights.checked) restoreCustomHeights();
+ if (noChange) restoreCustomHeights();
drawCoastline();
drawRelief();
- if (!keepData) {
+ const keepData = states.length && manors.length;
+ if (keepData) {
+ restoreRegions();
+ } else {
generateCultures();
manorsAndRegions();
- } else {
- restoreRegions();
}
cleanData();
console.timeEnd("TOTAL");
@@ -6176,9 +6195,9 @@ function fantasyMap() {
generate();
return;
}
- if (id === "editCountries") {editCountries();}
- if (id === "editCultures") {editCultures();}
- if (id === "editScale" || id === "editScaleCountries" || id === "editScaleBurgs") {editScale();}
+ if (id === "editCountries") editCountries();
+ if (id === "editCultures") editCultures();
+ if (id === "editScale" || id === "editScaleCountries" || id === "editScaleBurgs") editScale();
if (id === "countriesManually") {
customization = 2;
tip("Click to select a country, drag the circle to re-assign", true);
@@ -6337,7 +6356,9 @@ function fantasyMap() {
link.click();
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
}
- if (id === "burgNamesImport") {burgsListToLoad.click();}
+
+ if (id === "burgNamesImport") burgsListToLoad.click();
+
if (id === "removeCountries") {
alertMessage.innerHTML = `Are you sure you want remove all countries?`;
$("#alert").dialog({resizable: false, title: "Remove countries",
@@ -6351,8 +6372,7 @@ function fantasyMap() {
states.map(function(s) {
const c = +s.capital;
if (isNaN(c)) return;
- $("#burgLabels [data-id=" + c + "]").detach().appendTo($("#burgLabels #towns"));
- $("#burgIcons [data-id=" + c + "]").detach().appendTo($("#burgIcons #towns"));
+ moveBurgToGroup(c, "towns");
});
labels.select("#countries").selectAll("text").remove();
regions.selectAll("path").remove();
@@ -6488,9 +6508,11 @@ function fantasyMap() {
});
}
if (id === "fromHeightmap") {
- let message = "It's highly recommended to finalize a heightmap as a first step. ";
- message += "If you want to edit a map, it's better to clean up all the data except on heights. ";
- message += "You may also keep the data, but it can cause unexpected errors";
+ const message = `Hightmap is a basic element on which secondary data (burgs, countries, cultures) is based.
+ If you want to significantly change the hightmap, it may be better to clean up all the secondary data
+ and let the system to re-generate it based on the updated hightmap. In case of minor changes, you can keep the data.
+ Newly added lands will be considered as neutral. Burgs located on a removed land cells will be deleted.
+ Routes won't be regenerated.`
alertMessage.innerHTML = message;
$("#alert").dialog({resizable: false, title: "Edit Heightmap",
buttons: {
@@ -6538,9 +6560,7 @@ function fantasyMap() {
updateHeightmap();
updateHistory();
}
- if (id === "getMap") {
- if (states.length && manors.length) {getMap("keep");} else {getMap();}
- }
+ if (id === "getMap") getMap();
if (id === "applyTemplate") {
if ($("#templateEditor").is(":visible")) {return;}
$("#templateEditor").dialog({
@@ -6690,9 +6710,7 @@ function fantasyMap() {
start.click();
}
}
- if (id === "templateComplete") {
- if (customization === 1 && !$("#getMap").attr("disabled")) {getMap();}
- }
+ if (id === "templateComplete") getMap();
if (id === "convertColorsMinus") {
var current = +convertColors.value - 1;
if (current < 4) {current = 3;}
@@ -7327,7 +7345,8 @@ function fantasyMap() {
// Enter Heightmap Customization mode
function customizeHeightmap() {
customization = 1;
- tip("Heightmap customization mode is active. Click on \"Complete\" to finalize the Heightmap", true);
+ tip('Heightmap customization mode is active. Click on "Complete" to finalize the Heightmap', true);
+ $("#getMap").removeClass("buttonoff").addClass("glow");
resetZoom();
landmassCounter.innerHTML = "0";
$('#grid').fadeIn();
@@ -7343,7 +7362,7 @@ function fantasyMap() {
tip("", true);
canvas.style.opacity = 0;
$("#customizationMenu").slideUp();
- $("#getMap").attr("disabled", true).addClass("buttonoff");
+ $("#getMap").addClass("buttonoff").removeClass("glow");
$("#landmass").empty();
$('#grid').empty().fadeOut();
$('#toggleGrid').addClass("buttonoff");
@@ -7354,7 +7373,6 @@ function fantasyMap() {
historyStage = 0;
$("#customizeHeightmap").slideUp();
$("#openEditor").slideDown();
- $("#getMap").removeClass("glow");
debug.selectAll(".circle, .tag, .line").remove();
}
@@ -7536,8 +7554,7 @@ function fantasyMap() {
if (oldState === "neutral") {manors[burg].population *= (1 / urbanFactor);}
manors[burg].population *= 2; // give capital x2 population bonus
states[state].capital = burg;
- $("#burgLabels [data-id=" + burg + "]").detach().appendTo($("#burgLabels #capitals"));
- $("#burgIcons [data-id=" + burg + "]").detach().appendTo($("#burgIcons #capitals"));
+ moveBurgToGroup(burg, "capitals");
}
} else {
// free cell -> create new burg for a capital
@@ -7605,47 +7622,33 @@ function fantasyMap() {
});
// fully remove country
$("#countriesBody .icon-trash-empty").on("click", function() {
- var s = +(this.parentNode.id).slice(5);
- if (states[s].capital === "select") {
- removeCountry(s);
- return;
- }
- alertMessage.innerHTML = `Are you sure you want to remove the country?`;
+ const s = +(this.parentNode.id).slice(5);
+ alertMessage.innerHTML = `Are you sure you want to remove the country? All lands and burgs will become neutral`;
$("#alert").dialog({resizable: false, title: "Remove country", buttons: {
- Remove: function() {
- removeCountry(s);
- $(this).dialog("close");
- },
+ Remove: function() {removeCountry(s); $(this).dialog("close");},
Cancel: function() {$(this).dialog("close");}
}});
});
function removeCountry(s) {
const cellsCount = states[s].cells;
- const capital = states[s].capital;
+ const capital = +states[s].capital;
+ if (!isNaN(capital)) moveBurgToGroup(capital, "towns");
states.splice(s, 1);
states.map(function(s, i) {s.i = i;});
- cells.map(function(c) {
+ land.map(function(c) {
if (c.region === s) c.region = "neutral";
else if (c.region > s) c.region -= 1;
});
// do only if removed state had cells
if (cellsCount) {
- // change capital to burg
- $("#burgLabels [data-id=" + capital + "]").detach().appendTo($("#burgLabels #towns"));
- $("#burgIcons [data-id=" + capital + "]").detach().appendTo($("#burgIcons #towns"));
- var burgsSelection = $.grep(manors, function(e) {return (e.region === s);});
- var urbanFactor = 0.9;
- burgsSelection.map(function(b) {
- if (b.i === capital) {b.population *= 0.5;}
- b.population *= urbanFactor;
- b.region = "neutral";
- });
+ manors.map(function(b) {if (b.region === s) b.region = "neutral";});
// re-calculate neutral data
- if (states[states.length-1].capital !== "neutral") {
- states.push({i: states.length, color: "neutral", name: "Neutrals", capital: "neutral"});
+ const i = states.length;
+ if (states[i-1].capital !== "neutral") {
+ states.push({i, color: "neutral", name: "Neutrals", capital: "neutral"});
}
- recalculateStateData(states.length - 1); // re-calc data for neutrals
+ recalculateStateData(i-1); // re-calc data for neutrals
redrawRegions();
}
editCountries();
@@ -7708,25 +7711,25 @@ function fantasyMap() {
var x = +l.attr("x"), y = +l.attr("y");
zoomTo(x, y, 8, 1600);
});
+
$("#burgsBody > div").hover(focusBurg, unfocus);
+
$("#burgsBody > div").click(function() {
- if (!$("#changeCapital").hasClass("pressed")) {return;}
- var type = $(this).attr("data-type");
- if (type.includes("capital")) {return;}
- var s = +$("#burgsEditor").attr("data-state");
- var b = +$(this).attr("id").slice(5);
- var oldCap = states[s].capital;
- manors[oldCap].population *= 0.5;
- manors[b].population *= 2;
- states[s].capital = b;
- recalculateStateData(s);
- $("#labels [data-id=" + oldCap + "]").detach().appendTo($("#burgLabels #towns"));
- $("#icons [data-id=" + oldCap + "]").detach().appendTo($("#burgIcons #towns"));
- $("#labels [data-id=" + b + "]").detach().appendTo($("#burgLabels #capitals"));
- $("#icons [data-id=" + b + "]").detach().appendTo($("#burgIcons #towns"));
- updateCountryEditors();
+ if (!$("#changeCapital").hasClass("pressed")) return;
+ const s = +$("#burgsEditor").attr("data-state");
+ const newCap = +$(this).attr("id").slice(5);
+ const oldCap = +states[s].capital;
+ if (newCap === oldCap) {
+ tip("This burg is already a capital! Please select a different burg", null, "error");
+ return;
+ }
$("#changeCapital").removeClass("pressed");
+ states[s].capital = newCap;
+ if (!isNaN(oldCap)) moveBurgToGroup(oldCap, "towns");
+ recalculateStateData(s);
+ moveBurgToGroup(newCap, "capitals");
});
+
$(".burgName").on("input", function() {
var b = +(this.parentNode.id).slice(5);
manors[b].name = this.value;
@@ -7858,7 +7861,7 @@ function fantasyMap() {
// open editCultures dialog
function editCultures() {
- if (cults.selectAll("path").size() === 0) $("#toggleCultures").click();
+ if (!cults.selectAll("path").size()) $("#toggleCultures").click();
if (regions.style("display") !== "none") $("#toggleCountries").click();
layoutPreset.value = "layoutCultural";
$("#culturesBody").empty();
@@ -7876,9 +7879,9 @@ function fantasyMap() {
});
manors.map(function(m) {
- const r = m.region;
- if (r === undefined || r === "removed") return;
- urbPops[r] = urbPops[r] ? urbPops[r] + m.population : m.population;
+ const c = m.culture;
+ if (isNaN(c)) return;
+ urbPops[c] = urbPops[c] ? urbPops[c] + m.population : m.population;
});
for (let c = 0; c < cultures.length; c++) {