mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
1.0.40
This commit is contained in:
parent
d5ec72b6b8
commit
dd1510e4ff
8 changed files with 265 additions and 61 deletions
|
|
@ -1823,7 +1823,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="aboutContent" class="tabcontent">
|
<div id="aboutContent" class="tabcontent">
|
||||||
<p><a href="https://github.com/Azgaar/Fantasy-Map-Generator" target="_blank">Fantasy Map Generator</a> is a free <a href="https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE" target="_blank">open source</a> tool which procedurally generates fantasy maps. You may use auto-generated maps as they are, edit them or even create a new map from scratch. Check out the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial" target="_blank">quick start tutorial</a> and <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki" target="_blank">project wiki</a> for guidance.</p>
|
<p><a href="https://github.com/Azgaar/Fantasy-Map-Generator" target="_blank">Fantasy Map Generator</a> is a free <a href="https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE" target="_blank">open source</a> tool which procedurally generates fantasy maps. You may use auto-generated maps as they are, edit them or even create a new map from scratch. Check out the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial" target="_blank">quick start tutorial</a> and <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A" target="_blank">Q&A</a> for guidance.</p>
|
||||||
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share created maps. You may support the project on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>.</p>
|
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share created maps. You may support the project on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>.</p>
|
||||||
<p>The project is under active development. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
|
<p>The project is under active development. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
|
||||||
<p>A special thanks to all supporters! <i data-tip="Click to see supporters names" class="collapsible icon-down-open pointer"></i></p>
|
<p>A special thanks to all supporters! <i data-tip="Click to see supporters names" class="collapsible icon-down-open pointer"></i></p>
|
||||||
|
|
@ -2626,6 +2626,7 @@
|
||||||
|
|
||||||
<div id="burgsBottom">
|
<div id="burgsBottom">
|
||||||
<button id="burgsEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
<button id="burgsEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
||||||
|
<button id="burgsChart" data-tip="Show burgs bubble chart" class="icon-chart-area"></button>
|
||||||
<button id="regenerateBurgNames" data-tip="Regenerate burg names based on assigned culture" class="icon-retweet"></button>
|
<button id="regenerateBurgNames" data-tip="Regenerate burg names based on assigned culture" class="icon-retweet"></button>
|
||||||
<button id="addNewBurg" data-tip="Add a new burg. Hold Shift to add multiple" class="icon-plus"></button>
|
<button id="addNewBurg" data-tip="Add a new burg. Hold Shift to add multiple" class="icon-plus"></button>
|
||||||
<button id="burgsExport" data-tip="Save burgs-related data as a text file (.csv)" class="icon-download"></button>
|
<button id="burgsExport" data-tip="Save burgs-related data as a text file (.csv)" class="icon-download"></button>
|
||||||
|
|
|
||||||
|
|
@ -162,11 +162,11 @@ function getMapData() {
|
||||||
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
||||||
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
||||||
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
||||||
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value,
|
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value,
|
||||||
heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
||||||
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value,
|
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value,
|
||||||
barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
||||||
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value,
|
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value,
|
||||||
temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds),
|
temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds),
|
||||||
mapName.value].join("|");
|
mapName.value].join("|");
|
||||||
const coords = JSON.stringify(mapCoordinates);
|
const coords = JSON.stringify(mapCoordinates);
|
||||||
|
|
@ -240,11 +240,57 @@ function saveGeoJSON() {
|
||||||
Cells: saveGeoJSON_Cells,
|
Cells: saveGeoJSON_Cells,
|
||||||
Routes: saveGeoJSON_Roads,
|
Routes: saveGeoJSON_Roads,
|
||||||
Rivers: saveGeoJSON_Rivers,
|
Rivers: saveGeoJSON_Rivers,
|
||||||
|
Markers: saveGeoJSON_Markers,
|
||||||
Close: function() {$(this).dialog("close");}
|
Close: function() {$(this).dialog("close");}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function saveGeoJSON_Cells() {
|
||||||
|
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
||||||
|
const cells = pack.cells, v = pack.vertices;
|
||||||
|
|
||||||
|
cells.i.forEach(i => {
|
||||||
|
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Polygon\", \"coordinates\": [[";
|
||||||
|
cells.v[i].forEach(n => {
|
||||||
|
let x = mapCoordinates.lonW + (v.p[n][0] / graphWidth) * mapCoordinates.lonT;
|
||||||
|
let y = mapCoordinates.latN - (v.p[n][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
||||||
|
data += "["+x+","+y+"],";
|
||||||
|
});
|
||||||
|
// close the ring
|
||||||
|
let x = mapCoordinates.lonW + (v.p[cells.v[i][0]][0] / graphWidth) * mapCoordinates.lonT;
|
||||||
|
let y = mapCoordinates.latN - (v.p[cells.v[i][0]][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
||||||
|
data += "["+x+","+y+"]";
|
||||||
|
data += "]] },\n \"properties\": {\n";
|
||||||
|
|
||||||
|
let height = parseInt(getFriendlyHeight([cells.p[i][0],cells.p[i][1]]));
|
||||||
|
|
||||||
|
data += " \"id\": \""+i+"\",\n";
|
||||||
|
data += " \"height\": \""+height+"\",\n";
|
||||||
|
data += " \"biome\": \""+cells.biome[i]+"\",\n";
|
||||||
|
data += " \"type\": \""+pack.features[cells.f[i]].type+"\",\n";
|
||||||
|
data += " \"population\": \""+getFriendlyPopulation(i)+"\",\n";
|
||||||
|
data += " \"state\": \""+cells.state[i]+"\",\n";
|
||||||
|
data += " \"province\": \""+cells.province[i]+"\",\n";
|
||||||
|
data += " \"culture\": \""+cells.culture[i]+"\",\n";
|
||||||
|
data += " \"religion\": \""+cells.religion[i]+"\"\n";
|
||||||
|
data +=" }\n},\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
|
||||||
|
data += "]}";
|
||||||
|
|
||||||
|
const dataBlob = new Blob([data], {type: "application/json"});
|
||||||
|
const url = window.URL.createObjectURL(dataBlob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.download = getFileName("Cells") + ".geojson";
|
||||||
|
link.href = url;
|
||||||
|
link.click();
|
||||||
|
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
function saveGeoJSON_Roads() {
|
function saveGeoJSON_Roads() {
|
||||||
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
||||||
|
|
||||||
|
|
@ -296,6 +342,34 @@ function saveGeoJSON_Rivers() {
|
||||||
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveGeoJSON_Markers() {
|
||||||
|
|
||||||
|
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
||||||
|
|
||||||
|
markers._groups[0][0].childNodes.forEach(n => {
|
||||||
|
let x = mapCoordinates.lonW + (n.dataset.x / graphWidth) * mapCoordinates.lonT;
|
||||||
|
let y = mapCoordinates.latN - (n.dataset.y / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
||||||
|
|
||||||
|
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Point\", \"coordinates\": ["+x+", "+y+"]";
|
||||||
|
data += " },\n \"properties\": {\n";
|
||||||
|
data += " \"id\": \""+n.id+"\",\n";
|
||||||
|
data += " \"type\": \""+n.dataset.id.substring(8)+"\"\n";
|
||||||
|
data +=" }\n},\n";
|
||||||
|
|
||||||
|
});
|
||||||
|
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
|
||||||
|
data += "]}";
|
||||||
|
|
||||||
|
const dataBlob = new Blob([data], {type: "application/json"});
|
||||||
|
const url = window.URL.createObjectURL(dataBlob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.download = getFileName("Markers") + ".geojson";
|
||||||
|
link.href = url;
|
||||||
|
link.click();
|
||||||
|
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
function getRoadPoints(node) {
|
function getRoadPoints(node) {
|
||||||
let points = [];
|
let points = [];
|
||||||
const l = node.getTotalLength();
|
const l = node.getTotalLength();
|
||||||
|
|
@ -413,49 +487,6 @@ function getFileName(dataType) {
|
||||||
return name + " " + type + day + " " + time;
|
return name + " " + type + day + " " + time;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveGeoJSON_Cells() {
|
|
||||||
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
|
||||||
const cells = pack.cells, v = pack.vertices;
|
|
||||||
|
|
||||||
cells.i.forEach(i => {
|
|
||||||
data += "{\n \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Polygon\", \"coordinates\": [[";
|
|
||||||
cells.v[i].forEach(n => {
|
|
||||||
let x = mapCoordinates.lonW + (v.p[n][0] / graphWidth) * mapCoordinates.lonT;
|
|
||||||
let y = mapCoordinates.latN - (v.p[n][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
|
||||||
data += "["+x+","+y+"],";
|
|
||||||
});
|
|
||||||
// close the ring
|
|
||||||
let x = mapCoordinates.lonW + (v.p[cells.v[i][0]][0] / graphWidth) * mapCoordinates.lonT;
|
|
||||||
let y = mapCoordinates.latN - (v.p[cells.v[i][0]][1] / graphHeight) * mapCoordinates.latT; // this is inverted in QGIS otherwise
|
|
||||||
data += "["+x+","+y+"]";
|
|
||||||
data += "]] },\n \"properties\": {\n";
|
|
||||||
|
|
||||||
let height = parseInt(getFriendlyHeight(cells.h[i]));
|
|
||||||
|
|
||||||
data += " \"id\": \""+i+"\",\n";
|
|
||||||
data += " \"height\": \""+height+"\",\n";
|
|
||||||
data += " \"biome\": \""+cells.biome[i]+"\",\n";
|
|
||||||
data += " \"population\": \""+cells.pop[i]+"\",\n";
|
|
||||||
data += " \"state\": \""+cells.state[i]+"\",\n";
|
|
||||||
data += " \"province\": \""+cells.province[i]+"\",\n";
|
|
||||||
data += " \"culture\": \""+cells.culture[i]+"\",\n";
|
|
||||||
data += " \"religion\": \""+cells.religion[i]+"\"\n";
|
|
||||||
data +=" }\n},\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
data = data.substring(0, data.length - 2)+"\n"; // remove trailing comma
|
|
||||||
data += "]}";
|
|
||||||
|
|
||||||
const dataBlob = new Blob([data], {type: "application/json"});
|
|
||||||
const url = window.URL.createObjectURL(dataBlob);
|
|
||||||
const link = document.createElement("a");
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.download = getFileName("Cells") + ".geojson";
|
|
||||||
link.href = url;
|
|
||||||
link.click();
|
|
||||||
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadFile(file, callback) {
|
function uploadFile(file, callback) {
|
||||||
uploadFile.timeStart = performance.now();
|
uploadFile.timeStart = performance.now();
|
||||||
|
|
||||||
|
|
@ -822,4 +853,4 @@ function parseLoadedData(data) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ function editBurgs() {
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
document.getElementById("burgsEditorRefresh").addEventListener("click", refreshBurgsEditor);
|
document.getElementById("burgsEditorRefresh").addEventListener("click", refreshBurgsEditor);
|
||||||
|
document.getElementById("burgsChart").addEventListener("click", showBurgsChart);
|
||||||
document.getElementById("burgsFilterState").addEventListener("change", burgsEditorAddLines);
|
document.getElementById("burgsFilterState").addEventListener("change", burgsEditorAddLines);
|
||||||
document.getElementById("burgsFilterCulture").addEventListener("change", burgsEditorAddLines);
|
document.getElementById("burgsFilterCulture").addEventListener("change", burgsEditorAddLines);
|
||||||
document.getElementById("regenerateBurgNames").addEventListener("click", regenerateNames);
|
document.getElementById("regenerateBurgNames").addEventListener("click", regenerateNames);
|
||||||
|
|
@ -250,6 +251,133 @@ function editBurgs() {
|
||||||
if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed");
|
if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showBurgsChart() {
|
||||||
|
// build hierarchy tree
|
||||||
|
const states = pack.states.map(s => {
|
||||||
|
const color = s.color ? s.color : "#ccc";
|
||||||
|
const name = s.fullName ? s.fullName : s.name;
|
||||||
|
return {id:s.i, state: s.i ? 0 : null, color, name}
|
||||||
|
});
|
||||||
|
const burgs = pack.burgs.filter(b => b.i && !b.removed).map(b => {
|
||||||
|
const id = b.i+states.length-1;
|
||||||
|
const population = b.population;
|
||||||
|
const capital = b.capital;
|
||||||
|
const province = pack.cells.province[b.cell];
|
||||||
|
const parent = province ? province + states.length-1 : b.state;
|
||||||
|
return {id, i:b.i, state:b.state, culture:b.culture, province, parent, name:b.name, population, capital, x:b.x, y:b.y}
|
||||||
|
});
|
||||||
|
const data = states.concat(burgs);
|
||||||
|
|
||||||
|
const root = d3.stratify().parentId(d => d.state)(data)
|
||||||
|
.sum(d => d.population).sort((a, b) => b.value - a.value);
|
||||||
|
|
||||||
|
const width = 150 + 200 * uiSizeOutput.value, height = 150 + 200 * uiSizeOutput.value;
|
||||||
|
const margin = {top: 0, right: -50, bottom: -10, left: -50};
|
||||||
|
const w = width - margin.left - margin.right;
|
||||||
|
const h = height - margin.top - margin.bottom;
|
||||||
|
const treeLayout = d3.pack().size([w, h]).padding(3);
|
||||||
|
|
||||||
|
// prepare svg
|
||||||
|
alertMessage.innerHTML = `<select id="burgsTreeType" style="display:block; margin-left:13px; font-size:11px">
|
||||||
|
<option value="states" selected>Group by state</option>
|
||||||
|
<option value="cultures">Group by culture</option>
|
||||||
|
<option value="parent">Group by province and state</option>
|
||||||
|
<option value="provinces">Group by province</option></select>`;
|
||||||
|
alertMessage.innerHTML += `<div id='burgsInfo' class='chartInfo'>‍</div>`;
|
||||||
|
const svg = d3.select("#alertMessage").insert("svg", "#burgsInfo").attr("id", "burgsTree")
|
||||||
|
.attr("width", width).attr("height", height-10).attr("stroke-width", 2);
|
||||||
|
const graph = svg.append("g").attr("transform", `translate(-50, -10)`);
|
||||||
|
document.getElementById("burgsTreeType").addEventListener("change", updateChart);
|
||||||
|
|
||||||
|
treeLayout(root);
|
||||||
|
|
||||||
|
const node = graph.selectAll("circle").data(root.leaves())
|
||||||
|
.join("circle").attr("data-id", d => d.data.i)
|
||||||
|
.attr("r", d => d.r).attr("fill", d => d.parent.data.color)
|
||||||
|
.attr("cx", d => d.x).attr("cy", d => d.y)
|
||||||
|
.on("mouseenter", d => showInfo(event, d))
|
||||||
|
.on("mouseleave", d => hideInfo(event, d))
|
||||||
|
.on("click", d => zoomTo(d.data.x, d.data.y, 8, 2000));
|
||||||
|
|
||||||
|
function showInfo(ev, d) {
|
||||||
|
d3.select(ev.target).transition().duration(1500).attr("stroke", "#c13119");
|
||||||
|
const name = d.data.name;
|
||||||
|
const parent = d.parent.data.name;
|
||||||
|
const population = si(d.value * populationRate.value * urbanization.value);
|
||||||
|
|
||||||
|
burgsInfo.innerHTML = `${name}. ${parent}. Population: ${population}`;
|
||||||
|
burgHighlightOn(ev);
|
||||||
|
tip("Click to zoom into view");
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideInfo(ev) {
|
||||||
|
burgHighlightOff(ev);
|
||||||
|
if (!document.getElementById("burgsInfo")) return;
|
||||||
|
burgsInfo.innerHTML = "‍";
|
||||||
|
d3.select(ev.target).transition().attr("stroke", "null");
|
||||||
|
tip("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateChart() {
|
||||||
|
const getStatesData = () => pack.states.map(s => {
|
||||||
|
const color = s.color ? s.color : "#ccc";
|
||||||
|
const name = s.fullName ? s.fullName : s.name;
|
||||||
|
return {id:s.i, state: s.i ? 0 : null, color, name}
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCulturesData = () => pack.cultures.map(c => {
|
||||||
|
const color = c.color ? c.color : "#ccc";
|
||||||
|
return {id:c.i, culture: c.i ? 0 : null, color, name:c.name}
|
||||||
|
});
|
||||||
|
|
||||||
|
const getParentData = () => {
|
||||||
|
const states = pack.states.map(s => {
|
||||||
|
const color = s.color ? s.color : "#ccc";
|
||||||
|
const name = s.fullName ? s.fullName : s.name;
|
||||||
|
return {id:s.i, parent: s.i ? 0 : null, color, name}
|
||||||
|
});
|
||||||
|
const provinces = pack.provinces.filter(p => p.i && !p.removed).map(p => {
|
||||||
|
return {id:p.i + states.length-1, parent: p.state, color:p.color, name:p.fullName}
|
||||||
|
});
|
||||||
|
return states.concat(provinces);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProvincesData = () => pack.provinces.map(p => {
|
||||||
|
const color = p.color ? p.color : "#ccc";
|
||||||
|
const name = p.fullName ? p.fullName : p.name;
|
||||||
|
return {id:p.i ? p.i : 0, province: p.i ? 0 : null, color, name}
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = d => {
|
||||||
|
if (this.value === "states") return d.state;
|
||||||
|
if (this.value === "cultures") return d.culture;
|
||||||
|
if (this.value === "parent") return d.parent;
|
||||||
|
if (this.value === "provinces") return d.province;
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = this.value === "states" ? getStatesData()
|
||||||
|
: this.value === "cultures" ? getCulturesData()
|
||||||
|
: this.value === "parent" ? getParentData() : getProvincesData();
|
||||||
|
burgs.forEach(b => b.id = b.i+base.length-1);
|
||||||
|
|
||||||
|
const data = base.concat(burgs);
|
||||||
|
|
||||||
|
const root = d3.stratify().parentId(d => value(d))(data)
|
||||||
|
.sum(d => d.population).sort((a, b) => b.value - a.value);
|
||||||
|
|
||||||
|
node.data(treeLayout(root).leaves()).transition().duration(2000)
|
||||||
|
.attr("data-id", d => d.data.i).attr("fill", d => d.parent.data.color)
|
||||||
|
.attr("cx", d => d.x).attr("cy", d => d.y).attr("r", d => d.r);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#alert").dialog({
|
||||||
|
title: "Burgs bubble chart", width: fitContent(),
|
||||||
|
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {},
|
||||||
|
close: () => {alertMessage.innerHTML = "";}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function downloadBurgsData() {
|
function downloadBurgsData() {
|
||||||
let data = "Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation ("+heightUnit.value+"),Capital,Port\n"; // headers
|
let data = "Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation ("+heightUnit.value+"),Capital,Port\n"; // headers
|
||||||
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
||||||
|
|
@ -259,7 +387,7 @@ function editBurgs() {
|
||||||
data += b.name + ",";
|
data += b.name + ",";
|
||||||
const province = pack.cells.province[b.cell];
|
const province = pack.cells.province[b.cell];
|
||||||
data += province ? pack.provinces[province].fullName + "," : ",";
|
data += province ? pack.provinces[province].fullName + "," : ",";
|
||||||
data += b.state ? pack.states[b.state].fullName : pack.states[b.state].name + ",";
|
data += b.state ? pack.states[b.state].fullName +"," : pack.states[b.state].name + ",";
|
||||||
data += pack.cultures[b.culture].name + ",";
|
data += pack.cultures[b.culture].name + ",";
|
||||||
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
|
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
|
||||||
data += rn(b.population * populationRate.value * urbanization.value) + ",";
|
data += rn(b.population * populationRate.value * urbanization.value) + ",";
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,42 @@ function applyOption(select, option) {
|
||||||
select.value = option;
|
select.value = option;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show info about the generator in a popup
|
||||||
|
function showInfo() {
|
||||||
|
const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord");
|
||||||
|
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit")
|
||||||
|
const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
|
||||||
|
const Trello = link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Trello");
|
||||||
|
|
||||||
|
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial");
|
||||||
|
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
|
||||||
|
|
||||||
|
alertMessage.innerHTML = `
|
||||||
|
<b>Fantasy Map Generator</b> (FMG) is an open-source application, it means the code is published an anyone can use it.
|
||||||
|
In case of FMG is also means that you own all created maps and can use them as you wish, you can even sell them.
|
||||||
|
|
||||||
|
<p>The development is supported by community, you can donate on ${Patreon}.
|
||||||
|
You can also help creating overviews, tutorials and spreding the word about the Generator.</p>
|
||||||
|
|
||||||
|
<p>The best way to get help is to contact the community on ${Discord} and ${Reddit}.
|
||||||
|
Before asking questions, please check out the ${QuickStart} and the ${QAA}.</p>
|
||||||
|
|
||||||
|
<p>You can track the development process on ${Trello}.</p>
|
||||||
|
|
||||||
|
Links:
|
||||||
|
<ul style="columns:2">
|
||||||
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}</li>
|
||||||
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}</li>
|
||||||
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "Changelog")}</li>
|
||||||
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys", "Hotkeys")}</li>
|
||||||
|
</ul>`;
|
||||||
|
|
||||||
|
$("#alert").dialog({resizable: false, title: document.title, width: "28em",
|
||||||
|
buttons: {OK: function() {$(this).dialog("close");}},
|
||||||
|
position: {my: "center", at: "center", of: "svg"}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// prevent default browser behavior for FMG-used hotkeys
|
// prevent default browser behavior for FMG-used hotkeys
|
||||||
document.addEventListener("keydown", event => {
|
document.addEventListener("keydown", event => {
|
||||||
if ([112, 113, 117, 120, 9].includes(event.keyCode)) event.preventDefault(); // F1, F2, F6, F9, Tab
|
if ([112, 113, 117, 120, 9].includes(event.keyCode)) event.preventDefault(); // F1, F2, F6, F9, Tab
|
||||||
|
|
@ -242,13 +278,14 @@ document.addEventListener("keyup", event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
|
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
|
||||||
if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
if (key === 112) showInfo(); // "F1" to show info
|
||||||
else if (key === 9) toggleOptions(event); // Tab to toggle options
|
|
||||||
|
|
||||||
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
||||||
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
|
else if (key === 113) regeneratePrompt(); // "F2" for a new map
|
||||||
else if (key === 117) quickSave(); // "F6" for quick save
|
else if (key === 117) quickSave(); // "F6" for quick save
|
||||||
else if (key === 120) quickLoad(); // "F9" for quick load
|
else if (key === 120) quickLoad(); // "F9" for quick load
|
||||||
|
else if (key === 9) toggleOptions(event); // Tab to toggle options
|
||||||
|
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
||||||
|
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
|
||||||
|
|
||||||
else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG
|
else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG
|
||||||
else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG
|
else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ function editHeightmap() {
|
||||||
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
|
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
|
||||||
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
|
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
|
||||||
|
|
||||||
<p>Check out <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization" target="_blank">wiki</a> for guidance.</p>
|
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>
|
||||||
|
|
||||||
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before edditing the heightmap!</p>`;
|
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before edditing the heightmap!</p>`;
|
||||||
|
|
||||||
$("#alert").dialog({resizable: false, title: "Edit Heightmap", width: "28em",
|
$("#alert").dialog({resizable: false, title: "Edit Heightmap", width: "28em",
|
||||||
|
|
|
||||||
|
|
@ -286,8 +286,10 @@ function editProvinces() {
|
||||||
const states = pack.states.map(s => {
|
const states = pack.states.map(s => {
|
||||||
return {id:s.i, state: s.i?0:null, color: s.i && s.color[0] === "#" ? d3.color(s.color).darker() : "#666"}
|
return {id:s.i, state: s.i?0:null, color: s.i && s.color[0] === "#" ? d3.color(s.color).darker() : "#666"}
|
||||||
});
|
});
|
||||||
const provinces = pack.provinces.filter(p => p.i && !p.removed);
|
const provinces = pack.provinces.filter(p => p.i && !p.removed).map(p => {
|
||||||
provinces.forEach(p => p.id = p.i + states.length - 1);
|
return {id:p.i+states.length-1, i:p.i, state:p.state, color:p.color,
|
||||||
|
name:p.name, fullName:p.fullName, area:p.area, urban:p.urban, rural:p.rural}
|
||||||
|
});
|
||||||
const data = states.concat(provinces);
|
const data = states.concat(provinces);
|
||||||
const root = d3.stratify().parentId(d => d.state)(data).sum(d => d.area);
|
const root = d3.stratify().parentId(d => d.state)(data).sum(d => d.area);
|
||||||
|
|
||||||
|
|
@ -371,9 +373,9 @@ function editProvinces() {
|
||||||
: this.value === "rural" ? d => d.rural
|
: this.value === "rural" ? d => d.rural
|
||||||
: this.value === "urban" ? d => d.urban
|
: this.value === "urban" ? d => d.urban
|
||||||
: d => d.rural + d.urban;
|
: d => d.rural + d.urban;
|
||||||
|
|
||||||
const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value);
|
root.sum(value);
|
||||||
node.data(treeLayout(newRoot).leaves());
|
node.data(treeLayout(root).leaves());
|
||||||
|
|
||||||
node.select("rect").transition().duration(1500)
|
node.select("rect").transition().duration(1500)
|
||||||
.attr("x", d => d.x0).attr("y", d => d.y0)
|
.attr("x", d => d.x0).attr("y", d => d.y0)
|
||||||
|
|
|
||||||
|
|
@ -419,7 +419,7 @@ function editStates() {
|
||||||
|
|
||||||
const node = graph.selectAll("g").data(root.leaves()).enter()
|
const node = graph.selectAll("g").data(root.leaves()).enter()
|
||||||
.append("g").attr("transform", d => `translate(${d.x},${d.y})`)
|
.append("g").attr("transform", d => `translate(${d.x},${d.y})`)
|
||||||
.attr("data-id", d => d.data.id)
|
.attr("data-id", d => d.data.i)
|
||||||
.on("mouseenter", d => showInfo(event, d))
|
.on("mouseenter", d => showInfo(event, d))
|
||||||
.on("mouseleave", d => hideInfo(event, d));
|
.on("mouseleave", d => hideInfo(event, d));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -543,5 +543,10 @@ function getAbsolutePath(href) {
|
||||||
return link.href;
|
return link.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wrap URL into html a element
|
||||||
|
function link(URL, description) {
|
||||||
|
return `<a href="${URL}" target="_blank">${description}</a>`
|
||||||
|
}
|
||||||
|
|
||||||
// localStorageDB
|
// localStorageDB
|
||||||
!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();
|
!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();
|
||||||
Loading…
Add table
Add a link
Reference in a new issue