diff --git a/index.html b/index.html index 2a2a7ce9..e6eb9d4d 100644 --- a/index.html +++ b/index.html @@ -3473,6 +3473,15 @@

Warning!

+ +
+
+ + + +
+
+
diff --git a/main.js b/main.js index 0766bddc..40675639 100644 --- a/main.js +++ b/main.js @@ -254,11 +254,11 @@ function findBurgForMFCG(params) { const cells = pack.cells, burgs = pack.burgs; if (pack.burgs.length < 2) {console.error("Cannot select a burg for MFCG"); return;} + // used for selection 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"); + const coast = +params.get("coast"); + const port = +params.get("port"); + const river = +params.get("river"); let selection = defineSelection(coast, port, river); if (!selection.length) selection = defineSelection(coast, !port, !river); @@ -278,8 +278,13 @@ function findBurgForMFCG(params) { 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 referrer = new URL(document.referrer).searchParams; + for (let p of referrer) { + if (p[0] === "size") burgs[b].population = +p[1]; else + if (p[0] === "shantytown") burgs[b].shanty = +p[1]; else + burgs[b][p[0]] = p[1]; + } const label = burgLabels.select("[data-id='" + b + "']"); if (label.size()) { @@ -290,6 +295,7 @@ function findBurgForMFCG(params) { }); } + burgs[b].MFCG = +seed; zoomTo(burgs[b].x, burgs[b].y, 8, 1600); invokeActiveZooming(); } @@ -1242,7 +1248,7 @@ function addMarkers(number = 1) { addMarker("mine", "⚒", 50, 50, 20); const resources = {"salt":5, "gold":2, "silver":4, "copper":2, "iron":3, "lead":1, "tin":1}; - while (count) { + while (count && hills.length) { const cell = hills.splice(Math.floor(Math.random() * hills.length), 1); const x = cells.p[cell][0], y = cells.p[cell][1]; const id = getNextId("markerElement"); diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js index 8f6c1ecb..c2e26efe 100644 --- a/modules/ui/burg-editor.js +++ b/modules/ui/burg-editor.js @@ -281,46 +281,49 @@ function editBurg(id) { function openInMFCG(event) { const id = elSelected.attr("data-id"); const burg = pack.burgs[id]; - const defSeed = seed + id.padStart(4, 0); + const defSeed = +(seed + id.padStart(4, 0)); if (isCtrlClick(event)) { - const newSeed = prompt(`Please provide a Medieval Fantasy City Generator seed. `+ - `Seed should be a number. Default seed is FMG map seed + burg id padded to 4 chars with zeros (${defSeed}). `+ - `Please note that if seed is custom, "Overworld" button from MFCG will open a different map`, burg.MFCG || defSeed); - if (newSeed) burg.MFCG = newSeed; else return; + prompt(`Please provide a Medieval Fantasy City Generator seed.
+ Seed should be a number. Default seed is FMG map seed + burg id padded to 4 chars with zeros (${defSeed}).
+ Please note that if seed is custom, "Overworld" button from MFCG will open a different map`, {default:burg.MFCG||defSeed, step:1, min:1, max:1e13-1}, v => { + burg.MFCG = v; + openMFCG(); + }); + } else openMFCG(); + + function openMFCG() { + const name = elSelected.text(); + const size = Math.max(Math.min(rn(burg.population), 65), 6); + + const s = burg.MFCG || defSeed; + const cell = burg.cell; + const hub = +pack.cells.road[cell] > 50; + const river = pack.cells.r[cell] ? 1 : 0; + + const coast = +burg.port; + const citadel = +burg.citadel; + const walls = +burg.walls; + const plaza = +burg.plaza; + const temple = +burg.temple; + const shanty = +burg.shanty; + + const site = "http://fantasycities.watabou.ru/"; + const url = `${site}?name=${name}&size=${size}&seed=${s}&hub=${hub}&random=0&continuous=0&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}`; + openURL(url); } - - const name = elSelected.text(); - const size = Math.max(Math.min(rn(burg.population), 65), 6); - - const s = burg.MFCG || defSeed; - const cell = burg.cell; - const hub = +pack.cells.road[cell] > 50; - const river = pack.cells.r[cell] ? 1 : 0; - - const coast = +burg.port; - const citadel = +burg.citadel; - const walls = +burg.walls; - const plaza = +burg.plaza; - const temple = +burg.temple; - const shanty = +burg.shanty; - - const url = `http://fantasycities.watabou.ru/?name=${name}&size=${size}&seed=${s}&hub=${hub}&random=0&continuous=0&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}`; - openURL(url); } function openInIAHG(event) { - const id = elSelected.attr("data-id"); - const burg = pack.burgs[id]; - const defSeed = `${seed}-b${id}`; + const id = elSelected.attr("data-id"), burg = pack.burgs[id], defSeed = `${seed}-b${id}`; + const openIAHG = () => openURL("https://ironarachne.com/heraldry/" + (burg.IAHG || defSeed)); if (isCtrlClick(event)) { - const newSeed = prompt(`Please provide an Iron Arachne Heraldry Generator seed. `+ - `Default seed is a combination of FMG map seed and burg id (${defSeed})`, burg.IAHG || defSeed); - if (newSeed) burg.IAHG = newSeed; else return; - } - - const s = burg.IAHG || defSeed; - openURL("https://ironarachne.com/heraldry/" + s); + prompt(`Please provide an Iron Arachne Heraldry Generator seed.
Default seed is a combination of FMG map seed and burg id (${defSeed})`, + {default:burg.IAHG || defSeed}, v => { + if (v && v != defSeed) burg.IAHG = v; + openIAHG(); + }); + } else openIAHG(); } function toggleRelocateBurg() { diff --git a/modules/ui/cultures-editor.js b/modules/ui/cultures-editor.js index 88820810..369083af 100644 --- a/modules/ui/cultures-editor.js +++ b/modules/ui/cultures-editor.js @@ -460,10 +460,10 @@ function editCultures() { } function changeCode(d) { - const code = prompt(`Please provide an abbreviation for culture: ${d.data.name}`, d.data.code); - if (!code) return; - pack.cultures[d.data.i].code = code; - nodes.select("g[data-id='"+d.data.i+"']").select("text").text(code); + prompt(`Please provide an abbreviation for culture: ${d.data.name}`, {default:d.data.code}, v => { + pack.cultures[d.data.i].code = v; + nodes.select("g[data-id='"+d.data.i+"']").select("text").text(v); + }); } } diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index 7648f0c9..66f8a0c6 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -711,12 +711,13 @@ function editHeightmap() { function setRange(event) { if (event.target.value !== "interval") return; - const interval = prompt("Set a height interval. E.g. '17-20'. Avoid space, use hyphen as a separator"); - if (!interval || interval === "") return; - const opt = document.createElement("option"); - opt.value = opt.innerHTML = interval; - event.target.add(opt); - event.target.value = interval; + + prompt("Set a height interval. Avoid space, use hyphen as a separator", {default:"17-20"}, v => { + const opt = document.createElement("option"); + opt.value = opt.innerHTML = v; + event.target.add(opt); + event.target.value = v; + }); } function selectTemplate(e) { diff --git a/modules/ui/layers.js b/modules/ui/layers.js index bf5e6bf4..20970df9 100644 --- a/modules/ui/layers.js +++ b/modules/ui/layers.js @@ -76,14 +76,14 @@ function changePreset(preset) { } function savePreset() { - 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"; + prompt("Please provide a preset name", {default:""}, preset => { + 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() { diff --git a/modules/ui/provinces-editor.js b/modules/ui/provinces-editor.js index 5d6d1912..bc7b6379 100644 --- a/modules/ui/provinces-editor.js +++ b/modules/ui/provinces-editor.js @@ -193,15 +193,15 @@ function editProvinces() { function provinceOpenCOA(event, p) { const defSeed = `${seed}-p${p}`; + const openIAHG = () => openURL("https://ironarachne.com/heraldry/" + (pack.provinces[p].IAHG || defSeed)); if (isCtrlClick(event)) { - const newSeed = prompt(`Please provide an Iron Arachne Heraldry Generator seed. `+ - `Default seed is a combination of FMG map seed and province id (${defSeed})`, pack.provinces[p].IAHG || defSeed); - if (newSeed && newSeed != defSeed) pack.provinces[p].IAHG = newSeed; else return; - } - - const s = pack.provinces[p].IAHG || defSeed; - openURL("https://ironarachne.com/heraldry/" + s); + prompt(`Please provide an Iron Arachne Heraldry Generator seed.
Default seed is a combination of FMG map seed and province id (${defSeed})`, + {default:pack.provinces[p].IAHG || defSeed}, v => { + if (v && v != defSeed) pack.provinces[p].IAHG = v; + openIAHG(); + }); + } else openIAHG(); } function capitalZoomIn(p) { diff --git a/modules/ui/religions-editor.js b/modules/ui/religions-editor.js index c4ad337b..704ff531 100644 --- a/modules/ui/religions-editor.js +++ b/modules/ui/religions-editor.js @@ -450,10 +450,10 @@ function editReligions() { } function changeCode(d) { - const code = prompt(`Please provide an abbreviation for ${d.data.name}`, d.data.code); - if (!code) return; - pack.religions[d.data.i].code = code; - nodes.select("g[data-id='"+d.data.i+"']").select("text").text(code); + prompt(`Please provide an abbreviation for ${d.data.name}`, {default:d.data.code}, v => { + pack.religions[d.data.i].code = v; + nodes.select("g[data-id='"+d.data.i+"']").select("text").text(v); + }); } } diff --git a/modules/ui/states-editor.js b/modules/ui/states-editor.js index a534001d..92af1fbf 100644 --- a/modules/ui/states-editor.js +++ b/modules/ui/states-editor.js @@ -307,15 +307,15 @@ function editStates() { function stateOpenCOA(event, state) { const defSeed = `${seed}-s${state}`; + const openIAHG = () => openURL("https://ironarachne.com/heraldry/" + (pack.states[state].IAHG || defSeed)); if (isCtrlClick(event)) { - const newSeed = prompt(`Please provide an Iron Arachne Heraldry Generator seed. `+ - `Default seed is a combination of FMG map seed and province id (${defSeed})`, pack.states[state].IAHG || defSeed); - if (newSeed && newSeed != defSeed) pack.states[state].IAHG = newSeed; else return; - } - - const s = pack.states[state].IAHG || defSeed; - openURL("https://ironarachne.com/heraldry/" + s); + prompt(`Please provide an Iron Arachne Heraldry Generator seed.
Default seed is a combination of FMG map seed and state id (${defSeed})`, + {default:pack.states[state].IAHG || defSeed}, v => { + if (v && v != defSeed) pack.states[state].IAHG = v; + openIAHG(); + }); + } else openIAHG(); } function changePopulation(state) { diff --git a/modules/ui/tools.js b/modules/ui/tools.js index 6ca05b78..5104cfb1 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -250,43 +250,39 @@ function regenerateMilitary() { } function regenerateMarkers(event) { - let number = gauss(1, .5, .3, 5, 2); - if (isCtrlClick(event)) { - const numberManual = prompt("Please provide markers number multiplier", 1); - if (numberManual === null || numberManual === "" || isNaN(+numberManual)) { - tip("The number provided is invalid, please try again and provide a valid number", false, "error", 4000); - return; - } + prompt("Please provide markers number multiplier", {default:1, step:.01, min:0, max:100}, v => { + if (v === null || v === "" || isNaN(+v)) return; + addNumberOfMarkers(Math.min(+v, 100)); + }); + } else addNumberOfMarkers(gauss(1, .5, .3, 5, 2)); - number = Math.min(+numberManual, 100); + function addNumberOfMarkers(number) { + // remove existing markers and assigned notes + markers.selectAll("use").each(function() { + const index = notes.findIndex(n => n.id === this.id); + if (index != -1) notes.splice(index, 1); + }).remove(); + + addMarkers(number); + if (!layerIsOn("toggleMarkers")) toggleMarkers(); } - - // remove existing markers and assigned notes - markers.selectAll("use").each(function() { - const index = notes.findIndex(n => n.id === this.id); - if (index != -1) notes.splice(index, 1); - }).remove(); - - addMarkers(number); } function regenerateZones(event) { - let number = gauss(1, .5, .6, 5, 2); - if (isCtrlClick(event)) { - const numberManual = prompt("Please provide zones number multiplier", 1); - if (numberManual === null || numberManual === "" || isNaN(+numberManual)) { - tip("The number provided is invalid, please try again and provide a valid number", false, "error", 4000); - return; - } + prompt("Please provide zones number multiplier", {default:1, step:.01, min:0, max:100}, v => { + if (v === null || v === "" || isNaN(+v)) return; + addNumberOfZones(Math.min(+v, 100)); + }); + } else addNumberOfZones(gauss(1, .5, .6, 5, 2)); - number = Math.min(+numberManual, 100); + function addNumberOfZones(number) { + zones.selectAll("g").remove(); // remove existing zones + addZones(number); + if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click(); + if (!layerIsOn("toggleZones")) toggleZones(); } - - zones.selectAll("g").remove(); // remove existing zones - addZones(number); - if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click(); } function unpressClickToAddButton() { diff --git a/modules/ui/units-editor.js b/modules/ui/units-editor.js index 072cd653..d545c128 100644 --- a/modules/ui/units-editor.js +++ b/modules/ui/units-editor.js @@ -41,8 +41,13 @@ function editUnits() { function changeDistanceUnit() { if (this.value === "custom_name") { - const custom = prompt("Provide a custom name for distance unit"); - if (custom) this.options.add(new Option(custom, custom, false, true)); + prompt("Provide a custom name for a distance unit", {default:""}, custom => { + this.options.add(new Option(custom, custom, false, true)); + lock("distanceUnit"); + drawScaleBar(); + calculateFriendlyGridSize(); + }); + return; } lock("distanceUnit"); @@ -69,9 +74,11 @@ function editUnits() { function changeHeightUnit() { if (this.value === "custom_name") { - const custom = prompt("Provide a custom name for height unit"); - if (custom) this.options.add(new Option(custom, custom, false, true)); - else this.value = "ft"; + prompt("Provide a custom name for a height unit", {default:""}, custom => { + this.options.add(new Option(custom, custom, false, true)); + lock("heightUnit"); + }); + return; } lock("heightUnit"); diff --git a/modules/utils.js b/modules/utils.js index 463fb0be..a28db030 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -608,5 +608,26 @@ function generateDate(from = 100, to = 1000) { return new Date(rand(from, to),rand(12),rand(31)).toLocaleDateString("en", {year:'numeric', month:'long', day:'numeric'}); } +// prompt replacer (prompt does not work in Electron) +window.prompt = function(dialogText = "Please provide an input", options = {default:1, step:.01, min:0, max:100}, callback) { + if (options.default === undefined) {console.error("Prompt: options object does not have default value defined"); return;} + const modal = document.getElementById("prompt"); + const input = modal.querySelector("#promptInput"); + modal.querySelector("#dialogText").innerHTML = dialogText; + const type = typeof(options.default) === "number" ? "number" : "text"; + input.type = type; + if (options.step !== undefined) input.step = options.step; + if (options.min !== undefined) input.min = options.min; + if (options.max !== undefined) input.max = options.max; + input.placeholder = "type a " + type; + input.value = options.default; + modal.showModal(); + + modal.addEventListener("close", () => { + if (callback && modal.returnValue === "yes") callback(input.value); + input.value = modal.returnValue = ""; + }, {once: true}); +} + // localStorageDB !function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}(); \ No newline at end of file