mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-23 12:31:24 +01:00
Merge branch 'master' into dev-submaps
This commit is contained in:
commit
0a7218db99
24 changed files with 904 additions and 515 deletions
|
|
@ -447,15 +447,17 @@ function overviewBurgs() {
|
|||
}
|
||||
|
||||
function downloadBurgsData() {
|
||||
let data = "Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation (" + heightUnit.value + "),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town\n"; // headers
|
||||
let data = "Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Population,Longitude,Latitude,Elevation (" + heightUnit.value + "),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town\n"; // headers
|
||||
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
||||
|
||||
valid.forEach(b => {
|
||||
data += b.i + ",";
|
||||
data += b.name + ",";
|
||||
const province = pack.cells.province[b.cell];
|
||||
data += province ? pack.provinces[province].name + "," : ",";
|
||||
data += province ? pack.provinces[province].fullName + "," : ",";
|
||||
data += b.state ? pack.states[b.state].fullName + "," : pack.states[b.state].name + ",";
|
||||
data += pack.states[b.state].name + ",";
|
||||
data += pack.states[b.state].fullName + ",";
|
||||
data += pack.cultures[b.culture].name + ",";
|
||||
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
|
||||
data += rn(b.population * populationRate * urbanization) + ",";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ function editHeightmap() {
|
|||
<p><i>Erase</i> mode also allows you Convert an Image into a heightmap or use Template Editor.</p>
|
||||
<p>You can <i>keep</i> the data, but you won't be able to change the coastline.</p>
|
||||
<p>Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.</p>
|
||||
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before editing the heightmap!</p>
|
||||
<p>Please <span class="pseudoLink" onclick=dowloadMap(); editHeightmap();>save the map</span> before editing the heightmap!</p>
|
||||
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>`;
|
||||
|
||||
$("#alert").dialog({
|
||||
|
|
@ -328,10 +328,10 @@ function editHeightmap() {
|
|||
|
||||
for (const i of pack.cells.i) {
|
||||
const g = pack.cells.g[i];
|
||||
const land = pack.cells.h[i] >= 20;
|
||||
const isLand = pack.cells.h[i] >= 20;
|
||||
|
||||
// check biome
|
||||
pack.cells.biome[i] = land && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], pack.cells.h[i]);
|
||||
pack.cells.biome[i] = isLand && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], grid.cells.temp[g], pack.cells.h[i]);
|
||||
|
||||
// rivers data
|
||||
if (!erosionAllowed) {
|
||||
|
|
@ -340,7 +340,7 @@ function editHeightmap() {
|
|||
pack.cells.fl[i] = fl[g];
|
||||
}
|
||||
|
||||
if (!land) continue;
|
||||
if (!isLand) continue;
|
||||
pack.cells.culture[i] = culture[g];
|
||||
pack.cells.pop[i] = pop[g];
|
||||
pack.cells.road[i] = road[g];
|
||||
|
|
@ -837,31 +837,27 @@ function editHeightmap() {
|
|||
const steps = body.querySelectorAll("#templateBody > div");
|
||||
if (!steps.length) return;
|
||||
|
||||
const {addHill, addPit, addRange, addTrough, addStrait, modify, smooth} = HeightmapGenerator;
|
||||
grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights
|
||||
|
||||
for (const s of steps) {
|
||||
if (s.style.opacity == 0.5) continue;
|
||||
const type = s.dataset.type;
|
||||
for (const step of steps) {
|
||||
if (step.style.opacity === "0.5") continue;
|
||||
const type = step.dataset.type;
|
||||
|
||||
const elCount = s.querySelector(".templateCount") || "";
|
||||
const elHeight = s.querySelector(".templateHeight") || "";
|
||||
const count = step.querySelector(".templateCount")?.value || "";
|
||||
const height = step.querySelector(".templateHeight")?.value || "";
|
||||
const dist = step.querySelector(".templateDist")?.value || null;
|
||||
const x = step.querySelector(".templateX")?.value || null;
|
||||
const y = step.querySelector(".templateY")?.value || null;
|
||||
|
||||
const elDist = s.querySelector(".templateDist");
|
||||
const dist = elDist ? elDist.value : null;
|
||||
|
||||
const templateX = s.querySelector(".templateX");
|
||||
const x = templateX ? templateX.value : null;
|
||||
const templateY = s.querySelector(".templateY");
|
||||
const y = templateY ? templateY.value : null;
|
||||
|
||||
if (type === "Hill") HeightmapGenerator.addHill(elCount.value, elHeight.value, x, y);
|
||||
else if (type === "Pit") HeightmapGenerator.addPit(elCount.value, elHeight.value, x, y);
|
||||
else if (type === "Range") HeightmapGenerator.addRange(elCount.value, elHeight.value, x, y);
|
||||
else if (type === "Trough") HeightmapGenerator.addTrough(elCount.value, elHeight.value, x, y);
|
||||
else if (type === "Strait") HeightmapGenerator.addStrait(elCount.value, dist);
|
||||
else if (type === "Add") HeightmapGenerator.modify(dist, +elCount.value, 1);
|
||||
else if (type === "Multiply") HeightmapGenerator.modify(dist, 0, +elCount.value);
|
||||
else if (type === "Smooth") HeightmapGenerator.smooth(+elCount.value);
|
||||
if (type === "Hill") addHill(count, height, x, y);
|
||||
else if (type === "Pit") addPit(count, height, x, y);
|
||||
else if (type === "Range") addRange(count, height, x, y);
|
||||
else if (type === "Trough") addTrough(count, height, x, y);
|
||||
else if (type === "Strait") addStrait(count, dist);
|
||||
else if (type === "Add") modify(dist, +count, 1);
|
||||
else if (type === "Multiply") modify(dist, 0, +count);
|
||||
else if (type === "Smooth") smooth(+count);
|
||||
|
||||
updateHistory("noStat"); // update history every step
|
||||
}
|
||||
|
|
@ -880,17 +876,13 @@ function editHeightmap() {
|
|||
|
||||
let data = "";
|
||||
for (const s of steps) {
|
||||
if (s.style.opacity == 0.5) continue;
|
||||
if (s.style.opacity === "0.5") continue;
|
||||
|
||||
const type = s.getAttribute("data-type");
|
||||
const elCount = s.querySelector(".templateCount");
|
||||
const count = elCount ? elCount.value : "0";
|
||||
const elHeight = s.querySelector(".templateHeight");
|
||||
const elDist = s.querySelector(".templateDist");
|
||||
const arg3 = elHeight ? elHeight.value : elDist ? elDist.value : "0";
|
||||
const templateX = s.querySelector(".templateX");
|
||||
const x = templateX ? templateX.value : "0";
|
||||
const templateY = s.querySelector(".templateY");
|
||||
const y = templateY ? templateY.value : "0";
|
||||
const count = s.querySelector(".templateCount")?.value || "0";
|
||||
const arg3 = s.querySelector(".templateHeight")?.value || s.querySelector(".templateDist")?.value || "0";
|
||||
const x = s.querySelector(".templateX")?.value || "0";
|
||||
const y = s.querySelector(".templateY")?.value || "0";
|
||||
data += `${type} ${count} ${arg3} ${x} ${y}\r\n`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1456,16 +1456,19 @@ function toggleRivers(event) {
|
|||
|
||||
function drawRivers() {
|
||||
TIME && console.time("drawRivers");
|
||||
rivers.selectAll("*").remove();
|
||||
|
||||
const {addMeandering, getRiverPath} = Rivers;
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const riverPaths = pack.rivers.map(river => {
|
||||
const meanderedPoints = addMeandering(river.cells, river.points);
|
||||
const widthFactor = river.widthFactor || 1;
|
||||
const startingWidth = river.sourceWidth || 0;
|
||||
const path = getRiverPath(meanderedPoints, widthFactor, startingWidth);
|
||||
return `<path id="river${river.i}" d="${path}"/>`;
|
||||
|
||||
const riverPaths = pack.rivers.map(({cells, points, i, widthFactor, sourceWidth}) => {
|
||||
if (!cells || cells.length < 2) return;
|
||||
const meanderedPoints = addMeandering(cells, points);
|
||||
const path = getRiverPath(meanderedPoints, widthFactor, sourceWidth);
|
||||
return `<path id="river${i}" d="${path}"/>`;
|
||||
});
|
||||
rivers.html(riverPaths.join(""));
|
||||
|
||||
TIME && console.timeEnd("drawRivers");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,9 @@ function showSupporters() {
|
|||
Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee,
|
||||
Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth,
|
||||
Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,Nobody679,良义 金,Chris Gray,Phoenix Boatwright,Mackenzie,
|
||||
"Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas"`;
|
||||
Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,
|
||||
George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,Mike Conley,Xavier privé,Hope You're Well,
|
||||
Mark Sprietsma,Robert Landry,Nick Mowry"`;
|
||||
|
||||
const array = supporters
|
||||
.replace(/(?:\r\n|\r|\n)/g, "")
|
||||
|
|
@ -621,6 +623,7 @@ document.getElementById("sticked").addEventListener("click", function (event) {
|
|||
const id = event.target.id;
|
||||
if (id === "newMapButton") regeneratePrompt();
|
||||
else if (id === "saveButton") showSavePane();
|
||||
else if (id === "exportButton") showExportPane();
|
||||
else if (id === "loadButton") showLoadPane();
|
||||
else if (id === "zoomReset") resetZoom(1000);
|
||||
});
|
||||
|
|
@ -654,12 +657,13 @@ function regeneratePrompt() {
|
|||
}
|
||||
|
||||
function showSavePane() {
|
||||
document.getElementById("showLabels").checked = !hideLabels.checked;
|
||||
const sharableLinkContainer = document.getElementById("sharableLinkContainer");
|
||||
sharableLinkContainer.style.display = "none";
|
||||
|
||||
$("#saveMapData").dialog({
|
||||
title: "Save map",
|
||||
resizable: false,
|
||||
width: "30em",
|
||||
width: "27em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Close: function () {
|
||||
|
|
@ -669,21 +673,21 @@ function showSavePane() {
|
|||
});
|
||||
}
|
||||
|
||||
// download map data as GeoJSON
|
||||
function saveGeoJSON() {
|
||||
alertMessage.innerHTML = `You can export map data in GeoJSON format used in GIS tools such as QGIS.
|
||||
Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export", "wiki-page")} for guidance`;
|
||||
function copyLinkToClickboard() {
|
||||
const shrableLink = document.getElementById("sharableLink");
|
||||
const link = shrableLink.getAttribute("href");
|
||||
navigator.clipboard.writeText(link).then(() => tip("Link is copied to the clipboard", true, "success", 8000));
|
||||
}
|
||||
|
||||
$("#alert").dialog({
|
||||
title: "GIS data export",
|
||||
function showExportPane() {
|
||||
document.getElementById("showLabels").checked = !hideLabels.checked;
|
||||
|
||||
$("#exportMapData").dialog({
|
||||
title: "Export map data",
|
||||
resizable: false,
|
||||
width: "35em",
|
||||
width: "26em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Cells: saveGeoJSON_Cells,
|
||||
Routes: saveGeoJSON_Routes,
|
||||
Rivers: saveGeoJSON_Rivers,
|
||||
Markers: saveGeoJSON_Markers,
|
||||
Close: function () {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
|
|
@ -691,11 +695,11 @@ function saveGeoJSON() {
|
|||
});
|
||||
}
|
||||
|
||||
function showLoadPane() {
|
||||
async function showLoadPane() {
|
||||
$("#loadMapData").dialog({
|
||||
title: "Load map",
|
||||
resizable: false,
|
||||
width: "17em",
|
||||
width: "22em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Close: function () {
|
||||
|
|
@ -703,6 +707,19 @@ function showLoadPane() {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
const dpx = document.getElementById("loadFromDropbox");
|
||||
const dpf = dpx.querySelector("select");
|
||||
const files = await Cloud.providers.dropbox.list();
|
||||
dpx.style.display = files? "block" : "none";
|
||||
if (!files) return;
|
||||
while(dpf.firstChild) dpf.removeChild(dpf.firstChild);
|
||||
files.forEach(f => {
|
||||
const opt = document.createElement('option');
|
||||
opt.innerText = f.name;
|
||||
opt.value = f.path;
|
||||
dpf.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
function loadURL() {
|
||||
|
|
|
|||
|
|
@ -934,20 +934,22 @@ function editProvinces() {
|
|||
|
||||
function downloadProvincesData() {
|
||||
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
|
||||
let data = "Id,Province,Form,State,Color,Capital,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
let data = "Id,Province,Full Name,Form,State,Color,Capital,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
|
||||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
let key = parseInt(el.dataset.id);
|
||||
const key = parseInt(el.dataset.id);
|
||||
const provincePack = pack.provinces[key];
|
||||
data += el.dataset.id + ",";
|
||||
data += el.dataset.name + ",";
|
||||
data += provincePack.fullName + ",";
|
||||
data += el.dataset.form + ",";
|
||||
data += el.dataset.state + ",";
|
||||
data += el.dataset.color + ",";
|
||||
data += el.dataset.capital + ",";
|
||||
data += el.dataset.area + ",";
|
||||
data += el.dataset.population + ",";
|
||||
data += `${Math.round(pack.provinces[key].rural * populationRate)},`;
|
||||
data += `${Math.round(pack.provinces[key].urban * populationRate * urbanization)}\n`;
|
||||
data += `${Math.round(provincePack.rural * populationRate)},`;
|
||||
data += `${Math.round(provincePack.urban * populationRate * urbanization)}\n`;
|
||||
});
|
||||
|
||||
const name = getFileName("Provinces") + ".csv";
|
||||
|
|
|
|||
|
|
@ -100,16 +100,13 @@ function createRiver() {
|
|||
const basin = getBasin(parent);
|
||||
|
||||
rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth, parent, cells: riverCells, basin, name, type: "River"});
|
||||
const id = "river" + riverId;
|
||||
|
||||
// render river
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
viewbox
|
||||
.select("#rivers")
|
||||
.append("path")
|
||||
.attr("id", "river" + riverId)
|
||||
.attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
|
||||
viewbox.select("#rivers").append("path").attr("id", id).attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
|
||||
|
||||
editRiver(riverId);
|
||||
editRiver(id);
|
||||
}
|
||||
|
||||
function closeRiverCreator() {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ function editRiver(id) {
|
|||
document.getElementById("toggleCells").dataset.forced = +!layerIsOn("toggleCells");
|
||||
if (!layerIsOn("toggleCells")) toggleCells();
|
||||
|
||||
elSelected = d3.select("#" + id);
|
||||
elSelected = d3.select("#" + id).on("click", addControlPoint);
|
||||
|
||||
tip("Drag control points to change the river course. For major changes please create a new river instead", true);
|
||||
tip("Drag control points to change the river course. Click on point to remove it. Click on river to add additional control point. For major changes please create a new river instead", true);
|
||||
debug.append("g").attr("id", "controlCells");
|
||||
debug.append("g").attr("id", "controlPoints");
|
||||
|
||||
|
|
@ -19,8 +19,8 @@ function editRiver(id) {
|
|||
const river = getRiver();
|
||||
const {cells, points} = river;
|
||||
const riverPoints = Rivers.getRiverPoints(cells, points);
|
||||
drawControlPoints(riverPoints, cells);
|
||||
drawCells(cells, "current");
|
||||
drawControlPoints(riverPoints);
|
||||
drawCells(cells);
|
||||
|
||||
$("#riverEditor").dialog({
|
||||
title: "Edit River",
|
||||
|
|
@ -92,37 +92,35 @@ function editRiver(id) {
|
|||
document.getElementById("riverWidth").value = width;
|
||||
}
|
||||
|
||||
function drawControlPoints(points, cells) {
|
||||
function drawControlPoints(points) {
|
||||
debug
|
||||
.select("#controlPoints")
|
||||
.selectAll("circle")
|
||||
.data(points)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.join("circle")
|
||||
.attr("cx", d => d[0])
|
||||
.attr("cy", d => d[1])
|
||||
.attr("r", 0.6)
|
||||
.attr("data-cell", (d, i) => cells[i])
|
||||
.attr("data-i", (d, i) => i)
|
||||
.call(d3.drag().on("start", dragControlPoint));
|
||||
.call(d3.drag().on("start", dragControlPoint))
|
||||
.on("click", removeControlPoint);
|
||||
}
|
||||
|
||||
function drawCells(cells, type) {
|
||||
function drawCells(cells) {
|
||||
const validCells = [...new Set(cells)].filter(i => pack.cells.i[i]);
|
||||
debug
|
||||
.select("#controlCells")
|
||||
.selectAll(`polygon.${type}`)
|
||||
.data(cells.filter(i => pack.cells.i[i]))
|
||||
.selectAll(`polygon`)
|
||||
.data(validCells)
|
||||
.join("polygon")
|
||||
.attr("points", d => getPackPolygon(d))
|
||||
.attr("class", type);
|
||||
.attr("points", d => getPackPolygon(d));
|
||||
}
|
||||
|
||||
function dragControlPoint() {
|
||||
const {i, r, fl} = pack.cells;
|
||||
const {r, fl} = pack.cells;
|
||||
const river = getRiver();
|
||||
|
||||
const initCell = +this.dataset.cell;
|
||||
const index = +this.dataset.i;
|
||||
const {x: x0, y: y0} = d3.event;
|
||||
const initCell = findCell(x0, y0);
|
||||
|
||||
let movedToCell = null;
|
||||
|
||||
|
|
@ -136,22 +134,18 @@ function editRiver(id) {
|
|||
this.setAttribute("cy", y);
|
||||
this.__data__ = [rn(x, 1), rn(y, 1)];
|
||||
redrawRiver();
|
||||
drawCells(river.cells);
|
||||
});
|
||||
|
||||
d3.event.on("end", () => {
|
||||
if (movedToCell) {
|
||||
this.dataset.cell = movedToCell;
|
||||
river.cells[index] = movedToCell;
|
||||
drawCells(river.cells, "current");
|
||||
|
||||
if (!r[movedToCell]) {
|
||||
// swap river data
|
||||
r[initCell] = 0;
|
||||
r[movedToCell] = river.i;
|
||||
const sourceFlux = fl[initCell];
|
||||
fl[initCell] = fl[movedToCell];
|
||||
fl[movedToCell] = sourceFlux;
|
||||
}
|
||||
if (movedToCell && !r[movedToCell]) {
|
||||
// swap river data
|
||||
r[initCell] = 0;
|
||||
r[movedToCell] = river.i;
|
||||
const sourceFlux = fl[initCell];
|
||||
fl[initCell] = fl[movedToCell];
|
||||
fl[movedToCell] = sourceFlux;
|
||||
redrawRiver();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -159,8 +153,10 @@ function editRiver(id) {
|
|||
function redrawRiver() {
|
||||
const river = getRiver();
|
||||
river.points = debug.selectAll("#controlPoints > *").data();
|
||||
const {cells, widthFactor, sourceWidth} = river;
|
||||
const meanderedPoints = Rivers.addMeandering(cells, river.points);
|
||||
river.cells = river.points.map(([x, y]) => findCell(x, y));
|
||||
|
||||
const {widthFactor, sourceWidth} = river;
|
||||
const meanderedPoints = Rivers.addMeandering(river.cells, river.points);
|
||||
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
const path = Rivers.getRiverPath(meanderedPoints, widthFactor, sourceWidth);
|
||||
|
|
@ -170,6 +166,27 @@ function editRiver(id) {
|
|||
if (modules.elevation) showEPForRiver(elSelected.node());
|
||||
}
|
||||
|
||||
function addControlPoint() {
|
||||
const [x, y] = d3.mouse(this);
|
||||
const point = [rn(x, 1), rn(y, 1)];
|
||||
|
||||
const river = getRiver();
|
||||
if (!river.points) river.points = debug.selectAll("#controlPoints > *").data();
|
||||
|
||||
const index = getSegmentId(river.points, point, 2);
|
||||
river.points.splice(index, 0, point);
|
||||
drawControlPoints(river.points);
|
||||
redrawRiver();
|
||||
}
|
||||
|
||||
function removeControlPoint() {
|
||||
this.remove();
|
||||
redrawRiver();
|
||||
|
||||
const {cells} = getRiver();
|
||||
drawCells(cells);
|
||||
}
|
||||
|
||||
function changeName() {
|
||||
getRiver().name = this.value;
|
||||
}
|
||||
|
|
@ -244,6 +261,8 @@ function editRiver(id) {
|
|||
function closeRiverEditor() {
|
||||
debug.select("#controlPoints").remove();
|
||||
debug.select("#controlCells").remove();
|
||||
|
||||
elSelected.on("click", null);
|
||||
unselect();
|
||||
clearMainTip();
|
||||
|
||||
|
|
|
|||
|
|
@ -1028,12 +1028,13 @@ function editStates() {
|
|||
|
||||
function downloadStatesData() {
|
||||
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
|
||||
let data = "Id,State,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
|
||||
let data = "Id,State,Full Name,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
const key = parseInt(el.dataset.id);
|
||||
const statePack = pack.states[key];
|
||||
data += el.dataset.id + ",";
|
||||
data += el.dataset.name + ",";
|
||||
data += (statePack.fullName ? statePack.fullName : "") + ",";
|
||||
data += el.dataset.form + ",";
|
||||
data += el.dataset.color + ",";
|
||||
data += el.dataset.capital + ",";
|
||||
|
|
@ -1044,8 +1045,8 @@ function editStates() {
|
|||
data += el.dataset.burgs + ",";
|
||||
data += el.dataset.area + ",";
|
||||
data += el.dataset.population + ",";
|
||||
data += `${Math.round(pack.states[key].rural * populationRate)},`;
|
||||
data += `${Math.round(pack.states[key].urban * populationRate * urbanization)}\n`;
|
||||
data += `${Math.round(statePack.rural * populationRate)},`;
|
||||
data += `${Math.round(statePack.urban * populationRate * urbanization)}\n`;
|
||||
});
|
||||
|
||||
const name = getFileName("States") + ".csv";
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -136,21 +136,11 @@ function recalculatePopulation() {
|
|||
function regenerateStates() {
|
||||
const localSeed = Math.floor(Math.random() * 1e9); // new random seed
|
||||
Math.random = aleaPRNG(localSeed);
|
||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||
if (!burgs.length) {
|
||||
tip("No burgs to generate states. Please create burgs first", false, "error");
|
||||
return;
|
||||
}
|
||||
if (burgs.length < +regionsInput.value) {
|
||||
tip(`Not enough burgs to generate ${regionsInput.value} states. Will generate only ${burgs.length} states`, false, "warn");
|
||||
}
|
||||
|
||||
// burg local ids sorted by a bit randomized population:
|
||||
const sorted = burgs
|
||||
.map((b, i) => [i, b.population * Math.random()])
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(b => b[0]);
|
||||
const capitalsTree = d3.quadtree();
|
||||
const statesCount = +regionsInput.value;
|
||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||
if (!burgs.length) return tip("There are no any burgs to generate states. Please create burgs first", false, "error");
|
||||
if (burgs.length < statesCount) tip(`Not enough burgs to generate ${statesCount} states. Will generate only ${burgs.length} states`, false, "warn");
|
||||
|
||||
// turn all old capitals into towns
|
||||
burgs
|
||||
|
|
@ -167,8 +157,7 @@ function regenerateStates() {
|
|||
|
||||
unfog();
|
||||
|
||||
// if desired states number is 0
|
||||
if (regionsInput.value == 0) {
|
||||
if (!statesCount) {
|
||||
tip(`Cannot generate zero states. Please check the <i>States Number</i> option`, false, "warn");
|
||||
pack.states = pack.states.slice(0, 1); // remove all except of neutrals
|
||||
pack.states[0].diplomacy = []; // clear diplomacy
|
||||
|
|
@ -184,26 +173,34 @@ function regenerateStates() {
|
|||
return;
|
||||
}
|
||||
|
||||
const neutral = pack.states[0].name;
|
||||
const count = Math.min(+regionsInput.value, burgs.length);
|
||||
// burg local ids sorted by a bit randomized population:
|
||||
const sortedBurgs = burgs
|
||||
.map((b, i) => [b, b.population * Math.random()])
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(b => b[0]);
|
||||
const capitalsTree = d3.quadtree();
|
||||
|
||||
const neutral = pack.states[0].name; // neutrals name
|
||||
const count = Math.min(statesCount, burgs.length) + 1; // +1 for neutral
|
||||
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
|
||||
|
||||
pack.states = d3.range(count).map(i => {
|
||||
if (!i) return {i, name: neutral};
|
||||
|
||||
let capital = null,
|
||||
x = 0,
|
||||
y = 0;
|
||||
for (const i of sorted) {
|
||||
capital = burgs[i];
|
||||
(x = capital.x), (y = capital.y);
|
||||
if (capitalsTree.find(x, y, spacing) === undefined) break;
|
||||
let capital = null;
|
||||
for (const burg of sortedBurgs) {
|
||||
const {x, y} = burg;
|
||||
if (capitalsTree.find(x, y, spacing) === undefined) {
|
||||
burg.capital = 1;
|
||||
capital = burg;
|
||||
capitalsTree.add([x, y]);
|
||||
moveBurgToGroup(burg.i, "cities");
|
||||
break;
|
||||
}
|
||||
|
||||
spacing = Math.max(spacing - 1, 1);
|
||||
}
|
||||
|
||||
capitalsTree.add([x, y]);
|
||||
capital.capital = 1;
|
||||
moveBurgToGroup(capital.i, "cities");
|
||||
|
||||
const culture = capital.culture;
|
||||
const basename = capital.name.length < 9 && capital.cell % 5 === 0 ? capital.name : Names.getCulture(culture, 3, 6, "", 0);
|
||||
const name = Names.getState(basename, culture);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
function editWorld() {
|
||||
if (customization) return;
|
||||
$("#worldConfigurator").dialog({title: "Configure World", resizable: false, width: "42em",
|
||||
$("#worldConfigurator").dialog({
|
||||
title: "Configure World",
|
||||
resizable: false,
|
||||
width: "42em",
|
||||
buttons: {
|
||||
"Whole World": () => applyWorldPreset(100, 50),
|
||||
"Northern": () => applyWorldPreset(33, 25),
|
||||
"Tropical": () => applyWorldPreset(33, 50),
|
||||
"Southern": () => applyWorldPreset(33, 75),
|
||||
Northern: () => applyWorldPreset(33, 25),
|
||||
Tropical: () => applyWorldPreset(33, 50),
|
||||
Southern: () => applyWorldPreset(33, 75),
|
||||
"Restore Winds": restoreDefaultWinds
|
||||
}, open: function() {
|
||||
},
|
||||
open: function () {
|
||||
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
||||
buttons[0].addEventListener("mousemove", () => tip("Click to set map size to cover the whole World"));
|
||||
buttons[1].addEventListener("mousemove", () => tip("Click to set map size to cover the Northern latitudes"));
|
||||
|
|
@ -19,7 +23,8 @@ function editWorld() {
|
|||
|
||||
const globe = d3.select("#globe");
|
||||
const clr = d3.scaleSequential(d3.interpolateSpectral);
|
||||
const tMax = 30, tMin = -25; // temperature extremes
|
||||
const tMax = 30,
|
||||
tMin = -25; // temperature extremes
|
||||
const projection = d3.geoOrthographic().translate([100, 100]).scale(100);
|
||||
const path = d3.geoPath(projection);
|
||||
|
||||
|
|
@ -29,15 +34,15 @@ function editWorld() {
|
|||
if (modules.editWorld) return;
|
||||
modules.editWorld = true;
|
||||
|
||||
document.getElementById("worldControls").addEventListener("input", (e) => updateWorld(e.target));
|
||||
document.getElementById("worldControls").addEventListener("input", e => updateWorld(e.target));
|
||||
globe.select("#globeWindArrows").on("click", changeWind);
|
||||
globe.select("#globeGraticule").attr("d", round(path(d3.geoGraticule()()))); // globe graticule
|
||||
updateWindDirections();
|
||||
|
||||
function updateWorld(el) {
|
||||
if (el) {
|
||||
document.getElementById(el.dataset.stored+"Input").value = el.value;
|
||||
document.getElementById(el.dataset.stored+"Output").value = el.value;
|
||||
document.getElementById(el.dataset.stored + "Input").value = el.value;
|
||||
document.getElementById(el.dataset.stored + "Output").value = el.value;
|
||||
if (el.dataset.stored) lock(el.dataset.stored);
|
||||
}
|
||||
|
||||
|
|
@ -56,16 +61,18 @@ function editWorld() {
|
|||
if (layerIsOn("togglePrec")) drawPrec();
|
||||
if (layerIsOn("toggleBiomes")) drawBiomes();
|
||||
if (layerIsOn("toggleCoordinates")) drawCoordinates();
|
||||
if (layerIsOn("toggleRivers")) drawRivers();
|
||||
if (document.getElementById("canvas3d")) setTimeout(ThreeD.update(), 500);
|
||||
}
|
||||
|
||||
function updateGlobePosition() {
|
||||
const size = +document.getElementById("mapSizeOutput").value;
|
||||
const eqD = graphHeight / 2 * 100 / size;
|
||||
const eqD = ((graphHeight / 2) * 100) / size;
|
||||
|
||||
calculateMapCoordinates();
|
||||
const mc = mapCoordinates; // shortcut
|
||||
const scale = +distanceScaleInput.value, unit = distanceUnitInput.value;
|
||||
const scale = +distanceScaleInput.value,
|
||||
unit = distanceUnitInput.value;
|
||||
const meridian = toKilometer(eqD * 2 * scale);
|
||||
document.getElementById("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
|
||||
document.getElementById("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
|
||||
|
|
@ -82,27 +89,35 @@ function editWorld() {
|
|||
return 0; // 0 if distanceUnitInput is a custom unit
|
||||
}
|
||||
|
||||
function lat(lat) {return lat > 0 ? Math.abs(rn(lat)) + "°N" : Math.abs(rn(lat)) + "°S";} // parse latitude value
|
||||
const area = d3.geoGraticule().extent([[mc.lonW, mc.latN], [mc.lonE, mc.latS]]);
|
||||
function lat(lat) {
|
||||
return lat > 0 ? Math.abs(rn(lat)) + "°N" : Math.abs(rn(lat)) + "°S";
|
||||
} // parse latitude value
|
||||
const area = d3.geoGraticule().extent([
|
||||
[mc.lonW, mc.latN],
|
||||
[mc.lonE, mc.latS]
|
||||
]);
|
||||
globe.select("#globeArea").attr("d", round(path(area.outline()))); // map area
|
||||
}
|
||||
|
||||
function updateGlobeTemperature() {
|
||||
const tEq = +document.getElementById("temperatureEquatorOutput").value;
|
||||
document.getElementById("temperatureEquatorF").innerHTML = rn(tEq * 9/5 + 32);
|
||||
document.getElementById("temperatureEquatorF").innerHTML = rn((tEq * 9) / 5 + 32);
|
||||
const tPole = +document.getElementById("temperaturePoleOutput").value;
|
||||
document.getElementById("temperaturePoleF").innerHTML = rn(tPole * 9/5 + 32);
|
||||
document.getElementById("temperaturePoleF").innerHTML = rn((tPole * 9) / 5 + 32);
|
||||
globe.selectAll(".tempGradient90").attr("stop-color", clr(1 - (tPole - tMin) / (tMax - tMin)));
|
||||
globe.selectAll(".tempGradient60").attr("stop-color", clr(1 - (tEq - (tEq - tPole) * 2/3 - tMin) / (tMax - tMin)));
|
||||
globe.selectAll(".tempGradient30").attr("stop-color", clr(1 - (tEq - (tEq - tPole) * 1/3 - tMin) / (tMax - tMin)));
|
||||
globe.selectAll(".tempGradient60").attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 2) / 3 - tMin) / (tMax - tMin)));
|
||||
globe.selectAll(".tempGradient30").attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 1) / 3 - tMin) / (tMax - tMin)));
|
||||
globe.select(".tempGradient0").attr("stop-color", clr(1 - (tEq - tMin) / (tMax - tMin)));
|
||||
}
|
||||
|
||||
function updateWindDirections() {
|
||||
globe.select("#globeWindArrows").selectAll("path").each(function(d, i) {
|
||||
const tr = parseTransform(this.getAttribute("transform"));
|
||||
this.setAttribute("transform", `rotate(${options.winds[i]} ${tr[1]} ${tr[2]})`);
|
||||
});
|
||||
globe
|
||||
.select("#globeWindArrows")
|
||||
.selectAll("path")
|
||||
.each(function (d, i) {
|
||||
const tr = parseTransform(this.getAttribute("transform"));
|
||||
this.setAttribute("transform", `rotate(${options.winds[i]} ${tr[1]} ${tr[2]})`);
|
||||
});
|
||||
}
|
||||
|
||||
function changeWind() {
|
||||
|
|
@ -112,13 +127,13 @@ function editWorld() {
|
|||
const tr = parseTransform(arrow.getAttribute("transform"));
|
||||
arrow.setAttribute("transform", `rotate(${options.winds[tier]} ${tr[1]} ${tr[2]})`);
|
||||
localStorage.setItem("winds", options.winds);
|
||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
|
||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => ((90 - c) / 30) | 0);
|
||||
if (mapTiers.includes(tier)) updateWorld();
|
||||
}
|
||||
|
||||
function restoreDefaultWinds() {
|
||||
const defaultWinds = [225, 45, 225, 315, 135, 315];
|
||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
|
||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => ((90 - c) / 30) | 0);
|
||||
const update = mapTiers.some(t => options.winds[t] != defaultWinds[t]);
|
||||
options.winds = defaultWinds;
|
||||
updateWindDirections();
|
||||
|
|
@ -132,4 +147,4 @@ function editWorld() {
|
|||
lock("latitude");
|
||||
updateWorld();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue