mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 12:01:23 +01:00
Merge remote-tracking branch 'origin/master' into localization-dev
This commit is contained in:
commit
60bbf2e487
292 changed files with 11868 additions and 7469 deletions
|
|
@ -60,7 +60,7 @@ window.Cloud = (function () {
|
|||
|
||||
async save(fileName, contents) {
|
||||
const resp = await this.call("filesUpload", {path: "/" + fileName, contents});
|
||||
DEBUG && console.log("Dropbox response:", resp);
|
||||
DEBUG && console.info("Dropbox response:", resp);
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ window.Cloud = (function () {
|
|||
|
||||
// Callback function for auth window
|
||||
async setDropBoxToken(token) {
|
||||
DEBUG && console.log("Access token:", token);
|
||||
DEBUG && console.info("Access token:", token);
|
||||
setToken(this.name, token);
|
||||
await this.connect(token);
|
||||
this.authWindow.close();
|
||||
|
|
@ -118,9 +118,9 @@ window.Cloud = (function () {
|
|||
},
|
||||
|
||||
async getLink(path) {
|
||||
// return existitng shared link
|
||||
// return existing shared link
|
||||
const sharedLinks = await this.call("sharingListSharedLinks", {path});
|
||||
if (sharedLinks.result.links.length) return resp.result.links[0].url;
|
||||
if (sharedLinks.result.links.length) return sharedLinks.result.links[0].url;
|
||||
|
||||
// create new shared link
|
||||
const settings = {
|
||||
|
|
@ -131,7 +131,7 @@ window.Cloud = (function () {
|
|||
allow_download: true
|
||||
};
|
||||
const resp = await this.call("sharingCreateSharedLinkWithSettings", {path, settings});
|
||||
DEBUG && console.log("Dropbox link object:", resp.result);
|
||||
DEBUG && console.info("Dropbox link object:", resp.result);
|
||||
return resp.result.url;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,27 +1,21 @@
|
|||
"use strict";
|
||||
// Functions to export map to image or data files
|
||||
|
||||
// download map as SVG
|
||||
async function saveSVG() {
|
||||
TIME && console.time("saveSVG");
|
||||
async function exportToSvg() {
|
||||
TIME && console.time("exportToSvg");
|
||||
const url = await getMapURL("svg", {fullMap: true});
|
||||
const link = document.createElement("a");
|
||||
link.download = getFileName() + ".svg";
|
||||
link.href = url;
|
||||
link.click();
|
||||
|
||||
tip(
|
||||
`${link.download} is saved. Open "Downloads" screen (ctrl + J) to check. You can set image scale in options`,
|
||||
true,
|
||||
"success",
|
||||
5000
|
||||
);
|
||||
TIME && console.timeEnd("saveSVG");
|
||||
const message = `${link.download} is saved. Open 'Downloads' screen (ctrl + J) to check`;
|
||||
tip(message, true, "success", 5000);
|
||||
TIME && console.timeEnd("exportToSvg");
|
||||
}
|
||||
|
||||
// download map as PNG
|
||||
async function savePNG() {
|
||||
TIME && console.time("savePNG");
|
||||
async function exportToPng() {
|
||||
TIME && console.time("exportToPng");
|
||||
const url = await getMapURL("png");
|
||||
|
||||
const link = document.createElement("a");
|
||||
|
|
@ -41,22 +35,18 @@ async function savePNG() {
|
|||
window.setTimeout(function () {
|
||||
canvas.remove();
|
||||
window.URL.revokeObjectURL(link.href);
|
||||
tip(
|
||||
`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`,
|
||||
true,
|
||||
"success",
|
||||
5000
|
||||
);
|
||||
|
||||
const message = `${link.download} is saved. Open 'Downloads' screen (ctrl + J) to check. You can set image scale in options`;
|
||||
tip(message, true, "success", 5000);
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
TIME && console.timeEnd("savePNG");
|
||||
TIME && console.timeEnd("exportToPng");
|
||||
}
|
||||
|
||||
// download map as JPEG
|
||||
async function saveJPEG() {
|
||||
TIME && console.time("saveJPEG");
|
||||
async function exportToJpeg() {
|
||||
TIME && console.time("exportToJpeg");
|
||||
const url = await getMapURL("png");
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
|
|
@ -77,103 +67,122 @@ async function saveJPEG() {
|
|||
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||
};
|
||||
|
||||
TIME && console.timeEnd("saveJPEG");
|
||||
TIME && console.timeEnd("exportToJpeg");
|
||||
}
|
||||
|
||||
// download map as png tiles
|
||||
async function saveTiles() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// download schema
|
||||
const urlSchema = await getMapURL("tiles", {debug: true, fullMap: true});
|
||||
await import("../../libs/jszip.min.js");
|
||||
const zip = new window.JSZip();
|
||||
async function exportToPngTiles() {
|
||||
const status = byId("tileStatus");
|
||||
status.innerHTML = "Preparing files...";
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = graphWidth;
|
||||
canvas.height = graphHeight;
|
||||
const urlSchema = await getMapURL("tiles", {debug: true, fullMap: true});
|
||||
await import("../../libs/jszip.min.js");
|
||||
const zip = new window.JSZip();
|
||||
|
||||
const imgSchema = new Image();
|
||||
imgSchema.src = urlSchema;
|
||||
imgSchema.onload = function () {
|
||||
ctx.drawImage(imgSchema, 0, 0, canvas.width, canvas.height);
|
||||
canvas.toBlob(blob => zip.file(`fmg_tile_schema.png`, blob));
|
||||
};
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = graphWidth;
|
||||
canvas.height = graphHeight;
|
||||
|
||||
// download tiles
|
||||
const url = await getMapURL("tiles", {fullMap: true});
|
||||
const tilesX = +document.getElementById("tileColsInput").value;
|
||||
const tilesY = +document.getElementById("tileRowsInput").value;
|
||||
const scale = +document.getElementById("tileScaleInput").value;
|
||||
const imgSchema = new Image();
|
||||
imgSchema.src = urlSchema;
|
||||
await loadImage(imgSchema);
|
||||
|
||||
const tileW = (graphWidth / tilesX) | 0;
|
||||
const tileH = (graphHeight / tilesY) | 0;
|
||||
const tolesTotal = tilesX * tilesY;
|
||||
status.innerHTML = "Drawing schema...";
|
||||
ctx.drawImage(imgSchema, 0, 0, canvas.width, canvas.height);
|
||||
const blob = await canvasToBlob(canvas, "image/png");
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
zip.file("schema.png", blob);
|
||||
|
||||
const width = graphWidth * scale;
|
||||
const height = width * (tileH / tileW);
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
// download tiles
|
||||
const url = await getMapURL("tiles", {fullMap: true});
|
||||
const tilesX = +byId("tileColsInput").value;
|
||||
const tilesY = +byId("tileRowsInput").value;
|
||||
const scale = +byId("tileScaleInput").value;
|
||||
const tolesTotal = tilesX * tilesY;
|
||||
|
||||
let loaded = 0;
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
img.onload = function () {
|
||||
for (let y = 0, i = 0; y + tileH <= graphHeight; y += tileH) {
|
||||
for (let x = 0; x + tileW <= graphWidth; x += tileW, i++) {
|
||||
ctx.drawImage(img, x, y, tileW, tileH, 0, 0, width, height);
|
||||
const name = `fmg_tile_${i}.png`;
|
||||
canvas.toBlob(blob => {
|
||||
zip.file(name, blob);
|
||||
loaded += 1;
|
||||
if (loaded === tolesTotal) return downloadZip();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const tileW = (graphWidth / tilesX) | 0;
|
||||
const tileH = (graphHeight / tilesY) | 0;
|
||||
|
||||
function downloadZip() {
|
||||
const name = `${getFileName()}.zip`;
|
||||
zip.generateAsync({type: "blob"}).then(blob => {
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = name;
|
||||
link.click();
|
||||
link.remove();
|
||||
const width = graphWidth * scale;
|
||||
const height = width * (tileH / tileW);
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
setTimeout(() => URL.revokeObjectURL(link.href), 5000);
|
||||
resolve(true);
|
||||
});
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
await loadImage(img);
|
||||
|
||||
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
for (let y = 0, row = 0, id = 1; y + tileH <= graphHeight; y += tileH, row++) {
|
||||
const rowName = alphabet[row % alphabet.length];
|
||||
|
||||
for (let x = 0, cell = 1; x + tileW <= graphWidth; x += tileW, cell++, id++) {
|
||||
status.innerHTML = `Drawing tile ${rowName}${cell} (${id} of ${tolesTotal})...`;
|
||||
ctx.drawImage(img, x, y, tileW, tileH, 0, 0, width, height);
|
||||
const blob = await canvasToBlob(canvas, "image/png");
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
zip.file(`${rowName}${cell}.png`, blob);
|
||||
}
|
||||
}
|
||||
|
||||
status.innerHTML = "Zipping files...";
|
||||
zip.generateAsync({type: "blob"}).then(blob => {
|
||||
status.innerHTML = "Downloading the archive...";
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = getFileName() + ".zip";
|
||||
link.click();
|
||||
link.remove();
|
||||
|
||||
status.innerHTML = 'Done. Check .zip file in "Downloads" (crtl + J)';
|
||||
setTimeout(() => URL.revokeObjectURL(link.href), 5000);
|
||||
});
|
||||
|
||||
// promisified img.onload
|
||||
function loadImage(img) {
|
||||
return new Promise((resolve, reject) => {
|
||||
img.onload = () => resolve();
|
||||
img.onerror = err => reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
// promisified canvas.toBlob
|
||||
function canvasToBlob(canvas, mimeType, qualityArgument = 1) {
|
||||
return new Promise((resolve, reject) => {
|
||||
canvas.toBlob(
|
||||
blob => {
|
||||
if (blob) resolve(blob);
|
||||
else reject(new Error("Canvas toBlob() error"));
|
||||
},
|
||||
mimeType,
|
||||
qualityArgument
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// parse map svg to object url
|
||||
async function getMapURL(type, options = {}) {
|
||||
async function getMapURL(type, options) {
|
||||
const {
|
||||
debug = false,
|
||||
globe = false,
|
||||
noLabels = false,
|
||||
noWater = false,
|
||||
noScaleBar = false,
|
||||
noIce = false,
|
||||
fullMap = false
|
||||
} = options;
|
||||
} = options || {};
|
||||
|
||||
if (fullMap) drawScaleBar(1);
|
||||
|
||||
const cloneEl = document.getElementById("map").cloneNode(true); // clone svg
|
||||
const cloneEl = byId("map").cloneNode(true); // clone svg
|
||||
cloneEl.id = "fantasyMap";
|
||||
document.body.appendChild(cloneEl);
|
||||
const clone = d3.select(cloneEl);
|
||||
if (!debug) clone.select("#debug")?.remove();
|
||||
|
||||
const cloneDefs = cloneEl.getElementsByTagName("defs")[0];
|
||||
const svgDefs = document.getElementById("defElements");
|
||||
const svgDefs = byId("defElements");
|
||||
|
||||
const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
||||
if (isFirefox && type === "mesh") clone.select("#oceanPattern")?.remove();
|
||||
if (globe) clone.select("#scaleBar")?.remove();
|
||||
if (noLabels) {
|
||||
clone.select("#labels #states")?.remove();
|
||||
clone.select("#labels #burgLabels")?.remove();
|
||||
|
|
@ -183,14 +192,18 @@ async function getMapURL(type, options = {}) {
|
|||
clone.select("#oceanBase").attr("opacity", 0);
|
||||
clone.select("#oceanPattern").attr("opacity", 0);
|
||||
}
|
||||
if (noScaleBar) clone.select("#scaleBar")?.remove();
|
||||
if (noIce) clone.select("#ice")?.remove();
|
||||
if (fullMap) {
|
||||
// reset transform to show the whole map
|
||||
clone.attr("width", graphWidth).attr("height", graphHeight);
|
||||
clone.select("#viewbox").attr("transform", null);
|
||||
drawScaleBar(scale);
|
||||
|
||||
if (!noScaleBar) {
|
||||
drawScaleBar(clone.select("#scaleBar"), 1);
|
||||
fitScaleBar(clone.select("#scaleBar"), graphWidth, graphHeight);
|
||||
}
|
||||
}
|
||||
if (noScaleBar) clone.select("#scaleBar")?.remove();
|
||||
|
||||
if (type === "svg") removeUnusedElements(clone);
|
||||
if (customization && type === "mesh") updateMeshCells(clone);
|
||||
|
|
@ -229,35 +242,35 @@ async function getMapURL(type, options = {}) {
|
|||
.forEach(el => {
|
||||
const href = el.getAttribute("href") || el.getAttribute("xlink:href");
|
||||
if (!href) return;
|
||||
const emblem = document.getElementById(href.slice(1));
|
||||
const emblem = byId(href.slice(1));
|
||||
if (emblem) cloneDefs.append(emblem.cloneNode(true));
|
||||
});
|
||||
} else {
|
||||
cloneDefs.querySelector("#defs-emblems")?.remove();
|
||||
}
|
||||
|
||||
// replace ocean pattern href to base64
|
||||
if (location.hostname) {
|
||||
const el = cloneEl.getElementById("oceanicPattern");
|
||||
const url = el?.getAttribute("href");
|
||||
if (url) {
|
||||
{
|
||||
// replace ocean pattern href to base64
|
||||
const image = cloneEl.getElementById("oceanicPattern");
|
||||
const href = image?.getAttribute("href");
|
||||
if (href) {
|
||||
await new Promise(resolve => {
|
||||
getBase64(url, base64 => {
|
||||
el.setAttribute("href", base64);
|
||||
getBase64(href, base64 => {
|
||||
image.setAttribute("href", base64);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// replace texture href to base64
|
||||
if (location.hostname) {
|
||||
const el = cloneEl.getElementById("textureImage");
|
||||
const url = el?.getAttribute("href");
|
||||
if (url) {
|
||||
{
|
||||
// replace texture href to base64
|
||||
const image = cloneEl.querySelector("#texture > image");
|
||||
const href = image?.getAttribute("href");
|
||||
if (href) {
|
||||
await new Promise(resolve => {
|
||||
getBase64(url, base64 => {
|
||||
el.setAttribute("href", base64);
|
||||
getBase64(href, base64 => {
|
||||
image.setAttribute("href", base64);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
|
@ -375,7 +388,7 @@ function removeUnusedElements(clone) {
|
|||
|
||||
function updateMeshCells(clone) {
|
||||
const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter(i => grid.cells.h[i] >= 20);
|
||||
const scheme = getColorScheme(terrs.attr("scheme"));
|
||||
const scheme = getColorScheme(terrs.select("#landHeights").attr("scheme"));
|
||||
clone.select("#heights").attr("filter", "url(#blur1)");
|
||||
clone
|
||||
.select("#heights")
|
||||
|
|
@ -400,12 +413,6 @@ function inlineStyle(clone) {
|
|||
const key = compStyle[i];
|
||||
const value = compStyle.getPropertyValue(key);
|
||||
|
||||
// Firefox mask hack
|
||||
if (key === "mask-image" && value !== defaultStyles.getPropertyValue(key)) {
|
||||
style += "mask-image: url('#land');";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === "cursor") continue; // cursor should be default
|
||||
if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute
|
||||
if (value === defaultStyles.getPropertyValue(key)) continue;
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
window.Formats = (function () {
|
||||
async function csvParser(file, separator = ",") {
|
||||
const txt = await file.text();
|
||||
const rows = txt.split("\n");
|
||||
const headers = rows
|
||||
.shift()
|
||||
.split(separator)
|
||||
.map(x => x.toLowerCase());
|
||||
const data = rows.filter(a => a.trim() !== "").map(r => r.split(separator));
|
||||
|
||||
return {
|
||||
headers,
|
||||
data,
|
||||
iterator: function* (sortf) {
|
||||
const dataset = sortf ? this.data.sort(sortf) : this.data;
|
||||
for (const d of dataset) yield Object.fromEntries(d.map((a, i) => [this.headers[i], a]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {csvParser};
|
||||
})();
|
||||
|
|
@ -10,33 +10,30 @@ async function quickLoad() {
|
|||
}
|
||||
|
||||
async function loadFromDropbox() {
|
||||
const mapPath = document.getElementById("loadFromDropboxSelect")?.value;
|
||||
const mapPath = byId("loadFromDropboxSelect")?.value;
|
||||
|
||||
DEBUG && console.log("Loading map from Dropbox:", mapPath);
|
||||
DEBUG && console.info("Loading map from Dropbox:", mapPath);
|
||||
const blob = await Cloud.providers.dropbox.load(mapPath);
|
||||
uploadMap(blob);
|
||||
}
|
||||
|
||||
async function createSharableDropboxLink() {
|
||||
const mapFile = document.querySelector("#loadFromDropbox select").value;
|
||||
const sharableLink = document.getElementById("sharableLink");
|
||||
const sharableLinkContainer = document.getElementById("sharableLinkContainer");
|
||||
let url;
|
||||
const sharableLink = byId("sharableLink");
|
||||
const sharableLinkContainer = byId("sharableLinkContainer");
|
||||
|
||||
try {
|
||||
url = await Cloud.providers.dropbox.getLink(mapFile);
|
||||
} catch {
|
||||
const previewLink = await Cloud.providers.dropbox.getLink(mapFile);
|
||||
const directLink = previewLink.replace("www.dropbox.com", "dl.dropboxusercontent.com"); // DL allows CORS
|
||||
const finalLink = `${location.origin}${location.pathname}?maplink=${directLink}`;
|
||||
|
||||
sharableLink.innerText = finalLink.slice(0, 45) + "...";
|
||||
sharableLink.setAttribute("href", finalLink);
|
||||
sharableLinkContainer.style.display = "block";
|
||||
} catch (error) {
|
||||
ERROR && console.error(error);
|
||||
return tip("Dropbox API error. Can not create link.", true, "error", 2000);
|
||||
}
|
||||
|
||||
const fmg = window.location.href.split("?")[0];
|
||||
const reallink = `${fmg}?maplink=${url}`;
|
||||
// voodoo magic required by the yellow god of CORS
|
||||
const link = reallink.replace("www.dropbox.com/s/", "dl.dropboxusercontent.com/1/view/");
|
||||
const shortLink = link.slice(0, 50) + "...";
|
||||
|
||||
sharableLinkContainer.style.display = "block";
|
||||
sharableLink.innerText = shortLink;
|
||||
sharableLink.setAttribute("href", link);
|
||||
}
|
||||
|
||||
function loadMapPrompt(blob) {
|
||||
|
|
@ -113,7 +110,7 @@ function uploadMap(file, callback) {
|
|||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = async function (fileLoadedEvent) {
|
||||
if (callback) callback();
|
||||
document.getElementById("coas").innerHTML = ""; // remove auto-generated emblems
|
||||
byId("coas").innerHTML = ""; // remove auto-generated emblems
|
||||
const result = fileLoadedEvent.target.result;
|
||||
const [mapData, mapVersion] = await parseLoadedResult(result);
|
||||
|
||||
|
|
@ -186,22 +183,22 @@ function showUploadMessage(type, mapData, mapVersion) {
|
|||
title = "Newer file";
|
||||
canBeLoaded = false;
|
||||
} else if (type === "outdated") {
|
||||
message = `The map version (${mapVersion}) does not match the Generator version (${version}).<br>That is fine, click OK to the get map <b style="color: #005000">auto-updated</b>.<br>In case of issues please keep using an ${archive} of the Generator`;
|
||||
title = "Outdated file";
|
||||
canBeLoaded = true;
|
||||
INFO && console.info(`Loading map. Auto-update from ${mapVersion} to ${version}`);
|
||||
parseLoadedData(mapData, mapVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
alertMessage.innerHTML = message;
|
||||
const buttons = {
|
||||
OK: function () {
|
||||
$(this).dialog("close");
|
||||
if (canBeLoaded) parseLoadedData(mapData);
|
||||
if (canBeLoaded) parseLoadedData(mapData, mapVersion);
|
||||
}
|
||||
};
|
||||
$("#alert").dialog({title, buttons});
|
||||
}
|
||||
|
||||
async function parseLoadedData(data) {
|
||||
async function parseLoadedData(data, mapVersion) {
|
||||
try {
|
||||
// exit customization
|
||||
if (window.closeDialogs) closeDialogs();
|
||||
|
|
@ -221,6 +218,7 @@ async function parseLoadedData(data) {
|
|||
|
||||
INFO && console.group("Loaded Map " + seed);
|
||||
|
||||
// TODO: move all to options object
|
||||
void (function parseSettings() {
|
||||
const settings = data[1].split("|");
|
||||
if (settings[0]) applyOption(distanceUnitInput, settings[0]);
|
||||
|
|
@ -229,23 +227,16 @@ async function parseLoadedData(data) {
|
|||
if (settings[3]) applyOption(heightUnit, settings[3]);
|
||||
if (settings[4]) heightExponentInput.value = heightExponentOutput.value = settings[4];
|
||||
if (settings[5]) temperatureScale.value = settings[5];
|
||||
if (settings[6]) barSizeInput.value = barSizeOutput.value = settings[6];
|
||||
if (settings[7] !== undefined) barLabel.value = settings[7];
|
||||
if (settings[8] !== undefined) barBackOpacity.value = settings[8];
|
||||
if (settings[9]) barBackColor.value = settings[9];
|
||||
if (settings[10]) barPosX.value = settings[10];
|
||||
if (settings[11]) barPosY.value = settings[11];
|
||||
// setting 6-11 (scaleBar) are part of style now, kept as "" in newer versions for compatibility
|
||||
if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12];
|
||||
if (settings[13]) urbanization = urbanizationInput.value = urbanizationOutput.value = settings[13];
|
||||
if (settings[14]) mapSizeInput.value = mapSizeOutput.value = minmax(settings[14], 1, 100);
|
||||
if (settings[15]) latitudeInput.value = latitudeOutput.value = minmax(settings[15], 0, 100);
|
||||
if (settings[18]) precInput.value = precOutput.value = settings[18];
|
||||
|
||||
if (settings[19]) options = JSON.parse(settings[19]);
|
||||
// setting 16 and 17 (temperature) are part of options now, kept as "" in newer versions for compatibility
|
||||
if (settings[16]) options.temperatureEquator = +settings[16];
|
||||
if (settings[17]) options.temperatureNorthPole = options.temperatureSouthPole = +settings[17];
|
||||
|
||||
if (settings[20]) mapName.value = settings[20];
|
||||
if (settings[21]) hideLabels.checked = +settings[21];
|
||||
if (settings[22]) stylePreset.value = settings[22];
|
||||
|
|
@ -351,6 +342,15 @@ async function parseLoadedData(data) {
|
|||
statePaths?.remove();
|
||||
})();
|
||||
|
||||
void (function addMissingElements() {
|
||||
if (!texture.size()) {
|
||||
texture = viewbox
|
||||
.insert("g", "#landmass")
|
||||
.attr("id", "texture")
|
||||
.attr("data-href", "./images/textures/plaster.jpg");
|
||||
}
|
||||
})();
|
||||
|
||||
void (function parseGridData() {
|
||||
grid = JSON.parse(data[6]);
|
||||
|
||||
|
|
@ -404,46 +404,46 @@ async function parseLoadedData(data) {
|
|||
})();
|
||||
|
||||
void (function restoreLayersState() {
|
||||
// helper functions
|
||||
const notHidden = selection => selection.node() && selection.style("display") !== "none";
|
||||
const isVisible = selection => selection.node() && selection.style("display") !== "none";
|
||||
const isVisibleNode = node => node && node.style.display !== "none";
|
||||
const hasChildren = selection => selection.node()?.hasChildNodes();
|
||||
const hasChild = (selection, selector) => selection.node()?.querySelector(selector);
|
||||
const turnOn = el => document.getElementById(el).classList.remove("buttonoff");
|
||||
const turnOn = el => byId(el).classList.remove("buttonoff");
|
||||
|
||||
// turn all layers off
|
||||
document
|
||||
.getElementById("mapLayers")
|
||||
byId("mapLayers")
|
||||
.querySelectorAll("li")
|
||||
.forEach(el => el.classList.add("buttonoff"));
|
||||
|
||||
// turn on active layers
|
||||
if (notHidden(texture) && hasChild(texture, "image")) turnOn("toggleTexture");
|
||||
if (hasChild(texture, "image")) turnOn("toggleTexture");
|
||||
if (hasChildren(terrs)) turnOn("toggleHeight");
|
||||
if (hasChildren(biomes)) turnOn("toggleBiomes");
|
||||
if (hasChildren(cells)) turnOn("toggleCells");
|
||||
if (hasChildren(gridOverlay)) turnOn("toggleGrid");
|
||||
if (hasChildren(coordinates)) turnOn("toggleCoordinates");
|
||||
if (notHidden(compass) && hasChild(compass, "use")) turnOn("toggleCompass");
|
||||
if (isVisible(compass) && hasChild(compass, "use")) turnOn("toggleCompass");
|
||||
if (hasChildren(rivers)) turnOn("toggleRivers");
|
||||
if (notHidden(terrain) && hasChildren(terrain)) turnOn("toggleRelief");
|
||||
if (isVisible(terrain) && hasChildren(terrain)) turnOn("toggleRelief");
|
||||
if (hasChildren(relig)) turnOn("toggleReligions");
|
||||
if (hasChildren(cults)) turnOn("toggleCultures");
|
||||
if (hasChildren(statesBody)) turnOn("toggleStates");
|
||||
if (hasChildren(provs)) turnOn("toggleProvinces");
|
||||
if (hasChildren(zones) && notHidden(zones)) turnOn("toggleZones");
|
||||
if (notHidden(borders) && hasChild(borders, "path")) turnOn("toggleBorders");
|
||||
if (notHidden(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
|
||||
if (hasChildren(zones) && isVisible(zones)) turnOn("toggleZones");
|
||||
if (isVisible(borders) && hasChild(borders, "path")) turnOn("toggleBorders");
|
||||
if (isVisible(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
|
||||
if (hasChildren(temperature)) turnOn("toggleTemp");
|
||||
if (hasChild(population, "line")) turnOn("togglePopulation");
|
||||
if (hasChildren(ice)) turnOn("toggleIce");
|
||||
if (hasChild(prec, "circle")) turnOn("togglePrec");
|
||||
if (notHidden(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
|
||||
if (notHidden(labels)) turnOn("toggleLabels");
|
||||
if (notHidden(icons)) turnOn("toggleIcons");
|
||||
if (hasChildren(armies) && notHidden(armies)) turnOn("toggleMilitary");
|
||||
if (isVisible(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
|
||||
if (isVisible(labels)) turnOn("toggleLabels");
|
||||
if (isVisible(icons)) turnOn("toggleIcons");
|
||||
if (hasChildren(armies) && isVisible(armies)) turnOn("toggleMilitary");
|
||||
if (hasChildren(markers)) turnOn("toggleMarkers");
|
||||
if (notHidden(ruler)) turnOn("toggleRulers");
|
||||
if (notHidden(scaleBar)) turnOn("toggleScaleBar");
|
||||
if (isVisible(ruler)) turnOn("toggleRulers");
|
||||
if (isVisible(scaleBar)) turnOn("toggleScaleBar");
|
||||
if (isVisibleNode(byId("vignette"))) turnOn("toggleVignette");
|
||||
|
||||
getCurrentPreset();
|
||||
})();
|
||||
|
|
@ -456,17 +456,31 @@ async function parseLoadedData(data) {
|
|||
})();
|
||||
|
||||
{
|
||||
// dynamically import and run auto-udpdate script
|
||||
// dynamically import and run auto-update script
|
||||
const versionNumber = parseFloat(params[0]);
|
||||
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.93.00");
|
||||
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.97.04");
|
||||
resolveVersionConflicts(versionNumber);
|
||||
}
|
||||
|
||||
// add custom heightmap color scheme if any
|
||||
if (heightmapColorSchemes) {
|
||||
const oceanScheme = byId("oceanHeights")?.getAttribute("scheme");
|
||||
if (oceanScheme && !(oceanScheme in heightmapColorSchemes)) addCustomColorScheme(oceanScheme);
|
||||
const landScheme = byId("#landHeights")?.getAttribute("scheme");
|
||||
if (landScheme && !(landScheme in heightmapColorSchemes)) addCustomColorScheme(landScheme);
|
||||
}
|
||||
|
||||
{
|
||||
// add custom texture if any
|
||||
const textureHref = texture.attr("data-href");
|
||||
if (textureHref) updateTextureSelectValue(textureHref);
|
||||
}
|
||||
|
||||
void (function checkDataIntegrity() {
|
||||
const cells = pack.cells;
|
||||
|
||||
if (pack.cells.i.length !== pack.cells.state.length) {
|
||||
const message = "Data Integrity Check. Striping issue detected. To fix edit the heightmap in erase mode";
|
||||
const message = "Data integrity check. Striping issue detected. To fix edit the heightmap in ERASE mode";
|
||||
ERROR && console.error(message);
|
||||
}
|
||||
|
||||
|
|
@ -474,7 +488,7 @@ async function parseLoadedData(data) {
|
|||
invalidStates.forEach(s => {
|
||||
const invalidCells = cells.i.filter(i => cells.state[i] === s);
|
||||
invalidCells.forEach(i => (cells.state[i] = 0));
|
||||
ERROR && console.error("Data Integrity Check. Invalid state", s, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid state", s, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
const invalidProvinces = [...new Set(cells.province)].filter(
|
||||
|
|
@ -483,14 +497,14 @@ async function parseLoadedData(data) {
|
|||
invalidProvinces.forEach(p => {
|
||||
const invalidCells = cells.i.filter(i => cells.province[i] === p);
|
||||
invalidCells.forEach(i => (cells.province[i] = 0));
|
||||
ERROR && console.error("Data Integrity Check. Invalid province", p, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid province", p, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
const invalidCultures = [...new Set(cells.culture)].filter(c => !pack.cultures[c] || pack.cultures[c].removed);
|
||||
invalidCultures.forEach(c => {
|
||||
const invalidCells = cells.i.filter(i => cells.culture[i] === c);
|
||||
invalidCells.forEach(i => (cells.province[i] = 0));
|
||||
ERROR && console.error("Data Integrity Check. Invalid culture", c, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid culture", c, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
const invalidReligions = [...new Set(cells.religion)].filter(
|
||||
|
|
@ -499,14 +513,14 @@ async function parseLoadedData(data) {
|
|||
invalidReligions.forEach(r => {
|
||||
const invalidCells = cells.i.filter(i => cells.religion[i] === r);
|
||||
invalidCells.forEach(i => (cells.religion[i] = 0));
|
||||
ERROR && console.error("Data Integrity Check. Invalid religion", r, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid religion", r, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
const invalidFeatures = [...new Set(cells.f)].filter(f => f && !pack.features[f]);
|
||||
invalidFeatures.forEach(f => {
|
||||
const invalidCells = cells.i.filter(i => cells.f[i] === f);
|
||||
// No fix as for now
|
||||
ERROR && console.error("Data Integrity Check. Invalid feature", f, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid feature", f, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
const invalidBurgs = [...new Set(cells.burg)].filter(
|
||||
|
|
@ -515,7 +529,7 @@ async function parseLoadedData(data) {
|
|||
invalidBurgs.forEach(burgId => {
|
||||
const invalidCells = cells.i.filter(i => cells.burg[i] === burgId);
|
||||
invalidCells.forEach(i => (cells.burg[i] = 0));
|
||||
ERROR && console.error("Data Integrity Check. Invalid burg", burgId, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid burg", burgId, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
const invalidRivers = [...new Set(cells.r)].filter(r => r && !pack.rivers.find(river => river.i === r));
|
||||
|
|
@ -523,60 +537,112 @@ async function parseLoadedData(data) {
|
|||
const invalidCells = cells.i.filter(i => cells.r[i] === r);
|
||||
invalidCells.forEach(i => (cells.r[i] = 0));
|
||||
rivers.select("river" + r).remove();
|
||||
ERROR && console.error("Data Integrity Check. Invalid river", r, "is assigned to cells", invalidCells);
|
||||
ERROR && console.error("Data integrity check. Invalid river", r, "is assigned to cells", invalidCells);
|
||||
});
|
||||
|
||||
pack.burgs.forEach(burg => {
|
||||
if ((!burg.i || burg.removed) && burg.lock) {
|
||||
if (typeof burg.capital === "boolean") burg.capital = Number(burg.capital);
|
||||
|
||||
if (!burg.i && burg.lock) {
|
||||
ERROR && console.error(`Data integrity check. Burg 0 is marked as locked, removing the status`);
|
||||
delete burg.lock;
|
||||
return;
|
||||
}
|
||||
|
||||
if (burg.removed && burg.lock) {
|
||||
ERROR &&
|
||||
console.error(
|
||||
`Data Integrity Check. Burg ${burg.i || "0"} is removed or invalid but still locked. Unlocking the burg`
|
||||
);
|
||||
console.error(`Data integrity check. Removed burg ${burg.i} is marked as locked. Unlocking the burg`);
|
||||
delete burg.lock;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!burg.i || burg.removed) return;
|
||||
|
||||
if (burg.cell === undefined || burg.x === undefined || burg.y === undefined) {
|
||||
ERROR &&
|
||||
console.error(
|
||||
`Data Integrity Check. Burg ${burg.i} is missing cell info or coordinates. Removing the burg`
|
||||
`Data integrity check. Burg ${burg.i} is missing cell info or coordinates. Removing the burg`
|
||||
);
|
||||
burg.removed = true;
|
||||
}
|
||||
|
||||
if (burg.port < 0) {
|
||||
ERROR && console.error("Data Integrity Check. Burg", burg.i, "has invalid port value", burg.port);
|
||||
ERROR && console.error("Data integrity check. Burg", burg.i, "has invalid port value", burg.port);
|
||||
burg.port = 0;
|
||||
}
|
||||
|
||||
if (burg.cell >= cells.i.length) {
|
||||
ERROR && console.error("Data Integrity Check. Burg", burg.i, "is linked to invalid cell", burg.cell);
|
||||
ERROR && console.error("Data integrity check. Burg", burg.i, "is linked to invalid cell", burg.cell);
|
||||
burg.cell = findCell(burg.x, burg.y);
|
||||
cells.i.filter(i => cells.burg[i] === burg.i).forEach(i => (cells.burg[i] = 0));
|
||||
cells.burg[burg.cell] = burg.i;
|
||||
}
|
||||
|
||||
if (burg.state && !pack.states[burg.state]) {
|
||||
ERROR && console.error("Data Integrity Check. Burg", burg.i, "is linked to invalid state", burg.state);
|
||||
ERROR && console.error("Data integrity check. Burg", burg.i, "is linked to invalid state", burg.state);
|
||||
burg.state = 0;
|
||||
}
|
||||
|
||||
if (burg.state && pack.states[burg.state].removed) {
|
||||
ERROR && console.error("Data Integrity Check. Burg", burg.i, "is linked to removed state", burg.state);
|
||||
ERROR && console.error("Data integrity check. Burg", burg.i, "is linked to removed state", burg.state);
|
||||
burg.state = 0;
|
||||
}
|
||||
|
||||
if (burg.state === undefined) {
|
||||
ERROR && console.error("Data Integrity Check. Burg", burg.i, "has no state data");
|
||||
ERROR && console.error("Data integrity check. Burg", burg.i, "has no state data");
|
||||
burg.state = 0;
|
||||
}
|
||||
});
|
||||
|
||||
pack.states.forEach(state => {
|
||||
if (state.removed) return;
|
||||
|
||||
const stateBurgs = pack.burgs.filter(b => b.state === state.i && !b.removed);
|
||||
const capitalBurgs = stateBurgs.filter(b => b.capital);
|
||||
|
||||
if (!state.i && capitalBurgs.length) {
|
||||
ERROR &&
|
||||
console.error(
|
||||
`Data integrity check. Neutral burgs (${capitalBurgs
|
||||
.map(b => b.i)
|
||||
.join(", ")}) marked as capitals. Moving them to towns`
|
||||
);
|
||||
|
||||
capitalBurgs.forEach(burg => {
|
||||
burg.capital = 0;
|
||||
moveBurgToGroup(burg.i, "towns");
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (capitalBurgs.length > 1) {
|
||||
const message = `Data integrity check. State ${state.i} has multiple capitals (${capitalBurgs
|
||||
.map(b => b.i)
|
||||
.join(", ")}) assigned. Keeping the first as capital and moving others to towns`;
|
||||
ERROR && console.error(message);
|
||||
|
||||
capitalBurgs.forEach((burg, i) => {
|
||||
if (!i) return;
|
||||
burg.capital = 0;
|
||||
moveBurgToGroup(burg.i, "towns");
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.i && stateBurgs.length && !capitalBurgs.length) {
|
||||
ERROR &&
|
||||
console.error(`Data integrity check. State ${state.i} has no capital. Assigning the first burg as capital`);
|
||||
stateBurgs[0].capital = 1;
|
||||
moveBurgToGroup(stateBurgs[0].i, "cities");
|
||||
}
|
||||
});
|
||||
|
||||
pack.provinces.forEach(p => {
|
||||
if (!p.i || p.removed) return;
|
||||
if (pack.states[p.state] && !pack.states[p.state].removed) return;
|
||||
ERROR && console.error("Data Integrity Check. Province", p.i, "is linked to removed state", p.state);
|
||||
ERROR && console.error("Data integrity check. Province", p.i, "is linked to removed state", p.state);
|
||||
p.removed = true; // remove incorrect province
|
||||
});
|
||||
|
||||
|
|
@ -586,7 +652,7 @@ async function parseLoadedData(data) {
|
|||
|
||||
pack.markers.forEach(marker => {
|
||||
if (markerIds[marker.i]) {
|
||||
ERROR && console.error("Data Integrity Check. Marker", marker.i, "has non-unique id. Changing to", nextId);
|
||||
ERROR && console.error("Data integrity check. Marker", marker.i, "has non-unique id. Changing to", nextId);
|
||||
|
||||
const domElements = document.querySelectorAll("#marker" + marker.i);
|
||||
if (domElements[1]) domElements[1].id = "marker" + nextId; // rename 2nd dom element
|
||||
|
|
@ -606,7 +672,7 @@ async function parseLoadedData(data) {
|
|||
}
|
||||
})();
|
||||
|
||||
changeMapSize();
|
||||
fitMapToScreen();
|
||||
|
||||
// remove href from emblems, to trigger rendering on load
|
||||
emblems.selectAll("use").attr("href", null);
|
||||
|
|
@ -627,7 +693,7 @@ async function parseLoadedData(data) {
|
|||
ERROR && console.error(error);
|
||||
clearMainTip();
|
||||
|
||||
alertMessage.innerHTML = /* html */ `An error is occured on map loading. Select a different file to load, <br />generate a new random map or cancel the loading
|
||||
alertMessage.innerHTML = /* html */ `An error is occured on map loading. Select a different file to load, <br>generate a new random map or cancel the loading.<br>Map version: ${mapVersion}. Generator version: ${version}.
|
||||
<p id="errorBox">${parseError(error)}</p>`;
|
||||
|
||||
$("#alert").dialog({
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ function prepareMapData() {
|
|||
heightUnit.value,
|
||||
heightExponentInput.value,
|
||||
temperatureScale.value,
|
||||
barSizeInput.value,
|
||||
barLabel.value,
|
||||
barBackOpacity.value,
|
||||
barBackColor.value,
|
||||
barPosX.value,
|
||||
barPosY.value,
|
||||
"", // previously used for barSize.value
|
||||
"", // previously used for barLabel.value
|
||||
"", // previously used for barBackColor.value
|
||||
"", // previously used for barBackColor.value
|
||||
"", // previously used for barPosX.value
|
||||
"", // previously used for barPosY.value
|
||||
populationRate,
|
||||
urbanization,
|
||||
mapSizeOutput.value,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue