mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
v1.0.09
This commit is contained in:
parent
baf23bee37
commit
e743735e57
11 changed files with 789 additions and 546 deletions
20
index.css
20
index.css
|
|
@ -996,7 +996,7 @@ div.slider .ui-slider-handle {
|
|||
top: 100%;
|
||||
border: 1px solid #5e4fa2;
|
||||
background-color: #a4879b;
|
||||
width: 3.6em;
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
#loadDropdown {
|
||||
|
|
@ -1006,7 +1006,7 @@ div.slider .ui-slider-handle {
|
|||
top: 100%;
|
||||
border: 1px solid #5e4fa2;
|
||||
background-color: #a4879b;
|
||||
width: 6em;
|
||||
width: 9em;
|
||||
}
|
||||
|
||||
#loadDropdown>div,
|
||||
|
|
@ -1510,6 +1510,7 @@ div.states > div.biomeArea {
|
|||
cursor: default;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#title_name {
|
||||
|
|
@ -1808,6 +1809,12 @@ svg.button {
|
|||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#info-line {
|
||||
font-size: .9em;
|
||||
color: gray;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.optionsSeedRestore {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
|
|
@ -1922,6 +1929,15 @@ svg.button {
|
|||
margin: 10px 0 0 7px;
|
||||
}
|
||||
|
||||
#errorBox {
|
||||
font-size: .9em;
|
||||
font-family: monospace;
|
||||
color: #920303;
|
||||
background-color: #dabdbd91;
|
||||
padding: 2px;
|
||||
border: 1px solid #916e7f;
|
||||
}
|
||||
|
||||
#debug {
|
||||
font-size: 1px;
|
||||
opacity: 0.8;
|
||||
|
|
|
|||
49
index.html
49
index.html
|
|
@ -882,7 +882,7 @@
|
|||
|
||||
<div id="collapsible">
|
||||
<button id="optionsTrigger" data-tip="Click to show options pane. Shortcut: Tab" class="options glow" onclick="showOptions(event)" style="padding:7px 5px">►</button>
|
||||
<button id="regenerate" data-tip="Click to generate a new map. Shortcut: F7" onclick="regeneratePrompt()" class="options" style="display:none; padding:7px 8px">New Map!</button>
|
||||
<button id="regenerate" data-tip="Click to generate a new map. Shortcut: F2" onclick="regeneratePrompt()" class="options" style="display:none; padding:7px 8px">New Map!</button>
|
||||
</div>
|
||||
|
||||
<div id="options" style="display:none">
|
||||
|
|
@ -909,7 +909,8 @@
|
|||
<option value="landmass">Pure landmass</option>
|
||||
<option hidden value="custom">Custom (not saved)</option>
|
||||
</select>
|
||||
<button id="savePreset" data-tip="Click to save displayed layers as a new preset" class="icon-plus styleButton" onclick="savePreset()"></button>
|
||||
<button id="savePresetButton" data-tip="Click to save displayed layers as a new preset" class="icon-plus styleButton" style="display:none" onclick="savePreset()"></button>
|
||||
<button id="removePresetButton" data-tip="Click to remove current custom preset" class="icon-minus styleButton" style="display:none" onclick="removePreset()"></button>
|
||||
|
||||
<p data-tip="Click to toggle a layer, drag to raise or lower a layer">Displayed layers:</p>
|
||||
<ul data-tip="Click to toggle a layer, drag to raise or lower a layer" id="mapLayers">
|
||||
|
|
@ -1470,7 +1471,7 @@
|
|||
</div>
|
||||
|
||||
<div id="optionsContent" class="tabcontent">
|
||||
<p data-tip="Map generation settings. Generate a new map to apply the settings">Map Generation (new map to apply):</p>
|
||||
<p data-tip="Map generation settings. Generate a new map to apply the settings">Map settings (new map to apply):</p>
|
||||
<table>
|
||||
|
||||
<tr data-tip="Map height and width in pixels. Please consider reducing map size in case of performance issues">
|
||||
|
|
@ -1563,7 +1564,7 @@
|
|||
<td>
|
||||
<i data-locked=0 id="lock_provinces" class="icon-lock-open"></i>
|
||||
</td>
|
||||
<td>Provinces number</td>
|
||||
<td>Provinces ratio</td>
|
||||
<td>
|
||||
<input id="provincesInput" data-stored="provinces" type="range" min=0 max=100 value=30>
|
||||
</td>
|
||||
|
|
@ -1585,7 +1586,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Define state and cultures growth rate. Defines how many lands will stay neutral">
|
||||
<tr data-tip="Set state and cultures growth rate. Defines how many lands will stay neutral">
|
||||
<td>
|
||||
<i data-locked=0 id="lock_neutral" class="icon-lock-open"></i>
|
||||
</td>
|
||||
|
|
@ -1598,7 +1599,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Define how many towns (non-capital burgs) should be generated">
|
||||
<tr data-tip="Define a number of towns to be placed (if suitable area is enougth)">
|
||||
<td>
|
||||
<i data-locked=0 id="lock_manors" class="icon-lock-open"></i>
|
||||
</td>
|
||||
|
|
@ -1611,7 +1612,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Define how many organized religions and cults should be generated">
|
||||
<tr data-tip="Define how many organized (!) religions and cults should be generated">
|
||||
<td>
|
||||
<i data-locked=0 id="lock_religions" class="icon-lock-open"></i>
|
||||
</td>
|
||||
|
|
@ -1625,16 +1626,28 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<p data-tip="Interface settings that don't affect generation. They are applied immediately on change">User Interface:</p>
|
||||
<p data-tip="Tool settings that don't affect maps. Changes are getting applied immediately">Generator settings:</p>
|
||||
<table>
|
||||
<tr data-tip="Set user interface size">
|
||||
<tr data-tip="Set what Generator should do on opening">
|
||||
<td></td>
|
||||
<td>Onload behavior</td>
|
||||
<td>
|
||||
<select id="onloadMap" data-stored="onloadMap">
|
||||
<option value="random" selected>Generate random map</option>
|
||||
<option value="saved">Open last saved map</option>
|
||||
</select>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr data-tip="Set user interface size. Please note browser zoom also affects interface size (Ctrl + or Ctrl - to change)">
|
||||
<td></td>
|
||||
<td>Interface size</td>
|
||||
<td>
|
||||
<input id="uiSizeInput" data-stored="uiSize" type="range" min=.6 max=1.4 step=.1 value=1>
|
||||
<input id="uiSizeInput" data-stored="uiSize" type="range" min=.8 max=2 step=.1 value=1>
|
||||
</td>
|
||||
<td>
|
||||
<input id="uiSizeOutput" data-stored="uiSize" type="number" min=.6 max=1.4 step=.1 value=1>
|
||||
<input id="uiSizeOutput" data-stored="uiSize" type="number" min=.6 max=2 step=.1 value=1>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
@ -1819,18 +1832,20 @@
|
|||
</div>
|
||||
|
||||
<div id="sticked">
|
||||
<button id="newMapButton" data-tip="Generate a new map based on options. Shortcut: F7">New Map</button>
|
||||
<button id="newMapButton" data-tip="Generate a new map based on options. Shortcut: F2">New Map</button>
|
||||
<button id="saveButton" data-tip="Select file format to save map">Save</button>
|
||||
<div id="saveDropdown">
|
||||
<div id="saveMap" data-tip="Download the map as fully functional .map file to your machine. Shortcut: Ctrl + M">.map</div>
|
||||
<div id="saveSVG" data-tip="Download the map as vector image (open in browser or Inkscape). Shortcut: Ctrl + S">.svg</div>
|
||||
<div id="savePNG" data-tip="Download visible part of the map as .png image. Texture will not be shown. Shortcut: Ctrl + P">.png</div>
|
||||
<div id="savePNG" data-tip="Download visible part of the map as .png image. Shortcut: Ctrl + P">.png</div>
|
||||
<div id="quickSave" data-tip="Save map to browser storage. Shortcut: F6">storage</div>
|
||||
<!-- <div id="saveDropbox" data-tip="Save fully functional .map file to Dropbox. Shortcut: Ctrl + B">Dropbox</div> -->
|
||||
</div>
|
||||
<button id="loadButton" data-tip="Load fully functional map in a .map format">Load</button>
|
||||
<div id="loadDropdown">
|
||||
<div id="loadMap" data-tip="Load .map file from local disk. Shortcut: Ctrl + L">from disk</div>
|
||||
<div id="loadMap" data-tip="Load .map file from local disk. Shortcut: Ctrl + L">from local disk</div>
|
||||
<div id="loadURL" data-tip="Load .map file from URL (server should allow CORS). Shortcut: Ctrl + U">from URL</div>
|
||||
<div id="quickLoad" data-tip="Load map from browser storage (if saved before). Shortcut: F9">from storage</div>
|
||||
<!-- <div id="loadDropbox" data-tip="Load .map file from Dropbox. Shortcut: Ctrl + D">from Dropbox</div> -->
|
||||
</div>
|
||||
<button id="zoomReset" data-tip="Reset map zoom. Shortcut: 0">Reset Zoom</button>
|
||||
|
|
@ -2006,7 +2021,7 @@
|
|||
<span id="riverReset" data-tip="Reset transformation to default" class="icon-cancel pointer"></span>
|
||||
</div>
|
||||
|
||||
<button id="riverLength" data-tip="Route length in selected units">0</button>
|
||||
<button id="riverLength" data-tip="River length in selected units">0</button>
|
||||
<button id="riverCopy" data-tip="Copy river" class="icon-clone"></button>
|
||||
<button id="riverNew" data-tip="Create new river clicking on map" class="icon-map-pin"></button>
|
||||
<button id="riverLegend" data-tip="Edit free text notes (legend) for the river" class="icon-edit"></button>
|
||||
|
|
@ -2022,7 +2037,7 @@
|
|||
<span id="routeGroupAdd" data-tip="Create new group for this route" class="icon-plus pointer"></span>
|
||||
<span id="routeGroupRemove" data-tip="Remove all routes of this group" class="icon-trash-empty pointer"></span>
|
||||
</div>
|
||||
<button id="routeLength" data-tip="River length in selected units">0</button>
|
||||
<button id="routeLength" data-tip="Route length in selected units">0</button>
|
||||
<button id="routeSplit" data-tip="Click on a control point to split the route" class="icon-unlink"></button>
|
||||
<button id="routeLegend" data-tip="Edit free text notes (legend) for the route" class="icon-edit"></button>
|
||||
<button id="routeNew" data-tip="Create new route clicking on map" class="icon-map-pin"></button>
|
||||
|
|
@ -2952,8 +2967,8 @@
|
|||
<script src="libs/polylabel.min.js"></script>
|
||||
<script src="libs/jquery-ui.min.js"></script>
|
||||
<script src="libs/seedrandom.min.js"></script>
|
||||
|
||||
<script src="modules/ui/layers.js"></script>
|
||||
|
||||
<script defer src="modules/ui/general.js"></script>
|
||||
<script defer src="modules/ui/options.js"></script>
|
||||
<script defer src="modules/ui/measurers.js"></script>
|
||||
|
|
|
|||
410
main.js
410
main.js
|
|
@ -8,7 +8,13 @@
|
|||
|
||||
"use strict";
|
||||
const version = "1.0"; // generator version
|
||||
document.title += " v " + version;
|
||||
document.title += " v" + version;
|
||||
|
||||
// if map version is not stored, clear localStorage and show a message
|
||||
if (localStorage.getItem("version") != version) {
|
||||
localStorage.clear();
|
||||
setTimeout(showWelcomeMessage, 8000);
|
||||
}
|
||||
|
||||
// append svg layers (in default order)
|
||||
let svg = d3.select("#map");
|
||||
|
|
@ -111,20 +117,58 @@ oceanPattern.append("rect").attr("fill", "url(#oceanic)").attr("x", 0).attr("y",
|
|||
oceanLayers.append("rect").attr("id", "oceanBase").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
||||
|
||||
applyDefaultNamesData(); // always apply default namesbase load as namesdata is not stored in .map file
|
||||
void function removeLoading() {
|
||||
d3.select("#loading").transition().duration(5000).style("opacity", 0).remove();
|
||||
d3.select("#initial").transition().duration(5000).attr("opacity", 0).remove();
|
||||
d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1);
|
||||
d3.select("#tooltip").transition().duration(3000).style("opacity", 1);
|
||||
}()
|
||||
|
||||
// load linked map from url or generate a random map
|
||||
// decide which map should be loaded or generated on page load
|
||||
void function checkLoadParameters() {
|
||||
const url = new URL(window.location.href);
|
||||
const maplink = url.searchParams.get("maplink");
|
||||
const params = url.searchParams;
|
||||
|
||||
// check if URL is valid
|
||||
if (maplink) {
|
||||
// of there is a valid maplink, try to load .map file from URL
|
||||
if (params.get("maplink")) {
|
||||
console.warn("Load map from URL");
|
||||
const maplink = params.get("maplink");
|
||||
const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
|
||||
const valid = pattern.test(maplink);
|
||||
if (valid) {loadMapFromURL(maplink, 1); return;} else
|
||||
showUploadErrorMessage("Map link is a valid URL", maplink);
|
||||
if (valid) {loadMapFromURL(maplink, 1); return;}
|
||||
else showUploadErrorMessage("Map link is not a valid URL", maplink);
|
||||
}
|
||||
generateRandomMapOnLoad();
|
||||
|
||||
// if there is a seed (user of MFCG provided), generate map for it
|
||||
if (params.get("seed")) {
|
||||
console.warn("Generate map for seed");
|
||||
generateMapOnLoad();
|
||||
return;
|
||||
}
|
||||
|
||||
// open latest map if option is active and map is stored
|
||||
if (onloadMap.value === "saved") {
|
||||
ldb.get("lastMap", blob => {
|
||||
if (blob) {
|
||||
console.warn("Load last saved map");
|
||||
try {
|
||||
uploadFile(blob);
|
||||
}
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
console.warn("Cannot load stored map, random map to be generated");
|
||||
generateMapOnLoad();
|
||||
}
|
||||
} else {
|
||||
console.error("No map stored, random map to be generated");
|
||||
generateMapOnLoad();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn("Generate random map");
|
||||
generateMapOnLoad();
|
||||
}()
|
||||
|
||||
function loadMapFromURL(maplink, random) {
|
||||
|
|
@ -137,25 +181,104 @@ function loadMapFromURL(maplink, random) {
|
|||
}).then(blob => uploadFile(blob))
|
||||
.catch(error => {
|
||||
showUploadErrorMessage(error.message, URL, random);
|
||||
if (random) generateRandomMapOnLoad();
|
||||
if (random) generateMapOnLoad();
|
||||
});
|
||||
}
|
||||
|
||||
function showUploadErrorMessage(error, URL, random) {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
alertMessage.innerHTML = `
|
||||
Cannot load map from the <a href='${URL}' target='_blank'>link provided</a>.
|
||||
${random?`A new random map is generated. `:''}
|
||||
Please ensure the linked file is reachable`;
|
||||
Please ensure the linked file is reachable and CORS is allowed on server side`;
|
||||
$("#alert").dialog({title: "Loading error", width: 320, buttons: {OK: function() {$(this).dialog("close");}}});
|
||||
}
|
||||
|
||||
function generateRandomMapOnLoad() {
|
||||
function generateMapOnLoad() {
|
||||
applyDefaultStyle(); // apply style
|
||||
generate(); // generate map
|
||||
focusOn(); // based on searchParams focus on point, cell or burg from MFCG
|
||||
applyPreset(); // apply saved layers preset
|
||||
setTimeout(showWelcomeMessage, 7000); // show welcome message if required
|
||||
}
|
||||
|
||||
// focus on coordinates, cell or burg provided in searchParams
|
||||
function focusOn() {
|
||||
const url = new URL(window.location.href);
|
||||
const params = url.searchParams;
|
||||
|
||||
if (params.get("from") === "MFCG") {
|
||||
if (params.get("seed").length === 13) {
|
||||
// show back burg from MFCG
|
||||
params.set("burg", params.get("seed").slice(-4));
|
||||
} else {
|
||||
// select burg for MFCG
|
||||
findBurgForMFCG(params);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const s = +params.get("scale") || 8;
|
||||
let x = +params.get("x");
|
||||
let y = +params.get("y");
|
||||
|
||||
const c = +params.get("cell");
|
||||
if (c) {
|
||||
x = pack.cells.p[c][0];
|
||||
y = pack.cells.p[c][1];
|
||||
}
|
||||
|
||||
const b = +params.get("burg");
|
||||
if (b && pack.burgs[b]) {
|
||||
x = pack.burgs[b].x;
|
||||
y = pack.burgs[b].y;
|
||||
}
|
||||
|
||||
if (x && y) zoomTo(x, y, s, 1600);
|
||||
}
|
||||
|
||||
// find burg for MFCG and focus on it
|
||||
function findBurgForMFCG(params) {
|
||||
const cells = pack.cells, burgs = pack.burgs;
|
||||
if (pack.burgs.length < 2) {console.error("Cannot select a burg for MFCG"); return;}
|
||||
|
||||
const size = +params.get("size");
|
||||
const name = params.get("name");
|
||||
let coast = +params.get("coast");
|
||||
let port = +params.get("port");
|
||||
let river = +params.get("river");
|
||||
|
||||
let selection = defineSelection(coast, port, river);
|
||||
if (!selection.length) selection = defineSelection(coast, !port, !river);
|
||||
if (!selection.length) selection = defineSelection(!coast, 0, !river);
|
||||
if (!selection.length) selection = [burgs[1]]; // select first if nothing is found
|
||||
|
||||
function defineSelection(coast, port, river) {
|
||||
if (port && river) return burgs.filter(b => b.port && cells.r[b.cell]);
|
||||
if (!port && coast && river) return burgs.filter(b => !b.port && cells.t[b.cell] === 1 && cells.r[b.cell]);
|
||||
if (!coast && !river) return burgs.filter(b => cells.t[b.cell] !== 1 && !cells.r[b.cell]);
|
||||
if (!coast && river) return burgs.filter(b => cells.t[b.cell] !== 1 && cells.r[b.cell]);
|
||||
if (coast && river) return burgs.filter(b => cells.t[b.cell] === 1 && cells.r[b.cell]);
|
||||
return [];
|
||||
}
|
||||
|
||||
// select a burg with closest population from selection
|
||||
const selected = d3.scan(selection, (a, b) => Math.abs(a.population - size) - Math.abs(b.population - size));
|
||||
const b = selection[selected].i;
|
||||
if (!b) {console.error("Cannot select a burg for MFCG"); return;}
|
||||
if (size) burgs[b].population = size;
|
||||
if (name) burgs[b].name = name;
|
||||
|
||||
const label = burgLabels.select("[data-id='" + b + "']");
|
||||
if (label.size()) {
|
||||
tip("Here stands the glorious city of " + burgs[b].name, true, "success", 12000);
|
||||
label.text(burgs[b].name).classed("drag", true).on("mouseover", function() {
|
||||
d3.select(this).classed("drag", false);
|
||||
label.on("mouseover", null);
|
||||
});
|
||||
}
|
||||
|
||||
zoomTo(burgs[b].x, burgs[b].y, 8, 1600);
|
||||
invokeActiveZooming();
|
||||
}
|
||||
|
||||
function applyDefaultNamesData() {
|
||||
|
|
@ -354,133 +477,45 @@ function applyDefaultStyle() {
|
|||
fogging.attr("opacity", .8).attr("fill", "#000000").attr("stroke-width", 5);
|
||||
}
|
||||
|
||||
// focus on coordinates, cell or burg provided in searchParams
|
||||
function focusOn() {
|
||||
const url = new URL(window.location.href);
|
||||
const params = url.searchParams;
|
||||
|
||||
if (params.get("from") === "MFCG") {
|
||||
if (params.get("seed").length === 13) {
|
||||
// show back burg from MFCG
|
||||
params.set("burg", params.get("seed").slice(-4));
|
||||
} else {
|
||||
// select burg for MFCG
|
||||
findBurgForMFCG(params);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const s = +params.get("scale") || 8;
|
||||
let x = +params.get("x");
|
||||
let y = +params.get("y");
|
||||
|
||||
const c = +params.get("cell");
|
||||
if (c) {
|
||||
x = pack.cells.p[c][0];
|
||||
y = pack.cells.p[c][1];
|
||||
}
|
||||
|
||||
const b = +params.get("burg");
|
||||
if (b && pack.burgs[b]) {
|
||||
x = pack.burgs[b].x;
|
||||
y = pack.burgs[b].y;
|
||||
}
|
||||
|
||||
if (x && y) zoomTo(x, y, s, 1600);
|
||||
}
|
||||
|
||||
// find burg for MFCG and focus on it
|
||||
function findBurgForMFCG(params) {
|
||||
const cells = pack.cells, burgs = pack.burgs;
|
||||
if (pack.burgs.length < 2) {console.error("Cannot select a burg for MFCG"); return;}
|
||||
|
||||
const size = +params.get("size");
|
||||
const name = params.get("name");
|
||||
let coast = +params.get("coast");
|
||||
let port = +params.get("port");
|
||||
let river = +params.get("river");
|
||||
|
||||
let selection = defineSelection(coast, port, river);
|
||||
if (!selection.length) selection = defineSelection(coast, !port, !river);
|
||||
if (!selection.length) selection = defineSelection(!coast, 0, !river);
|
||||
if (!selection.length) selection = [burgs[1]]; // select first if nothing is found
|
||||
|
||||
function defineSelection(coast, port, river) {
|
||||
if (port && river) return burgs.filter(b => b.port && cells.r[b.cell]);
|
||||
if (!port && coast && river) return burgs.filter(b => !b.port && cells.t[b.cell] === 1 && cells.r[b.cell]);
|
||||
if (!coast && !river) return burgs.filter(b => cells.t[b.cell] !== 1 && !cells.r[b.cell]);
|
||||
if (!coast && river) return burgs.filter(b => cells.t[b.cell] !== 1 && cells.r[b.cell]);
|
||||
if (coast && river) return burgs.filter(b => cells.t[b.cell] === 1 && cells.r[b.cell]);
|
||||
return [];
|
||||
}
|
||||
|
||||
// select a burg with closest population from selection
|
||||
const selected = d3.scan(selection, (a, b) => Math.abs(a.population - size) - Math.abs(b.population - size));
|
||||
const b = selection[selected].i;
|
||||
if (!b) {console.error("Cannot select a burg for MFCG"); return;}
|
||||
if (size) burgs[b].population = size;
|
||||
if (name) burgs[b].name = name;
|
||||
|
||||
const label = burgLabels.select("[data-id='" + b + "']");
|
||||
if (label.size()) {
|
||||
tip("Here stands the glorious city of " + burgs[b].name, true, "success", 12000);
|
||||
label.text(burgs[b].name).classed("drag", true).on("mouseover", function() {
|
||||
d3.select(this).classed("drag", false);
|
||||
label.on("mouseover", null);
|
||||
});
|
||||
}
|
||||
|
||||
zoomTo(burgs[b].x, burgs[b].y, 8, 1600);
|
||||
invokeActiveZooming();
|
||||
}
|
||||
|
||||
function showWelcomeMessage() {
|
||||
// Changelog dialog window
|
||||
if (localStorage.getItem("version") != version) {
|
||||
const link = 'https://www.reddit.com/r/FantasyMapGenerator/comments/cxu1c5/update_new_version_is_published_v_10'; // announcement on Reddit
|
||||
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
|
||||
const link = 'https://www.reddit.com/r/FantasyMapGenerator/comments/cxu1c5/update_new_version_is_published_v_10'; // announcement on Reddit
|
||||
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
|
||||
|
||||
This version is compatible with versions 0.8b and 0.9b, but not with older .map files.
|
||||
Please use an <a href='https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog' target='_blank'>archived version</a> to open old files.
|
||||
This version is compatible with versions 0.8b and 0.9b, but not with older .map files.
|
||||
Please use an <a href='https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog' target='_blank'>archived version</a> to open old files.
|
||||
|
||||
<ul><a href=${link} target='_blank'>Main changes:</a>
|
||||
<li>Provinces and Provinces Editor</li>
|
||||
<li>Religions Layer and Religions Editor</li>
|
||||
<li>Full state names (state types)</li>
|
||||
<li>Multi-lined labels</li>
|
||||
<li>State relations (diplomacy)</li>
|
||||
<li>Custom layers (zones)</li>
|
||||
<li>Places of interest (auto-added markers)</li>
|
||||
<li>New color picker and hatching fill</li>
|
||||
<li>Legend boxes</li>
|
||||
<li>World Configurator presets</li>
|
||||
<li>Improved state labels placement</li>
|
||||
<li>Relief icons sets</li>
|
||||
<li>Fogging</li>
|
||||
<li>Custom layer presets</li>
|
||||
<li>Custom biomes</li>
|
||||
<li>State, province and burg COAs</li>
|
||||
<li>Desktop version (see <a href='https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A#is-there-a-desktop-version' target='_blank'>here)</a></li>
|
||||
</ul>
|
||||
<ul><a href=${link} target='_blank'>Main changes:</a>
|
||||
<li>Provinces and Provinces Editor</li>
|
||||
<li>Religions Layer and Religions Editor</li>
|
||||
<li>Full state names (state types)</li>
|
||||
<li>Multi-lined labels</li>
|
||||
<li>State relations (diplomacy)</li>
|
||||
<li>Custom layers (zones)</li>
|
||||
<li>Places of interest (auto-added markers)</li>
|
||||
<li>New color picker and hatching fill</li>
|
||||
<li>Legend boxes</li>
|
||||
<li>World Configurator presets</li>
|
||||
<li>Improved state labels placement</li>
|
||||
<li>Relief icons sets</li>
|
||||
<li>Fogging</li>
|
||||
<li>Custom layer presets</li>
|
||||
<li>Custom biomes</li>
|
||||
<li>State, province and burg COAs</li>
|
||||
<li>Desktop version (see <a href='https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A#is-there-a-desktop-version' target='_blank'>here)</a></li>
|
||||
</ul>
|
||||
|
||||
<p>Join our <a href='https://www.reddit.com/r/FantasyMapGenerator' target='_blank'>Reddit community</a> and
|
||||
<a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a>
|
||||
to ask questions, share maps, discuss the Generator, report bugs and propose new features.</p>
|
||||
<p>Join our <a href='https://www.reddit.com/r/FantasyMapGenerator' target='_blank'>Reddit community</a> and
|
||||
<a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a>
|
||||
to ask questions, share maps, discuss the Generator, report bugs and propose new features.</p>
|
||||
|
||||
<p>Thanks for all supporters on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>!</i></p>`;
|
||||
<p>Thanks for all supporters on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>!</i></p>`;
|
||||
|
||||
$("#alert").dialog(
|
||||
{resizable: false, title: "Fantasy Map Generator update", width: 310,
|
||||
buttons: {
|
||||
OK: function() {
|
||||
localStorage.clear();
|
||||
localStorage.setItem("version", version);
|
||||
$(this).dialog("close");
|
||||
}
|
||||
},
|
||||
position: {my: "center", at: "center", of: "svg"}
|
||||
});
|
||||
}
|
||||
$("#alert").dialog(
|
||||
{resizable: false, title: "Fantasy Map Generator update", width: 310,
|
||||
buttons: {OK: function() {$(this).dialog("close")}},
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
close: () => localStorage.setItem("version", version)}
|
||||
);
|
||||
}
|
||||
|
||||
function zoomed() {
|
||||
|
|
@ -601,6 +636,7 @@ void function addDragToUpload() {
|
|||
}
|
||||
// all good - show uploading text and load the map
|
||||
$("#map-dragged > p").text("Uploading<span>.</span><span>.</span><span>.</span>");
|
||||
closeDialogs();
|
||||
uploadFile(file, function onUploadFinish() {
|
||||
$("#map-dragged > p").text("Drop to upload");
|
||||
});
|
||||
|
|
@ -608,45 +644,65 @@ void function addDragToUpload() {
|
|||
}()
|
||||
|
||||
function generate() {
|
||||
const timeStart = performance.now();
|
||||
console.time("TOTAL");
|
||||
invokeActiveZooming();
|
||||
generateSeed();
|
||||
console.group("Map " + seed);
|
||||
applyMapSize();
|
||||
randomizeOptions();
|
||||
placePoints();
|
||||
calculateVoronoi(grid, grid.points);
|
||||
drawScaleBar();
|
||||
HeightmapGenerator.generate();
|
||||
markFeatures();
|
||||
openNearSeaLakes();
|
||||
OceanLayers();
|
||||
calculateMapCoordinates();
|
||||
calculateTemperatures();
|
||||
generatePrecipitation();
|
||||
reGraph();
|
||||
drawCoastline();
|
||||
try {
|
||||
const timeStart = performance.now();
|
||||
invokeActiveZooming();
|
||||
generateSeed();
|
||||
console.group("Generated Map " + seed);
|
||||
applyMapSize();
|
||||
randomizeOptions();
|
||||
placePoints();
|
||||
calculateVoronoi(grid, grid.points);
|
||||
drawScaleBar();
|
||||
HeightmapGenerator.generate();
|
||||
markFeatures();
|
||||
openNearSeaLakes();
|
||||
OceanLayers();
|
||||
calculateMapCoordinates();
|
||||
calculateTemperatures();
|
||||
generatePrecipitation();
|
||||
reGraph();
|
||||
drawCoastline();
|
||||
|
||||
elevateLakes();
|
||||
Rivers.generate();
|
||||
defineBiomes();
|
||||
|
||||
rankCells();
|
||||
Cultures.generate();
|
||||
Cultures.expand();
|
||||
BurgsAndStates.generate();
|
||||
Religions.generate();
|
||||
|
||||
drawStates();
|
||||
drawBorders();
|
||||
BurgsAndStates.drawStateLabels();
|
||||
addZone();
|
||||
addMarkers();
|
||||
|
||||
elevateLakes();
|
||||
Rivers.generate();
|
||||
defineBiomes();
|
||||
console.warn(`TOTAL: ${rn((performance.now()-timeStart)/1000,2)}s`);
|
||||
showStatistics();
|
||||
console.groupEnd("Generated Map " + seed);
|
||||
}
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
clearMainTip();
|
||||
|
||||
rankCells();
|
||||
Cultures.generate();
|
||||
Cultures.expand();
|
||||
BurgsAndStates.generate();
|
||||
Religions.generate();
|
||||
const regex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
|
||||
const errorNoURL = error.stack.replace(regex, url => '<i>' + last(url.split("/")) + '</i>');
|
||||
const errorParsed = errorNoURL.replace(/at /ig, "<br> at ");
|
||||
|
||||
drawStates();
|
||||
drawBorders();
|
||||
BurgsAndStates.drawStateLabels();
|
||||
addZone();
|
||||
addMarkers();
|
||||
|
||||
console.warn(`TOTAL: ${rn((performance.now()-timeStart)/1000,2)}s`);
|
||||
showStatistics();
|
||||
console.groupEnd("Map " + seed);
|
||||
alertMessage.innerHTML = `An error is occured on map generation. Please retry.
|
||||
<br>If error persists, clear the stored data and try again.
|
||||
<p id="errorBox">${errorParsed}</p>`;
|
||||
$("#alert").dialog({
|
||||
resizable: false, title: "Generation error", maxWidth:500, buttons: {
|
||||
"Clear data": function() {localStorage.clear(); localStorage.setItem("version", version);},
|
||||
Regenerate: function() {regenerateMap(); $(this).dialog("close");}
|
||||
}, position: {my: "center", at: "center", of: "svg"}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// generate map seed (string!) or get it from URL searchParams
|
||||
|
|
@ -1004,13 +1060,16 @@ function drawCoastline() {
|
|||
if (start === -1) continue; // cannot start here
|
||||
const connectedVertices = connectVertices(start, type);
|
||||
used[f] = 1;
|
||||
const points = connectedVertices.map(v => vertices.p[v]);
|
||||
let points = connectedVertices.map(v => vertices.p[v]);
|
||||
const area = d3.polygonArea(points); // area with lakes/islands
|
||||
if (area > 0 && features[f].type === "lake") points = points.reverse();
|
||||
features[f].area = Math.abs(area);
|
||||
|
||||
const path = round(lineGen(points));
|
||||
const id = features[f].group + features[f].i;
|
||||
if (features[f].type === "lake") {
|
||||
landMask.append("path").attr("d", path).attr("fill", "black");
|
||||
//waterMask.append("path").attr("d", path).attr("fill", "white"); // uncomment to show over lakes
|
||||
// waterMask.append("path").attr("d", path).attr("fill", "white"); // uncomment to show over lakes
|
||||
lakes.select("#"+features[f].group).append("path").attr("d", path).attr("id", id); // draw the lake
|
||||
} else {
|
||||
landMask.append("path").attr("d", path).attr("fill", "white");
|
||||
|
|
@ -1046,9 +1105,9 @@ function drawCoastline() {
|
|||
const c0 = c[0] >= n || cells.t[c[0]] === t;
|
||||
const c1 = c[1] >= n || cells.t[c[1]] === t;
|
||||
const c2 = c[2] >= n || cells.t[c[2]] === t;
|
||||
if (v[0] !== prev && c0 !== c1) current = v[0];
|
||||
else if (v[1] !== prev && c1 !== c2) current = v[1];
|
||||
else if (v[2] !== prev && c0 !== c2) current = v[2];
|
||||
if (v[0] !== prev && c0 !== c1) current = v[0]; else
|
||||
if (v[1] !== prev && c1 !== c2) current = v[1]; else
|
||||
if (v[2] !== prev && c0 !== c2) current = v[2];
|
||||
if (current === chain[chain.length-1]) {console.error("Next vertex is not found"); break;}
|
||||
}
|
||||
chain.push(chain[0]); // push first vertex as the last one
|
||||
|
|
@ -1429,6 +1488,7 @@ function showStatistics() {
|
|||
}
|
||||
|
||||
const regenerateMap = debounce(function() {
|
||||
console.warn("Generate new random map");
|
||||
closeDialogs("#worldConfigurator");
|
||||
customization = 0;
|
||||
undraw();
|
||||
|
|
|
|||
|
|
@ -103,21 +103,22 @@
|
|||
// place secondary settlements based on geo and economical evaluation
|
||||
function placeTowns() {
|
||||
console.time('placeTowns');
|
||||
const score = new Int16Array(cells.s.map(s => s * gauss(1,3,0,20,3))); // cell score for towns placement
|
||||
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
|
||||
const score = new Int16Array(cells.s.map(s => s * gauss(1,3,0,20,3))); // a bit randomized cell score for towns placement
|
||||
const sorted = cells.i.filter(i => !cells.burg[i] && score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
|
||||
|
||||
const desiredNumber = manorsInput.value == 1000 ? rn(sorted.length / 8 / densityInput.value ** .8) : manorsInput.valueAsNumber;
|
||||
const burgsNumber = Math.min(desiredNumber, sorted.length);
|
||||
const burgsNumber = Math.min(desiredNumber, sorted.length); // towns to generate
|
||||
let burgsAdded = 0;
|
||||
|
||||
const burgsTree = burgs[0];
|
||||
let spacing = (graphWidth + graphHeight) / 150 / (burgsNumber ** .7 / 66); // min distance between towns
|
||||
|
||||
while (burgsAdded < burgsNumber) {
|
||||
while (burgsAdded < burgsNumber && spacing > 1) {
|
||||
for (let i=0; burgsAdded < burgsNumber && i < sorted.length; i++) {
|
||||
if (cells.burg[sorted[i]]) continue;
|
||||
const cell = sorted[i], x = cells.p[cell][0], y = cells.p[cell][1];
|
||||
const s = spacing * gauss(1, .3, .2, 2, 2); // randomize to make the placement not uniform
|
||||
if (cells.burg[cell] || burgsTree.find(x, y, s) !== undefined) continue; // to close to existing burg
|
||||
const s = spacing * gauss(1, .3, .2, 2, 2); // randomize to make placement not uniform
|
||||
if (burgsTree.find(x, y, s) !== undefined) continue; // to close to existing burg
|
||||
const burg = burgs.length;
|
||||
const culture = cells.culture[cell];
|
||||
const name = Names.getCulture(culture);
|
||||
|
|
@ -133,11 +134,7 @@
|
|||
console.error(`Cannot place all burgs. Requested ${desiredNumber}, placed ${burgsAdded}`);
|
||||
}
|
||||
|
||||
//const min = d3.min(score.filter(s => s)), max = d3.max(score);
|
||||
//terrs.selectAll("polygon").data(sorted).enter().append("polygon").attr("points", d => getPackPolygon(d)).attr("fill", d => color(1 - normalize(score[d], min, max)));
|
||||
//labels.selectAll("text").data(sorted).enter().append("text").attr("x", d => cells.p[d][0]).attr("y", d => cells.p[d][1]).text(d => score[d]).attr("font-size", 2);
|
||||
|
||||
burgs[0] = {name:undefined};
|
||||
burgs[0] = {name:undefined}; // do not store burgsTree anymore
|
||||
console.timeEnd('placeTowns');
|
||||
}
|
||||
}
|
||||
|
|
@ -823,7 +820,7 @@
|
|||
cells.province = new Uint16Array(cells.i.length); // cell state
|
||||
const percentage = +provincesInput.value;
|
||||
if (states.length < 2 || !percentage) return; // no provinces
|
||||
const max = gauss(400, 50, 300, 500) / percentage ** .5; // max growth in 300-30 range
|
||||
const max = percentage == 100 ? 1000 : gauss(20, 5, 5, 100) * percentage ** .5; // max growth
|
||||
|
||||
const forms = {
|
||||
Monarchy:{County:11, Earldom:3, Shire:1, Landgrave:1, Margrave:1, Barony:1},
|
||||
|
|
|
|||
|
|
@ -152,8 +152,9 @@ function GFontToDataURI(url) {
|
|||
}
|
||||
|
||||
// prepare map data for saving
|
||||
function getMapURL() {
|
||||
console.time("saveMap");
|
||||
function getMapData() {
|
||||
if (customization) return false;
|
||||
console.time("createMapDataBlob");
|
||||
|
||||
return new Promise(resolve => {
|
||||
const date = new Date();
|
||||
|
|
@ -192,33 +193,33 @@ function getMapURL() {
|
|||
pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl,
|
||||
pack.cells.pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state,
|
||||
pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces].join("\r\n");
|
||||
const dataBlob = new Blob([data], {type: "text/plain"});
|
||||
const URL = window.URL.createObjectURL(dataBlob);
|
||||
|
||||
console.timeEnd("saveMap");
|
||||
resolve(URL);
|
||||
const blob = new Blob([data], {type: "text/plain"});
|
||||
|
||||
console.timeEnd("createMapDataBlob");
|
||||
resolve(blob);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Save in .map format
|
||||
// Download .map file
|
||||
async function saveMap() {
|
||||
if (customization) {tip("Map cannot be saved when is in edit mode, please exit the mode and retry", false, "error"); return;}
|
||||
if (customization) {tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return;}
|
||||
closeDialogs();
|
||||
|
||||
const URL = await getMapURL();
|
||||
const blob = await getMapData();
|
||||
const URL = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.download = "fantasy_map_" + Date.now() + ".map";
|
||||
link.href = URL;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check`, true, "success", 7000);
|
||||
tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000);
|
||||
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||
}
|
||||
|
||||
function uploadFile(file, callback) {
|
||||
console.time("loadMap");
|
||||
closeDialogs();
|
||||
uploadFile.timeStart = performance.now();
|
||||
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = function(fileLoadedEvent) {
|
||||
const dataLoaded = fileLoadedEvent.target.result;
|
||||
|
|
@ -252,285 +253,386 @@ function uploadFile(file, callback) {
|
|||
}
|
||||
|
||||
function parseLoadedData(data) {
|
||||
closeDialogs();
|
||||
const reliefIcons = document.getElementById("defs-relief").innerHTML; // save relief icons
|
||||
const hatching = document.getElementById("hatching").cloneNode(true); // save hatching
|
||||
|
||||
void function parseParameters() {
|
||||
const params = data[0].split("|");
|
||||
if (params[3]) {seed = params[3]; optionsSeed.value = seed;}
|
||||
if (params[4]) graphWidth = +params[4];
|
||||
if (params[5]) graphHeight = +params[5];
|
||||
}()
|
||||
|
||||
void function parseOptions() {
|
||||
const options = data[1].split("|");
|
||||
if (options[0]) applyOption(distanceUnitInput, options[0]);
|
||||
if (options[1]) distanceScaleInput.value = distanceScaleOutput.value = options[1];
|
||||
if (options[2]) areaUnit.value = options[2];
|
||||
if (options[3]) applyOption(heightUnit, options[3]);
|
||||
if (options[4]) heightExponentInput.value = heightExponentOutput.value = options[4];
|
||||
if (options[5]) temperatureScale.value = options[5];
|
||||
if (options[6]) barSize.value = barSizeOutput.value = options[6];
|
||||
if (options[7] !== undefined) barLabel.value = options[7];
|
||||
if (options[8] !== undefined) barBackOpacity.value = options[8];
|
||||
if (options[9]) barBackColor.value = options[9];
|
||||
if (options[10]) barPosX.value = options[10];
|
||||
if (options[11]) barPosY.value = options[11];
|
||||
if (options[12]) populationRate.value = populationRateOutput.value = options[12];
|
||||
if (options[13]) urbanization.value = urbanizationOutput.value = options[13];
|
||||
if (options[14]) mapSizeInput.value = mapSizeOutput.value = Math.max(Math.min(options[14], 100), 1);
|
||||
if (options[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(options[15], 100), 0);
|
||||
if (options[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = options[16];
|
||||
if (options[17]) temperaturePoleInput.value = temperaturePoleOutput.value = options[17];
|
||||
if (options[18]) precInput.value = precOutput.value = options[18];
|
||||
if (options[19]) winds = JSON.parse(options[19]);
|
||||
}()
|
||||
|
||||
void function parseConfiguration() {
|
||||
if (data[2]) mapCoordinates = JSON.parse(data[2]);
|
||||
if (data[4]) notes = JSON.parse(data[4]);
|
||||
|
||||
const biomes = data[3].split("|");
|
||||
biomesData = applyDefaultBiomesSystem();
|
||||
biomesData.color = biomes[0].split(",");
|
||||
biomesData.habitability = biomes[1].split(",").map(h => +h);
|
||||
biomesData.name = biomes[2].split(",");
|
||||
|
||||
// push custom biomes if any
|
||||
for (let i=biomesData.i.length; i < biomesData.name.length; i++) {
|
||||
biomesData.i.push(biomesData.i.length);
|
||||
biomesData.iconsDensity.push(0);
|
||||
biomesData.icons.push([]);
|
||||
biomesData.cost.push(50);
|
||||
}
|
||||
}()
|
||||
|
||||
void function replaceSVG() {
|
||||
svg.remove();
|
||||
document.body.insertAdjacentHTML("afterbegin", data[5]);
|
||||
}()
|
||||
|
||||
void function redefineElements() {
|
||||
svg = d3.select("#map");
|
||||
defs = svg.select("#deftemp");
|
||||
viewbox = svg.select("#viewbox");
|
||||
scaleBar = svg.select("#scaleBar");
|
||||
legend = svg.select("#legend");
|
||||
ocean = viewbox.select("#ocean");
|
||||
oceanLayers = ocean.select("#oceanLayers");
|
||||
oceanPattern = ocean.select("#oceanPattern");
|
||||
lakes = viewbox.select("#lakes");
|
||||
landmass = viewbox.select("#landmass");
|
||||
texture = viewbox.select("#texture");
|
||||
terrs = viewbox.select("#terrs");
|
||||
biomes = viewbox.select("#biomes");
|
||||
cells = viewbox.select("#cells");
|
||||
gridOverlay = viewbox.select("#gridOverlay");
|
||||
coordinates = viewbox.select("#coordinates");
|
||||
compass = viewbox.select("#compass");
|
||||
rivers = viewbox.select("#rivers");
|
||||
terrain = viewbox.select("#terrain");
|
||||
relig = viewbox.select("#relig");
|
||||
cults = viewbox.select("#cults");
|
||||
regions = viewbox.select("#regions");
|
||||
statesBody = regions.select("#statesBody");
|
||||
statesHalo = regions.select("#statesHalo");
|
||||
provs = viewbox.select("#provs");
|
||||
zones = viewbox.select("#zones");
|
||||
borders = viewbox.select("#borders");
|
||||
stateBorders = borders.select("#stateBorders");
|
||||
provinceBorders = borders.select("#provinceBorders");
|
||||
routes = viewbox.select("#routes");
|
||||
roads = routes.select("#roads");
|
||||
trails = routes.select("#trails");
|
||||
searoutes = routes.select("#searoutes");
|
||||
temperature = viewbox.select("#temperature");
|
||||
coastline = viewbox.select("#coastline");
|
||||
prec = viewbox.select("#prec");
|
||||
population = viewbox.select("#population");
|
||||
labels = viewbox.select("#labels");
|
||||
icons = viewbox.select("#icons");
|
||||
burgIcons = icons.select("#burgIcons");
|
||||
anchors = icons.select("#anchors");
|
||||
markers = viewbox.select("#markers");
|
||||
ruler = viewbox.select("#ruler");
|
||||
fogging = viewbox.select("#fogging");
|
||||
debug = viewbox.select("#debug");
|
||||
freshwater = lakes.select("#freshwater");
|
||||
salt = lakes.select("#salt");
|
||||
burgLabels = labels.select("#burgLabels");
|
||||
}()
|
||||
|
||||
void function parseGridData() {
|
||||
grid = JSON.parse(data[6]);
|
||||
calculateVoronoi(grid, grid.points);
|
||||
grid.cells.h = Uint8Array.from(data[7].split(","));
|
||||
grid.cells.prec = Uint8Array.from(data[8].split(","));
|
||||
grid.cells.f = Uint16Array.from(data[9].split(","));
|
||||
grid.cells.t = Int8Array.from(data[10].split(","));
|
||||
grid.cells.temp = Int8Array.from(data[11].split(","));
|
||||
}()
|
||||
|
||||
void function parsePackData() {
|
||||
pack = {};
|
||||
reGraph();
|
||||
reMarkFeatures();
|
||||
pack.features = JSON.parse(data[12]);
|
||||
pack.cultures = JSON.parse(data[13]);
|
||||
pack.states = JSON.parse(data[14]);
|
||||
pack.burgs = JSON.parse(data[15]);
|
||||
pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: "No religion"}];
|
||||
pack.provinces = data[30] ? JSON.parse(data[30]) : [0];
|
||||
|
||||
const cells = pack.cells;
|
||||
cells.biome = Uint8Array.from(data[16].split(","));
|
||||
cells.burg = Uint16Array.from(data[17].split(","));
|
||||
cells.conf = Uint8Array.from(data[18].split(","));
|
||||
cells.culture = Uint16Array.from(data[19].split(","));
|
||||
cells.fl = Uint16Array.from(data[20].split(","));
|
||||
cells.pop = Uint16Array.from(data[21].split(","));
|
||||
cells.r = Uint16Array.from(data[22].split(","));
|
||||
cells.road = Uint16Array.from(data[23].split(","));
|
||||
cells.s = Uint16Array.from(data[24].split(","));
|
||||
cells.state = Uint16Array.from(data[25].split(","));
|
||||
cells.religion = data[26] ? Uint16Array.from(data[26].split(",")) : new Uint16Array(cells.i.length);
|
||||
cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(cells.i.length);
|
||||
cells.crossroad = data[28] ? Uint16Array.from(data[28].split(",")) : new Uint16Array(cells.i.length);
|
||||
}()
|
||||
|
||||
void function restoreLayersState() {
|
||||
if (texture.style("display") !== "none" && texture.select("image").size()) turnButtonOn("toggleTexture"); else turnButtonOff("toggleTexture");
|
||||
if (terrs.selectAll("*").size()) turnButtonOn("toggleHeight"); else turnButtonOff("toggleHeight");
|
||||
if (biomes.selectAll("*").size()) turnButtonOn("toggleBiomes"); else turnButtonOff("toggleBiomes");
|
||||
if (cells.selectAll("*").size()) turnButtonOn("toggleCells"); else turnButtonOff("toggleCells");
|
||||
if (gridOverlay.selectAll("*").size()) turnButtonOn("toggleGrid"); else turnButtonOff("toggleGrid");
|
||||
if (coordinates.selectAll("*").size()) turnButtonOn("toggleCoordinates"); else turnButtonOff("toggleCoordinates");
|
||||
if (compass.style("display") !== "none" && compass.select("use").size()) turnButtonOn("toggleCompass"); else turnButtonOff("toggleCompass");
|
||||
if (rivers.style("display") !== "none") turnButtonOn("toggleRivers"); else turnButtonOff("toggleRivers");
|
||||
if (terrain.style("display") !== "none" && terrain.selectAll("*").size()) turnButtonOn("toggleRelief"); else turnButtonOff("toggleRelief");
|
||||
if (relig.selectAll("*").size()) turnButtonOn("toggleReligions"); else turnButtonOff("toggleReligions");
|
||||
if (cults.selectAll("*").size()) turnButtonOn("toggleCultures"); else turnButtonOff("toggleCultures");
|
||||
if (statesBody.selectAll("*").size()) turnButtonOn("toggleStates"); else turnButtonOff("toggleStates");
|
||||
if (provs.selectAll("*").size()) turnButtonOn("toggleProvinces"); else turnButtonOff("toggleProvinces");
|
||||
if (zones.selectAll("*").size() && zones.style("display") !== "none") turnButtonOn("toggleZones"); else turnButtonOff("toggleZones");
|
||||
if (borders.style("display") !== "none") turnButtonOn("toggleBorders"); else turnButtonOff("toggleBorders");
|
||||
if (routes.style("display") !== "none" && routes.selectAll("path").size()) turnButtonOn("toggleRoutes"); else turnButtonOff("toggleRoutes");
|
||||
if (temperature.selectAll("*").size()) turnButtonOn("toggleTemp"); else turnButtonOff("toggleTemp");
|
||||
if (prec.selectAll("circle").size()) turnButtonOn("togglePrec"); else turnButtonOff("togglePrec");
|
||||
if (labels.style("display") !== "none") turnButtonOn("toggleLabels"); else turnButtonOff("toggleLabels");
|
||||
if (icons.style("display") !== "none") turnButtonOn("toggleIcons"); else turnButtonOff("toggleIcons");
|
||||
if (markers.selectAll("*").size() && markers.style("display") !== "none") turnButtonOn("toggleMarkers"); else turnButtonOff("toggleMarkers");
|
||||
if (ruler.style("display") !== "none") turnButtonOn("toggleRulers"); else turnButtonOff("toggleRulers");
|
||||
if (scaleBar.style("display") !== "none") turnButtonOn("toggleScaleBar"); else turnButtonOff("toggleScaleBar");
|
||||
|
||||
// special case for population bars
|
||||
const populationIsOn = population.selectAll("line").size();
|
||||
if (populationIsOn) drawPopulation();
|
||||
if (populationIsOn) turnButtonOn("togglePopulation"); else turnButtonOff("togglePopulation");
|
||||
|
||||
getCurrentPreset();
|
||||
}()
|
||||
|
||||
void function restoreEvents() {
|
||||
ruler.selectAll("g").call(d3.drag().on("start", dragRuler));
|
||||
ruler.selectAll("text").on("click", removeParent);
|
||||
ruler.selectAll("g.ruler circle").call(d3.drag().on("drag", dragRulerEdge));
|
||||
ruler.selectAll("g.ruler circle").call(d3.drag().on("drag", dragRulerEdge));
|
||||
ruler.selectAll("g.ruler rect").call(d3.drag().on("start", rulerCenterDrag));
|
||||
ruler.selectAll("g.opisometer circle").call(d3.drag().on("start", dragOpisometerEnd));
|
||||
ruler.selectAll("g.opisometer circle").call(d3.drag().on("start", dragOpisometerEnd));
|
||||
|
||||
scaleBar.on("mousemove", () => tip("Click to open Units Editor"));
|
||||
legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend());
|
||||
}()
|
||||
|
||||
void function resolveVersionConflicts() {
|
||||
const version = parseFloat(data[0].split("|")[0]);
|
||||
if (version == 0.8) {
|
||||
// 0.9 has additional relief icons to be included into older maps
|
||||
document.getElementById("defs-relief").innerHTML = reliefIcons;
|
||||
}
|
||||
|
||||
if (version < 1) {
|
||||
// 1.0 adds a new religions layer
|
||||
relig = viewbox.insert("g", "#terrain").attr("id", "relig");
|
||||
Religions.generate();
|
||||
|
||||
// 1.0 adds a legend box
|
||||
legend = svg.append("g").attr("id", "legend");
|
||||
legend.attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC")
|
||||
.attr("font-size", 13).attr("data-size", 13).attr("data-x", 99).attr("data-y", 93)
|
||||
.attr("stroke-width", 2.5).attr("stroke", "#812929").attr("stroke-dasharray", "0 4 10 4").attr("stroke-linecap", "round");
|
||||
|
||||
// 1.0 separated drawBorders fron drawStates()
|
||||
stateBorders = borders.append("g").attr("id", "stateBorders");
|
||||
provinceBorders = borders.append("g").attr("id", "provinceBorders");
|
||||
borders.attr("opacity", null).attr("stroke", null).attr("stroke-width", null).attr("stroke-dasharray", null).attr("stroke-linecap", null).attr("filter", null);
|
||||
stateBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
|
||||
provinceBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
|
||||
|
||||
// 1.0 adds state relations, provinces, forms and full names
|
||||
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", .6);
|
||||
BurgsAndStates.collectStatistics();
|
||||
BurgsAndStates.generateDiplomacy();
|
||||
BurgsAndStates.defineStateForms();
|
||||
drawStates();
|
||||
BurgsAndStates.generateProvinces();
|
||||
drawBorders();
|
||||
if (!layerIsOn("toggleBorders")) $('#borders').fadeOut();
|
||||
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
|
||||
|
||||
// 1.0 adds hatching
|
||||
document.getElementsByTagName("defs")[0].appendChild(hatching);
|
||||
|
||||
// 1.0 adds zones layer
|
||||
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
|
||||
zones.attr("opacity", .6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt");
|
||||
addZone();
|
||||
if (!markers.selectAll("*").size()) {addMarkers(); turnButtonOn("toggleMarkers");}
|
||||
|
||||
// 1.0 add fogging layer (state focus)
|
||||
let fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)")
|
||||
.append("g").attr("id", "fogging").attr("display", "none");
|
||||
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
||||
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%")
|
||||
.attr("height", "100%").attr("fill", "white");
|
||||
|
||||
// 1.0 changes states opacity bask to regions level
|
||||
if (statesBody.attr("opacity")) {
|
||||
regions.attr("opacity", statesBody.attr("opacity"));
|
||||
statesBody.attr("opacity", null);
|
||||
try {
|
||||
const reliefIcons = document.getElementById("defs-relief").innerHTML; // save relief icons
|
||||
const hatching = document.getElementById("hatching").cloneNode(true); // save hatching
|
||||
|
||||
void function parseParameters() {
|
||||
const params = data[0].split("|");
|
||||
if (params[3]) {seed = params[3]; optionsSeed.value = seed;}
|
||||
if (params[4]) graphWidth = +params[4];
|
||||
if (params[5]) graphHeight = +params[5];
|
||||
}()
|
||||
|
||||
console.group("Loaded Map " + seed);
|
||||
|
||||
void function parseOptions() {
|
||||
const options = data[1].split("|");
|
||||
if (options[0]) applyOption(distanceUnitInput, options[0]);
|
||||
if (options[1]) distanceScaleInput.value = distanceScaleOutput.value = options[1];
|
||||
if (options[2]) areaUnit.value = options[2];
|
||||
if (options[3]) applyOption(heightUnit, options[3]);
|
||||
if (options[4]) heightExponentInput.value = heightExponentOutput.value = options[4];
|
||||
if (options[5]) temperatureScale.value = options[5];
|
||||
if (options[6]) barSize.value = barSizeOutput.value = options[6];
|
||||
if (options[7] !== undefined) barLabel.value = options[7];
|
||||
if (options[8] !== undefined) barBackOpacity.value = options[8];
|
||||
if (options[9]) barBackColor.value = options[9];
|
||||
if (options[10]) barPosX.value = options[10];
|
||||
if (options[11]) barPosY.value = options[11];
|
||||
if (options[12]) populationRate.value = populationRateOutput.value = options[12];
|
||||
if (options[13]) urbanization.value = urbanizationOutput.value = options[13];
|
||||
if (options[14]) mapSizeInput.value = mapSizeOutput.value = Math.max(Math.min(options[14], 100), 1);
|
||||
if (options[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(options[15], 100), 0);
|
||||
if (options[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = options[16];
|
||||
if (options[17]) temperaturePoleInput.value = temperaturePoleOutput.value = options[17];
|
||||
if (options[18]) precInput.value = precOutput.value = options[18];
|
||||
if (options[19]) winds = JSON.parse(options[19]);
|
||||
}()
|
||||
|
||||
void function parseConfiguration() {
|
||||
if (data[2]) mapCoordinates = JSON.parse(data[2]);
|
||||
if (data[4]) notes = JSON.parse(data[4]);
|
||||
|
||||
const biomes = data[3].split("|");
|
||||
biomesData = applyDefaultBiomesSystem();
|
||||
biomesData.color = biomes[0].split(",");
|
||||
biomesData.habitability = biomes[1].split(",").map(h => +h);
|
||||
biomesData.name = biomes[2].split(",");
|
||||
|
||||
// push custom biomes if any
|
||||
for (let i=biomesData.i.length; i < biomesData.name.length; i++) {
|
||||
biomesData.i.push(biomesData.i.length);
|
||||
biomesData.iconsDensity.push(0);
|
||||
biomesData.icons.push([]);
|
||||
biomesData.cost.push(50);
|
||||
}
|
||||
|
||||
// 1.0 changed labels to multi-lined
|
||||
labels.selectAll("textPath").each(function() {
|
||||
const text = this.textContent;
|
||||
const shift = this.getComputedTextLength() / -1.5;
|
||||
this.innerHTML = `<tspan x="${shift}">${text}</tspan>`;
|
||||
});
|
||||
|
||||
// 1.0 added new biome - Wetland
|
||||
biomesData.name.push("Wetland");
|
||||
biomesData.color.push("#0b9131");
|
||||
biomesData.habitability.push(12);
|
||||
}
|
||||
|
||||
if (version == 1) {
|
||||
// v 1.0 initial code had a bug with religion layer id
|
||||
if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
|
||||
|
||||
// v 1.0 initially has Sympathy status then relaced with Friendly
|
||||
for (const s of pack.states) {
|
||||
s.diplomacy = s.diplomacy.map(r => r === "Sympathy" ? "Friendly" : r);
|
||||
}()
|
||||
|
||||
void function replaceSVG() {
|
||||
svg.remove();
|
||||
document.body.insertAdjacentHTML("afterbegin", data[5]);
|
||||
}()
|
||||
|
||||
void function redefineElements() {
|
||||
svg = d3.select("#map");
|
||||
defs = svg.select("#deftemp");
|
||||
viewbox = svg.select("#viewbox");
|
||||
scaleBar = svg.select("#scaleBar");
|
||||
legend = svg.select("#legend");
|
||||
ocean = viewbox.select("#ocean");
|
||||
oceanLayers = ocean.select("#oceanLayers");
|
||||
oceanPattern = ocean.select("#oceanPattern");
|
||||
lakes = viewbox.select("#lakes");
|
||||
landmass = viewbox.select("#landmass");
|
||||
texture = viewbox.select("#texture");
|
||||
terrs = viewbox.select("#terrs");
|
||||
biomes = viewbox.select("#biomes");
|
||||
cells = viewbox.select("#cells");
|
||||
gridOverlay = viewbox.select("#gridOverlay");
|
||||
coordinates = viewbox.select("#coordinates");
|
||||
compass = viewbox.select("#compass");
|
||||
rivers = viewbox.select("#rivers");
|
||||
terrain = viewbox.select("#terrain");
|
||||
relig = viewbox.select("#relig");
|
||||
cults = viewbox.select("#cults");
|
||||
regions = viewbox.select("#regions");
|
||||
statesBody = regions.select("#statesBody");
|
||||
statesHalo = regions.select("#statesHalo");
|
||||
provs = viewbox.select("#provs");
|
||||
zones = viewbox.select("#zones");
|
||||
borders = viewbox.select("#borders");
|
||||
stateBorders = borders.select("#stateBorders");
|
||||
provinceBorders = borders.select("#provinceBorders");
|
||||
routes = viewbox.select("#routes");
|
||||
roads = routes.select("#roads");
|
||||
trails = routes.select("#trails");
|
||||
searoutes = routes.select("#searoutes");
|
||||
temperature = viewbox.select("#temperature");
|
||||
coastline = viewbox.select("#coastline");
|
||||
prec = viewbox.select("#prec");
|
||||
population = viewbox.select("#population");
|
||||
labels = viewbox.select("#labels");
|
||||
icons = viewbox.select("#icons");
|
||||
burgIcons = icons.select("#burgIcons");
|
||||
anchors = icons.select("#anchors");
|
||||
markers = viewbox.select("#markers");
|
||||
ruler = viewbox.select("#ruler");
|
||||
fogging = viewbox.select("#fogging");
|
||||
debug = viewbox.select("#debug");
|
||||
freshwater = lakes.select("#freshwater");
|
||||
salt = lakes.select("#salt");
|
||||
burgLabels = labels.select("#burgLabels");
|
||||
}()
|
||||
|
||||
void function parseGridData() {
|
||||
grid = JSON.parse(data[6]);
|
||||
calculateVoronoi(grid, grid.points);
|
||||
grid.cells.h = Uint8Array.from(data[7].split(","));
|
||||
grid.cells.prec = Uint8Array.from(data[8].split(","));
|
||||
grid.cells.f = Uint16Array.from(data[9].split(","));
|
||||
grid.cells.t = Int8Array.from(data[10].split(","));
|
||||
grid.cells.temp = Int8Array.from(data[11].split(","));
|
||||
}()
|
||||
|
||||
void function parsePackData() {
|
||||
pack = {};
|
||||
reGraph();
|
||||
reMarkFeatures();
|
||||
pack.features = JSON.parse(data[12]);
|
||||
pack.cultures = JSON.parse(data[13]);
|
||||
pack.states = JSON.parse(data[14]);
|
||||
pack.burgs = JSON.parse(data[15]);
|
||||
pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: "No religion"}];
|
||||
pack.provinces = data[30] ? JSON.parse(data[30]) : [0];
|
||||
|
||||
const cells = pack.cells;
|
||||
cells.biome = Uint8Array.from(data[16].split(","));
|
||||
cells.burg = Uint16Array.from(data[17].split(","));
|
||||
cells.conf = Uint8Array.from(data[18].split(","));
|
||||
cells.culture = Uint16Array.from(data[19].split(","));
|
||||
cells.fl = Uint16Array.from(data[20].split(","));
|
||||
cells.pop = Uint16Array.from(data[21].split(","));
|
||||
cells.r = Uint16Array.from(data[22].split(","));
|
||||
cells.road = Uint16Array.from(data[23].split(","));
|
||||
cells.s = Uint16Array.from(data[24].split(","));
|
||||
cells.state = Uint16Array.from(data[25].split(","));
|
||||
cells.religion = data[26] ? Uint16Array.from(data[26].split(",")) : new Uint16Array(cells.i.length);
|
||||
cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(cells.i.length);
|
||||
cells.crossroad = data[28] ? Uint16Array.from(data[28].split(",")) : new Uint16Array(cells.i.length);
|
||||
}()
|
||||
|
||||
void function restoreLayersState() {
|
||||
if (texture.style("display") !== "none" && texture.select("image").size()) turnButtonOn("toggleTexture"); else turnButtonOff("toggleTexture");
|
||||
if (terrs.selectAll("*").size()) turnButtonOn("toggleHeight"); else turnButtonOff("toggleHeight");
|
||||
if (biomes.selectAll("*").size()) turnButtonOn("toggleBiomes"); else turnButtonOff("toggleBiomes");
|
||||
if (cells.selectAll("*").size()) turnButtonOn("toggleCells"); else turnButtonOff("toggleCells");
|
||||
if (gridOverlay.selectAll("*").size()) turnButtonOn("toggleGrid"); else turnButtonOff("toggleGrid");
|
||||
if (coordinates.selectAll("*").size()) turnButtonOn("toggleCoordinates"); else turnButtonOff("toggleCoordinates");
|
||||
if (compass.style("display") !== "none" && compass.select("use").size()) turnButtonOn("toggleCompass"); else turnButtonOff("toggleCompass");
|
||||
if (rivers.style("display") !== "none") turnButtonOn("toggleRivers"); else turnButtonOff("toggleRivers");
|
||||
if (terrain.style("display") !== "none" && terrain.selectAll("*").size()) turnButtonOn("toggleRelief"); else turnButtonOff("toggleRelief");
|
||||
if (relig.selectAll("*").size()) turnButtonOn("toggleReligions"); else turnButtonOff("toggleReligions");
|
||||
if (cults.selectAll("*").size()) turnButtonOn("toggleCultures"); else turnButtonOff("toggleCultures");
|
||||
if (statesBody.selectAll("*").size()) turnButtonOn("toggleStates"); else turnButtonOff("toggleStates");
|
||||
if (provs.selectAll("*").size()) turnButtonOn("toggleProvinces"); else turnButtonOff("toggleProvinces");
|
||||
if (zones.selectAll("*").size() && zones.style("display") !== "none") turnButtonOn("toggleZones"); else turnButtonOff("toggleZones");
|
||||
if (borders.style("display") !== "none") turnButtonOn("toggleBorders"); else turnButtonOff("toggleBorders");
|
||||
if (routes.style("display") !== "none" && routes.selectAll("path").size()) turnButtonOn("toggleRoutes"); else turnButtonOff("toggleRoutes");
|
||||
if (temperature.selectAll("*").size()) turnButtonOn("toggleTemp"); else turnButtonOff("toggleTemp");
|
||||
if (prec.selectAll("circle").size()) turnButtonOn("togglePrec"); else turnButtonOff("togglePrec");
|
||||
if (labels.style("display") !== "none") turnButtonOn("toggleLabels"); else turnButtonOff("toggleLabels");
|
||||
if (icons.style("display") !== "none") turnButtonOn("toggleIcons"); else turnButtonOff("toggleIcons");
|
||||
if (markers.selectAll("*").size() && markers.style("display") !== "none") turnButtonOn("toggleMarkers"); else turnButtonOff("toggleMarkers");
|
||||
if (ruler.style("display") !== "none") turnButtonOn("toggleRulers"); else turnButtonOff("toggleRulers");
|
||||
if (scaleBar.style("display") !== "none") turnButtonOn("toggleScaleBar"); else turnButtonOff("toggleScaleBar");
|
||||
|
||||
// special case for population bars
|
||||
const populationIsOn = population.selectAll("line").size();
|
||||
if (populationIsOn) drawPopulation();
|
||||
if (populationIsOn) turnButtonOn("togglePopulation"); else turnButtonOff("togglePopulation");
|
||||
|
||||
getCurrentPreset();
|
||||
}()
|
||||
|
||||
void function restoreEvents() {
|
||||
ruler.selectAll("g").call(d3.drag().on("start", dragRuler));
|
||||
ruler.selectAll("text").on("click", removeParent);
|
||||
ruler.selectAll("g.ruler circle").call(d3.drag().on("drag", dragRulerEdge));
|
||||
ruler.selectAll("g.ruler circle").call(d3.drag().on("drag", dragRulerEdge));
|
||||
ruler.selectAll("g.ruler rect").call(d3.drag().on("start", rulerCenterDrag));
|
||||
ruler.selectAll("g.opisometer circle").call(d3.drag().on("start", dragOpisometerEnd));
|
||||
ruler.selectAll("g.opisometer circle").call(d3.drag().on("start", dragOpisometerEnd));
|
||||
|
||||
scaleBar.on("mousemove", () => tip("Click to open Units Editor"));
|
||||
legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend());
|
||||
}()
|
||||
|
||||
void function resolveVersionConflicts() {
|
||||
const version = parseFloat(data[0].split("|")[0]);
|
||||
if (version == 0.8) {
|
||||
// 0.9 has additional relief icons to be included into older maps
|
||||
document.getElementById("defs-relief").innerHTML = reliefIcons;
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if (version < 1) {
|
||||
// 1.0 adds a new religions layer
|
||||
relig = viewbox.insert("g", "#terrain").attr("id", "relig");
|
||||
Religions.generate();
|
||||
|
||||
// 1.0 adds a legend box
|
||||
legend = svg.append("g").attr("id", "legend");
|
||||
legend.attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC")
|
||||
.attr("font-size", 13).attr("data-size", 13).attr("data-x", 99).attr("data-y", 93)
|
||||
.attr("stroke-width", 2.5).attr("stroke", "#812929").attr("stroke-dasharray", "0 4 10 4").attr("stroke-linecap", "round");
|
||||
|
||||
// 1.0 separated drawBorders fron drawStates()
|
||||
stateBorders = borders.append("g").attr("id", "stateBorders");
|
||||
provinceBorders = borders.append("g").attr("id", "provinceBorders");
|
||||
borders.attr("opacity", null).attr("stroke", null).attr("stroke-width", null).attr("stroke-dasharray", null).attr("stroke-linecap", null).attr("filter", null);
|
||||
stateBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
|
||||
provinceBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
|
||||
|
||||
// 1.0 adds state relations, provinces, forms and full names
|
||||
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", .6);
|
||||
BurgsAndStates.collectStatistics();
|
||||
BurgsAndStates.generateDiplomacy();
|
||||
BurgsAndStates.defineStateForms();
|
||||
drawStates();
|
||||
BurgsAndStates.generateProvinces();
|
||||
drawBorders();
|
||||
if (!layerIsOn("toggleBorders")) $('#borders').fadeOut();
|
||||
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
|
||||
|
||||
// 1.0 adds hatching
|
||||
document.getElementsByTagName("defs")[0].appendChild(hatching);
|
||||
|
||||
// 1.0 adds zones layer
|
||||
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
|
||||
zones.attr("opacity", .6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt");
|
||||
addZone();
|
||||
if (!markers.selectAll("*").size()) {addMarkers(); turnButtonOn("toggleMarkers");}
|
||||
|
||||
// 1.0 add fogging layer (state focus)
|
||||
let fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)")
|
||||
.append("g").attr("id", "fogging").attr("display", "none");
|
||||
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
||||
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%")
|
||||
.attr("height", "100%").attr("fill", "white");
|
||||
|
||||
// 1.0 changes states opacity bask to regions level
|
||||
if (statesBody.attr("opacity")) {
|
||||
regions.attr("opacity", statesBody.attr("opacity"));
|
||||
statesBody.attr("opacity", null);
|
||||
}
|
||||
|
||||
// 1.0 changed labels to multi-lined
|
||||
labels.selectAll("textPath").each(function() {
|
||||
const text = this.textContent;
|
||||
const shift = this.getComputedTextLength() / -1.5;
|
||||
this.innerHTML = `<tspan x="${shift}">${text}</tspan>`;
|
||||
});
|
||||
|
||||
// 1.0 added new biome - Wetland
|
||||
biomesData.name.push("Wetland");
|
||||
biomesData.color.push("#0b9131");
|
||||
biomesData.habitability.push(12);
|
||||
}
|
||||
|
||||
if (version == 1) {
|
||||
// v 1.0 initial code had a bug with religion layer id
|
||||
if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
|
||||
|
||||
// v 1.0 initially has Sympathy status then relaced with Friendly
|
||||
for (const s of pack.states) {
|
||||
s.diplomacy = s.diplomacy.map(r => r === "Sympathy" ? "Friendly" : r);
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
changeMapSize();
|
||||
if (window.restoreDefaultEvents) restoreDefaultEvents();
|
||||
invokeActiveZooming();
|
||||
|
||||
console.warn(`TOTAL: ${rn((performance.now()-uploadFile.timeStart)/1000,2)}s`);
|
||||
showStatistics();
|
||||
console.groupEnd("Loaded Map " + seed);
|
||||
tip("Map is successfully loaded", true, "success", 7000);
|
||||
}
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
clearMainTip();
|
||||
|
||||
const regex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
|
||||
const errorNoURL = error.stack.replace(regex, url => '<i>' + last(url.split("/")) + '</i>');
|
||||
const errorParsed = errorNoURL.replace(/ at /ig, "<br> at ");
|
||||
|
||||
alertMessage.innerHTML = `An error is occured on map loading. Select a different file to load,
|
||||
<br>generate a new random map or cancel the loading
|
||||
<p id="errorBox">${errorParsed}</p>`;
|
||||
$("#alert").dialog({
|
||||
resizable: false, title: "Loading error", maxWidth:500, buttons: {
|
||||
"Select file": function() {$(this).dialog("close"); mapToLoad.click();},
|
||||
"New map": function() {$(this).dialog("close"); regenerateMap();},
|
||||
Cancel: function() {$(this).dialog("close")}
|
||||
}, position: {my: "center", at: "center", of: "svg"}
|
||||
});
|
||||
}
|
||||
|
||||
changeMapSize();
|
||||
restoreDefaultEvents();
|
||||
invokeActiveZooming();
|
||||
tip("Map is successfully loaded", true, "success", 7000);
|
||||
console.timeEnd("loadMap");
|
||||
showStatistics();
|
||||
}
|
||||
|
||||
async function quickSave() {
|
||||
const blob = await getMapData();
|
||||
if (blob) ldb.set("lastMap", blob); // auto-save map
|
||||
tip("Map is saved to browser memory", true, "success", 2000);
|
||||
}
|
||||
|
||||
function quickLoad() {
|
||||
ldb.get("lastMap", blob => {
|
||||
if (blob) {
|
||||
loadMapPrompt(blob);
|
||||
} else {
|
||||
tip("No map stored. Save map to storage first", true, "error", 2000);
|
||||
console.error("No map stored");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadMapPrompt(blob) {
|
||||
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
|
||||
if (workingTime < 10) {loadLastSavedMap(); return;}
|
||||
|
||||
alertMessage.innerHTML = `Are you sure you want to load saved map?<br>
|
||||
All unsaved changes made to the current map will be lost`;
|
||||
$("#alert").dialog({resizable: false, title: "Load saved map",
|
||||
buttons: {
|
||||
Cancel: function() {$(this).dialog("close");},
|
||||
Load: function() {loadLastSavedMap(); $(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
|
||||
function loadLastSavedMap() {
|
||||
console.warn("Load last saved map");
|
||||
closeDialogs();
|
||||
try {
|
||||
uploadFile(blob);
|
||||
}
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
tip("Cannot load last saved map", true, "error", 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saveReminder = function() {
|
||||
if (localStorage.getItem("noReminder")) return;
|
||||
const message = ["Please don't forget to save your work as a .map file",
|
||||
"Please remember to save work as a .map file",
|
||||
"Saving in .map format will ensure your data won't be lost in case of issues",
|
||||
"Safety is number one priority. Please save the map",
|
||||
"Don't forget to save your map on a regular basis!",
|
||||
"Just a gentle reminder for you to save the map",
|
||||
"Please forget to save your progress (saving as .map is the best option)",
|
||||
"Don't want to be reminded about need to save? Press CTRL+Q"];
|
||||
|
||||
saveReminder.reminder = setInterval(() => {
|
||||
if (customization) return;
|
||||
tip(ra(message), true, "warn", 2500);
|
||||
}, 1e6);
|
||||
saveReminder.status = 1;
|
||||
}
|
||||
|
||||
saveReminder();
|
||||
|
||||
function toggleSaveReminder() {
|
||||
if (saveReminder.status) {
|
||||
tip("Save reminder is turned off. Press CTRL+Q again to re-initiate", true, "warn", 2000);
|
||||
clearInterval(saveReminder.reminder);
|
||||
localStorage.setItem("noReminder", true);
|
||||
saveReminder.status = 0;
|
||||
} else {
|
||||
tip("Save reminder is turned on. Press CTRL+Q to turn off", true, "warn", 2000);
|
||||
localStorage.removeItem("noReminder");
|
||||
saveReminder();
|
||||
}
|
||||
}
|
||||
|
|
@ -148,7 +148,7 @@ function editBurgs() {
|
|||
function changeBurgPopulation() {
|
||||
const burg = +this.parentNode.dataset.id;
|
||||
if (this.value == "" || isNaN(+this.value)) {
|
||||
tip("Please provide an integer number", false, "error");
|
||||
tip("Please provide an integer number (like 10000, not 10K)", false, "error");
|
||||
this.value = si(pack.burgs[burg].population * populationRate.value * urbanization.value);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,22 +187,43 @@ function editDiplomacy() {
|
|||
const chronicle = pack.states[0].diplomacy;
|
||||
if (!chronicle.length) {tip("Relations history is blank", false, "error"); return;}
|
||||
|
||||
let message = `<div>`;
|
||||
chronicle.forEach(e => {
|
||||
message += `<div style="margin: 0.5em 0">`;
|
||||
e.forEach((l, i) => message += `<div${i ? "" : " style='font-weight:bold'"}>${l}</div>`);
|
||||
message += `</div>`;
|
||||
let message = `<div autocorrect="off" spellcheck="false">`;
|
||||
chronicle.forEach((e, d) => {
|
||||
message += `<div>`;
|
||||
e.forEach((l, i) => message += `<div contenteditable="true" data-id="${d}-${i}"${i ? "" : " style='font-weight:bold'"}>${l}</div>`);
|
||||
message += `‍</div>`;
|
||||
});
|
||||
alertMessage.innerHTML = message + `</div>`;
|
||||
alertMessage.innerHTML = message + `</div><i id="info-line">Type to edit. Press Enter to add a new line, empty the element to remove it</i>`;
|
||||
alertMessage.querySelectorAll("div[contenteditable='true']").forEach(el => el.addEventListener("input", changeReliationsHistory));
|
||||
|
||||
$("#alert").dialog({title: "Relations history", position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Save: function() {
|
||||
const text = this.querySelector("div").innerText.split("\n").join("\r\n");
|
||||
const dataBlob = new Blob([text], {type: "text/plain"});
|
||||
const url = window.URL.createObjectURL(dataBlob);
|
||||
const link = document.createElement("a");
|
||||
document.body.appendChild(link);
|
||||
link.download = "state_relations_history" + Date.now() + ".txt";
|
||||
link.href = url;
|
||||
link.click();
|
||||
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
|
||||
},
|
||||
Clear: function() {pack.states[0].diplomacy = []; $(this).dialog("close");},
|
||||
Close: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function changeReliationsHistory() {
|
||||
const i = this.dataset.id.split("-");
|
||||
const group = pack.states[0].diplomacy[i[0]];
|
||||
if (this.innerHTML === "") {
|
||||
group.splice(i[1], 1);
|
||||
this.remove();
|
||||
} else group[i[1]] = this.innerHTML;
|
||||
}
|
||||
|
||||
function showRelationsMatrix() {
|
||||
const states = pack.states.filter(s => s.i && !s.removed);
|
||||
const valid = states.map(s => s.i);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
// Module to store general UI functions
|
||||
"use strict";
|
||||
|
||||
// ask before closing the window
|
||||
window.onbeforeunload = () => "Are you sure you want to navigate away?";
|
||||
|
||||
// fit full-screen map if window is resized
|
||||
$(window).resize(function(e) {
|
||||
mapWidthInput.value = window.innerWidth;
|
||||
|
|
@ -11,6 +8,8 @@ $(window).resize(function(e) {
|
|||
changeMapSize();
|
||||
});
|
||||
|
||||
window.onbeforeunload = () => "Are you sure you want to navigate away?";
|
||||
|
||||
// Tooltips
|
||||
const tooltip = document.getElementById("tooltip");
|
||||
|
||||
|
|
@ -230,16 +229,22 @@ function applyOption(select, option) {
|
|||
// Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys
|
||||
document.addEventListener("keydown", function(event) {
|
||||
const active = document.activeElement.tagName;
|
||||
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
|
||||
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
|
||||
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
|
||||
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey;
|
||||
if (key === 118) regeneratePrompt(); // "F7" for new map
|
||||
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
||||
if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
||||
else if (key === 9) {toggleOptions(event); event.preventDefault();} // Tab to toggle options
|
||||
|
||||
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
||||
else if (key === 117) quickSave(); // "F6" for quick save
|
||||
else if (key === 120) quickLoad(); // "F9" for quick load
|
||||
|
||||
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 === 77) saveMap(); // Ctrl + "M" to save MAP file
|
||||
else if (ctrl && key === 85) mapToLoad.click(); // Ctrl + "U" to load MAP from URL
|
||||
else if (ctrl && key === 76) mapToLoad.click(); // Ctrl + "L" to load MAP from local file
|
||||
else if (ctrl && key === 81) toggleSaveReminder(); // Ctrl + "Q" to toggle save reminder
|
||||
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
|
||||
|
||||
else if (shift && key === 192) console.log(pack.cells); // Shift + "`" to log cells data
|
||||
|
|
|
|||
|
|
@ -22,22 +22,25 @@ function restoreLayers() {
|
|||
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
|
||||
}
|
||||
|
||||
// layers to be turned on; changable by user
|
||||
let presets = {
|
||||
"political": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar", "toggleStates"],
|
||||
"cultural": ["toggleBorders", "toggleCultures", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
||||
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
|
||||
"heightmap": ["toggleHeight", "toggleRivers", "toggleScaleBar"],
|
||||
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"landmass": ["toggleScaleBar"]
|
||||
}
|
||||
|
||||
restoreLayers(); // run on-load
|
||||
let presets = {}; // global object
|
||||
restoreCustomPresets(); // run on-load
|
||||
|
||||
function getDefaultPresets() {
|
||||
return {
|
||||
"political": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar", "toggleStates"],
|
||||
"cultural": ["toggleBorders", "toggleCultures", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
||||
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
|
||||
"heightmap": ["toggleHeight", "toggleRivers", "toggleScaleBar"],
|
||||
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"landmass": ["toggleScaleBar"]
|
||||
}
|
||||
}
|
||||
|
||||
function restoreCustomPresets() {
|
||||
presets = getDefaultPresets();
|
||||
const storedPresets = JSON.parse(localStorage.getItem("presets"));
|
||||
if (!storedPresets) return;
|
||||
|
||||
|
|
@ -45,6 +48,7 @@ function restoreCustomPresets() {
|
|||
if (presets[preset]) continue;
|
||||
layersPreset.add(new Option(preset, preset));
|
||||
}
|
||||
|
||||
presets = storedPresets;
|
||||
}
|
||||
|
||||
|
|
@ -62,34 +66,50 @@ function changePreset(preset) {
|
|||
});
|
||||
layersPreset.value = preset;
|
||||
localStorage.setItem("preset", preset);
|
||||
|
||||
const isDefault = getDefaultPresets()[preset];
|
||||
removePresetButton.style.display = isDefault ? "none" : "inline-block";
|
||||
savePresetButton.style.display = "none";
|
||||
}
|
||||
|
||||
function savePreset() {
|
||||
// don't allow if layers should already esist as a preset
|
||||
if (layersPreset.value !== "custom") {
|
||||
tip(`Current layers are already saved as a "${layersPreset.selectedOptions[0].label}" preset`, false, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// add new preset
|
||||
const preset = prompt("Please provide a preset name"); // preset name
|
||||
if (!preset) return;
|
||||
presets[preset] = Array.from(document.getElementById("mapLayers").querySelectorAll("li:not(.buttonoff)")).map(node => node.id).sort();
|
||||
layersPreset.add(new Option(preset, preset, false, true));
|
||||
localStorage.setItem("presets", JSON.stringify(presets));
|
||||
localStorage.setItem("preset", preset);
|
||||
removePresetButton.style.display = "inline-block";
|
||||
savePresetButton.style.display = "none";
|
||||
}
|
||||
|
||||
function removePreset() {
|
||||
const preset = layersPreset.value;
|
||||
delete presets[preset];
|
||||
const index = Array.from(layersPreset.options).findIndex(o => o.value === preset);
|
||||
layersPreset.options.remove(index);
|
||||
layersPreset.value = "custom";
|
||||
removePresetButton.style.display = "none";
|
||||
savePresetButton.style.display = "inline-block";
|
||||
|
||||
localStorage.setItem("presets", JSON.stringify(presets));
|
||||
localStorage.removeItem("preset");
|
||||
}
|
||||
|
||||
function getCurrentPreset() {
|
||||
const layers = Array.from(document.getElementById("mapLayers").querySelectorAll("li:not(.buttonoff)")).map(node => node.id).sort();
|
||||
const defaultPresets = getDefaultPresets();
|
||||
|
||||
for (const preset in presets) {
|
||||
if (JSON.stringify(presets[preset]) !== JSON.stringify(layers)) continue;
|
||||
layersPreset.value = preset;
|
||||
removePresetButton.style.display = defaultPresets[preset] ? "none" : "inline-block";
|
||||
return;
|
||||
}
|
||||
|
||||
layersPreset.value = "custom";
|
||||
removePresetButton.style.display = "none";
|
||||
savePresetButton.style.display = "inline-block";
|
||||
}
|
||||
|
||||
function toggleHeight() {
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@
|
|||
$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"});
|
||||
$("#mapLayers").disableSelection();
|
||||
|
||||
// show control elements and remove loading screen on map load
|
||||
d3.select("#loading").transition().duration(5000).style("opacity", 0).remove();
|
||||
d3.select("#initial").transition().duration(5000).attr("opacity", 0).remove();
|
||||
d3.select("#optionsContainer").transition().duration(5000).style("opacity", 1);
|
||||
d3.select("#tooltip").transition().duration(5000).style("opacity", 1);
|
||||
|
||||
// remove glow if tip is aknowledged
|
||||
if (localStorage.getItem("disable_click_arrow_tooltip")) {
|
||||
clearMainTip();
|
||||
|
|
@ -723,7 +717,7 @@ function changeMapSize() {
|
|||
oceanPattern.select("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
||||
oceanLayers.select("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
|
||||
fitScaleBar();
|
||||
fitLegendBox();
|
||||
if (window.fitLegendBox) fitLegendBox();
|
||||
}
|
||||
|
||||
// just apply map size that was already set, apply graph size!
|
||||
|
|
@ -994,28 +988,29 @@ document.getElementById("sticked").addEventListener("click", function(event) {
|
|||
else if (id === "saveButton") toggleSavePane();
|
||||
else if (id === "loadButton") toggleLoadPane();
|
||||
else if (id === "zoomReset") resetZoom(1000);
|
||||
else if (id === "quickSave") quickSave();
|
||||
else if (id === "saveMap") saveMap();
|
||||
else if (id === "saveSVG") saveAsImage("svg");
|
||||
else if (id === "savePNG") saveAsImage("png");
|
||||
else if (id === "saveDropbox") saveDropbox();
|
||||
if (id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveDropbox") toggleSavePane();
|
||||
if (id === "loadMap") mapToLoad.click()
|
||||
if (id === "loadURL") loadURL();
|
||||
if (id === "loadDropbox") loadDropbox();
|
||||
if (id === "loadURL" || id === "loadMap" || id === "loadDropbox") toggleLoadPane();
|
||||
if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveDropbox") toggleSavePane();
|
||||
if (id === "loadMap") mapToLoad.click();
|
||||
else if (id === "quickLoad") quickLoad();
|
||||
else if (id === "loadURL") loadURL();
|
||||
else if (id === "loadDropbox") loadDropbox();
|
||||
if (id === "quickLoad" || id === "loadURL" || id === "loadMap" || id === "loadDropbox") toggleLoadPane();
|
||||
});
|
||||
|
||||
function regeneratePrompt() {
|
||||
if (customization) {tip("Please exit the customization mode first", false, "warning"); return;}
|
||||
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
|
||||
if (workingTime < 15) {regenerateMap(); return;}
|
||||
if (workingTime < 10) {regenerateMap(); return;}
|
||||
|
||||
alertMessage.innerHTML = `Are you sure you want to generate a new map?<br>
|
||||
All unsaved changes made to the current map will be lost`;
|
||||
$("#alert").dialog({resizable: false, title: "Generate new map",
|
||||
buttons: {
|
||||
Cancel: function() {$(this).dialog("close");},
|
||||
Generate: regenerateMap
|
||||
Generate: function() {closeDialogs(); regenerateMap();}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1052,7 +1047,8 @@ function toggleSavePane() {
|
|||
// };
|
||||
|
||||
// // working file: "https://dl.dropbox.com/s/llg93mwyonyzdmu/test.map?dl=1";
|
||||
// const URL = await getMapURL();
|
||||
// const dataBlob = await getMapData();
|
||||
// const URL = window.URL.createObjectURL(dataBlob);
|
||||
// Dropbox.save(URL, filename, options);
|
||||
// }
|
||||
|
||||
|
|
@ -1072,6 +1068,7 @@ function loadURL() {
|
|||
Load: function() {
|
||||
const value = mapURL.value;
|
||||
if (!pattern.test(value)) {tip("Please provide a valid URL", false, "error"); return;}
|
||||
closeDialogs();
|
||||
loadMapFromURL(value);
|
||||
$(this).dialog("close");
|
||||
},
|
||||
|
|
@ -1104,5 +1101,6 @@ function loadURL() {
|
|||
document.getElementById("mapToLoad").addEventListener("change", function() {
|
||||
const fileToLoad = this.files[0];
|
||||
this.value = "";
|
||||
closeDialogs();
|
||||
uploadFile(fileToLoad);
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue