diff --git a/modules/heightmap-templates.js b/config/heightmap-templates.js similarity index 77% rename from modules/heightmap-templates.js rename to config/heightmap-templates.js index 7e0cd20c..29738609 100644 --- a/modules/heightmap-templates.js +++ b/config/heightmap-templates.js @@ -1,6 +1,6 @@ "use strict"; -window.HeightmapTemplates = (function () { +const heightmapTemplates = (function () { const volcano = `Hill 1 90-100 44-56 40-60 Multiply 0.8 50-100 0 0 Range 1.5 30-55 45-55 40-60 @@ -148,20 +148,19 @@ window.HeightmapTemplates = (function () { Range 6-8 40-50 5-95 10-90`; return { - volcano, - highIsland, - lowIsland, - continents, - archipelago, - atoll, - mediterranean, - peninsula, - peninsula, - pangea, - isthmus, - shattered, - taklamakan, - oldWorld, - fractious + volcano: {id: 0, name: "Volcano", template: volcano, probability: 3}, + highIsland: {id: 1, name: "High Island", template: highIsland, probability: 19}, + lowIsland: {id: 2, name: "Low Island", template: lowIsland, probability: 9}, + continents: {id: 3, name: "Continents", template: continents, probability: 16}, + archipelago: {id: 4, name: "Archipelago", template: archipelago, probability: 18}, + atoll: {id: 5, name: "Atoll", template: atoll, probability: 1}, + mediterranean: {id: 6, name: "Mediterranean", template: mediterranean, probability: 5}, + peninsula: {id: 7, name: "Peninsula", template: peninsula, probability: 3}, + pangea: {id: 8, name: "Pangea", template: pangea, probability: 5}, + isthmus: {id: 9, name: "Isthmus", template: isthmus, probability: 2}, + shattered: {id: 10, name: "Shattered", template: shattered, probability: 7}, + taklamakan: {id: 11, name: "Taklamakan", template: taklamakan, probability: 1}, + oldWorld: {id: 12, name: "Old World", template: oldWorld, probability: 8}, + fractious: {id: 13, name: "Fractious", template: fractious, probability: 3} }; })(); diff --git a/config/precreated-heightmaps.js b/config/precreated-heightmaps.js new file mode 100644 index 00000000..22f45abd --- /dev/null +++ b/config/precreated-heightmaps.js @@ -0,0 +1,27 @@ +"use strict"; + +const precreatedHeightmaps = { + "africa-centric": {id: 0, name: "Africa Centric"}, + arabia: {id: 1, name: "Arabia"}, + atlantics: {id: 2, name: "Atlantics"}, + britain: {id: 3, name: "Britain"}, + caribbean: {id: 4, name: "Caribbean"}, + "east-asia": {id: 5, name: "East Asia"}, + eurasia: {id: 6, name: "Eurasia"}, + europe: {id: 7, name: "Europe"}, + "europe-accented": {id: 8, name: "Europe Accented"}, + "europe-and-central-asia": {id: 9, name: "Europe and Central Asia"}, + "europe-central": {id: 10, name: "Europe Central"}, + "europe-north": {id: 11, name: "Europe North"}, + greenland: {id: 12, name: "Greenland"}, + hellenica: {id: 13, name: "Hellenica"}, + iceland: {id: 14, name: "Iceland"}, + "indian-ocean": {id: 15, name: "Indian Ocean"}, + "mediterranean-sea": {id: 16, name: "Mediterranean Sea"}, + "middle-east": {id: 17, name: "Middle East"}, + "north-america": {id: 18, name: "North America"}, + "us-centric": {id: 19, name: "US-centric"}, + "us-mainland": {id: 20, name: "US Mainland"}, + world: {id: 21, name: "World"}, + "world-from-pacific": {id: 22, name: "World from Pacific"} +}; diff --git a/index.html b/index.html index d027e45c..c3ef5ab1 100644 --- a/index.html +++ b/index.html @@ -1324,53 +1324,9 @@ Heightmap - - - - + + @@ -6133,7 +6089,8 @@ - + + diff --git a/main.js b/main.js index cbd67e94..7a13bd5d 100644 --- a/main.js +++ b/main.js @@ -899,7 +899,7 @@ function addLakesInDeepDepressions() { // near sea lakes usually get a lot of water inflow, most of them should brake threshold and flow out to sea (see Ancylus Lake) function openNearSeaLakes() { - if (templateInput.value === "Atoll") return; // no need for Atolls + if (byId("templateInput").value === "Atoll") return; // no need for Atolls const cells = grid.cells; const features = grid.features; @@ -944,7 +944,7 @@ function defineMapSize() { if (randomize || !locked("latitude")) latitudeOutput.value = latitudeInput.value = rn(latitude); function getSizeAndLatitude() { - const template = document.getElementById("templateInput").value; // heightmap template + const template = byId("templateInput").value; // heightmap template if (template === "africa-centric") return [45, 53]; if (template === "arabia") return [20, 35]; @@ -1921,11 +1921,14 @@ function addZones(number = 1) { // show map stats on generation complete function showStatistics() { - const template = templateInput.options[templateInput.selectedIndex].text; - const templateRandom = locked("template") ? "" : "(random)"; + const heightmap = byId("templateInput").value; + const isTemplate = heightmap in heightmapTemplates; + const heightmapType = isTemplate ? "template" : "precreated"; + const isRandomTemplate = isTemplate && !locked("template") ? "random " : ""; + const stats = ` Seed: ${seed} - Canvas size: ${graphWidth}x${graphHeight} - Template: ${template} ${templateRandom} + Canvas size: ${graphWidth}x${graphHeight} px + Heightmap: ${heightmap} (${isRandomTemplate}${heightmapType}) Points: ${grid.points.length} Cells: ${pack.cells.i.length} Map size: ${mapSizeOutput.value}% @@ -1937,7 +1940,7 @@ function showStatistics() { Cultures: ${pack.cultures.length - 1}`; mapId = Date.now(); // unique map id is it's creation date number - mapHistory.push({seed, width: graphWidth, height: graphHeight, template, created: mapId}); + mapHistory.push({seed, width: graphWidth, height: graphHeight, template: heightmap, created: mapId}); INFO && console.log(stats); } diff --git a/modules/dynamic/heightmap-selection.js b/modules/dynamic/heightmap-selection.js index 7b7a0412..15f62ce4 100644 --- a/modules/dynamic/heightmap-selection.js +++ b/modules/dynamic/heightmap-selection.js @@ -1,46 +1,3 @@ -const templates = [ - {id: "volcano", name: "Volcano"}, - {id: "highIsland", name: "High Island"}, - {id: "lowIsland", name: "Low Island"}, - {id: "continents", name: "Continents"}, - {id: "archipelago", name: "Archipelago"}, - {id: "atoll", name: "Atoll"}, - {id: "mediterranean", name: "Mediterranean"}, - {id: "peninsula", name: "Peninsula"}, - {id: "pangea", name: "Pangea"}, - {id: "isthmus", name: "Isthmus"}, - {id: "shattered", name: "Shattered"}, - {id: "taklamakan", name: "Taklamakan"}, - {id: "oldWorld", name: "Old World"}, - {id: "fractious", name: "Fractious"} -]; - -const heightmaps = [ - {id: "africa-centric", name: "Africa Centric"}, - {id: "arabia", name: "Arabia"}, - {id: "atlantics", name: "Atlantics"}, - {id: "britain", name: "Britain"}, - {id: "caribbean", name: "Caribbean"}, - {id: "east-asia", name: "East Asia"}, - {id: "eurasia", name: "Eurasia"}, - {id: "europe", name: "Europe"}, - {id: "europe-accented", name: "Europe Accented"}, - {id: "europe-and-central-asia", name: "Europe and Central Asia"}, - {id: "europe-central", name: "Europe Central"}, - {id: "europe-north", name: "Europe North"}, - {id: "greenland", name: "Greenland"}, - {id: "hellenica", name: "Hellenica"}, - {id: "iceland", name: "Iceland"}, - {id: "indian-ocean", name: "Indian Ocean"}, - {id: "mediterranean-sea", name: "Mediterranean Sea"}, - {id: "middle-east", name: "Middle East"}, - {id: "north-america", name: "North America"}, - {id: "us-centric", name: "US-centric"}, - {id: "us-mainland", name: "US Mainland"}, - {id: "world", name: "World"}, - {id: "world-from-pacific", name: "World from Pacific"} -]; - const initialSeed = generateSeed(); appendStyleSheet(); insertEditorHtml(); @@ -62,19 +19,20 @@ export function open() { }, Select: function () { const id = getSelected(); - $templateInput.value = id; + applyOption($templateInput, id, getName(id)); lock("template"); + $(this).dialog("close"); }, "New Map": function () { const id = getSelected(); - $templateInput.value = id; + applyOption($templateInput, id, getName(id)); lock("template"); const seed = getSeed(); Math.random = aleaPRNG(seed); - regeneratePrompt({seed}); + $(this).dialog("close"); } } @@ -208,13 +166,14 @@ function insertEditorHtml() { byId("dialogs").insertAdjacentHTML("beforeend", heightmapSelectionHtml); const sections = document.getElementsByClassName("heightmap-selection_container"); - sections[0].innerHTML = templates - .map(({id, name}) => { + sections[0].innerHTML = Object.keys(heightmapTemplates) + .map(key => { + const name = heightmapTemplates[key].name; Math.random = aleaPRNG(initialSeed); - const heights = generateHeightmap(id); + const heights = generateHeightmap(key); const dataUrl = drawHeights(heights); - return /* html */ `
+ return /* html */ `
${name}
${name} @@ -224,11 +183,12 @@ function insertEditorHtml() { }) .join(""); - sections[1].innerHTML = heightmaps - .map(({id, name}) => { - drawPrecreatedHeightmap(id); + sections[1].innerHTML = Object.keys(precreatedHeightmaps) + .map(key => { + const name = precreatedHeightmaps[key].name; + drawPrecreatedHeightmap(key); - return /* html */ `
+ return /* html */ `
${name}
${name}
`; @@ -271,6 +231,11 @@ function getSeed() { return byId("heightmapSelection").querySelector(".selected")?.dataset?.seed; } +function getName(id) { + const isTemplate = id in heightmapTemplates; + return isTemplate ? heightmapTemplates[id].name : precreatedHeightmaps[id].name; +} + function drawHeights(heights) { const canvas = document.createElement("canvas"); canvas.width = grid.cellsX; @@ -325,7 +290,7 @@ function redrawAll() { const {id, seed} = article.dataset; Math.random = aleaPRNG(seed); - const isTemplate = id in HeightmapTemplates; + const isTemplate = id in heightmapTemplates; if (isTemplate) drawTemplatePreview(id); else drawPrecreatedHeightmap(id); } diff --git a/modules/heightmap-generator.js b/modules/heightmap-generator.js index 71f2ea2d..bb4c0100 100644 --- a/modules/heightmap-generator.js +++ b/modules/heightmap-generator.js @@ -8,7 +8,7 @@ window.HeightmapGenerator = (function () { const cleanup = () => (heights = null); const fromTemplate = template => { - const templateString = HeightmapTemplates[template]; + const templateString = heightmapTemplates[template]?.template || ""; const steps = templateString.split("\n"); if (!steps.length) throw new Error(`Heightmap template: no steps. Template: ${template}. Steps: ${steps}`); @@ -52,7 +52,7 @@ window.HeightmapGenerator = (function () { const id = byId("templateInput").value; resetHeights(); - const isTemplate = id in HeightmapTemplates; + const isTemplate = id in heightmapTemplates; grid.cells.h = isTemplate ? fromTemplate(id) : await fromPrecreated(id); cleanup(); diff --git a/modules/ui/general.js b/modules/ui/general.js index 1b63dbac..7cb0f5c3 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -3,7 +3,7 @@ // fit full-screen map if window is resized window.addEventListener("resize", function (e) { - if (localStorage.getItem("mapWidth") && localStorage.getItem("mapHeight")) return; + if (stored("mapWidth") && stored("mapHeight")) return; mapWidthInput.value = window.innerWidth; mapHeightInput.value = window.innerHeight; changeMapSize(); @@ -414,7 +414,7 @@ document.querySelectorAll("[data-locked]").forEach(function (e) { // lock option function lock(id) { const input = document.querySelector('[data-stored="' + id + '"]'); - if (input) localStorage.setItem(id, input.value); + if (input) store(id, input.value); const el = document.getElementById("lock_" + id); if (!el) return; el.dataset.locked = 1; @@ -436,9 +436,14 @@ function locked(id) { return lockEl.dataset.locked == 1; } -// check if option is stored in localStorage -function stored(option) { - return localStorage.getItem(option); +// return key value stored in localStorage or null +function stored(key) { + return localStorage.getItem(key) || null; +} + +// store key value in localStorage +function store(key, value) { + return localStorage.setItem(key, value); } // assign skeaker behaviour @@ -458,10 +463,10 @@ function speak(text) { } // apply drop-down menu option. If the value is not in options, add it -function applyOption(select, id, name = id) { - const custom = !Array.from(select.options).some(o => o.value == id); - if (custom) select.options.add(new Option(name, id)); - select.value = id; +function applyOption($select, value, name = value) { + const isExisting = Array.from($select.options).some(o => o.value === value); + if (!isExisting) $select.options.add(new Option(name, value)); + $select.value = value; } // show info about the generator in a popup diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index 69d98184..beb34ae5 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -921,7 +921,7 @@ function editHeightmap(options) { body.setAttribute("data-changed", 0); body.innerHTML = ""; - const templateString = HeightmapTemplates[template]; + const templateString = heightmapTemplates[template]?.template; if (!templateString) return; const steps = templateString.split("\n"); diff --git a/modules/ui/options.js b/modules/ui/options.js index 260cdea5..888571c7 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -6,14 +6,14 @@ $("#exitCustomization").draggable({handle: "div"}); $("#mapLayers").disableSelection(); // remove glow if tip is aknowledged -if (localStorage.getItem("disable_click_arrow_tooltip")) { +if (stored("disable_click_arrow_tooltip")) { clearMainTip(); optionsTrigger.classList.remove("glow"); } // Show options pane on trigger click function showOptions(event) { - if (!localStorage.getItem("disable_click_arrow_tooltip")) { + if (!stored("disable_click_arrow_tooltip")) { clearMainTip(); localStorage.setItem("disable_click_arrow_tooltip", true); optionsTrigger.classList.remove("glow"); @@ -81,12 +81,12 @@ async function showSupporters() { } // on any option or dialog change -document.getElementById("options").addEventListener("change", checkIfStored); -document.getElementById("dialogs").addEventListener("change", checkIfStored); +document.getElementById("options").addEventListener("change", storeValueIfRequired); +document.getElementById("dialogs").addEventListener("change", storeValueIfRequired); document.getElementById("options").addEventListener("input", updateOutputToFollowInput); document.getElementById("dialogs").addEventListener("input", updateOutputToFollowInput); -function checkIfStored(ev) { +function storeValueIfRequired(ev) { if (ev.target.dataset.stored) lock(ev.target.dataset.stored); } @@ -142,7 +142,7 @@ optionsContent.addEventListener("click", function (event) { else if (id === "optionsMapHistory") showSeedHistoryDialog(); else if (id === "optionsCopySeed") copyMapURL(); else if (id === "optionsEraRegenerate") regenerateEra(); - else if (id === "templateSelectButton") openTemplateSelectionDialog(); + else if (id === "templateInput") openTemplateSelectionDialog(); else if (id === "zoomExtentDefault") restoreDefaultZoomExtent(); else if (id === "translateExtent") toggleTranslateExtent(event.target); else if (id === "speakerTest") testSpeaker(); @@ -233,7 +233,7 @@ const voiceInterval = setInterval(function () { voices.forEach((voice, i) => { select.options.add(new Option(voice.name, i, false)); }); - if (stored("speakerVoice")) select.value = localStorage.getItem("speakerVoice"); + if (stored("speakerVoice")) select.value = stored("speakerVoice"); // se voice to store else select.value = voices.findIndex(voice => voice.lang === "en-US"); // or to first found English-US }, 1000); @@ -273,13 +273,14 @@ function showSeedHistoryDialog() { // generate map with historical seed function restoreSeed(id) { - if (mapHistory[id].seed == seed) return tip("The current map is already generated with this seed", null, "error"); + const {seed, width, height, template} = mapHistory[id]; + byId("optionsSeed").value = seed; + byId("mapWidthInput").value = width; + byId("mapHeightInput").value = height; + byId("templateInput").value = template; - optionsSeed.value = mapHistory[id].seed; - mapWidthInput.value = mapHistory[id].width; - mapHeightInput.value = mapHistory[id].height; - templateInput.value = mapHistory[id].template; if (locked("template")) unlock("template"); + regeneratePrompt(); } @@ -457,43 +458,50 @@ function changeZoomExtent(value) { zoom.scaleTo(svg, scale); } -// control stored options logic +// restore options stored in localStorage function applyStoredOptions() { - if (!localStorage.getItem("mapWidth") || !localStorage.getItem("mapHeight")) { + if (!stored("mapWidth") || !stored("mapHeight")) { mapWidthInput.value = window.innerWidth; mapHeightInput.value = window.innerHeight; } - if (localStorage.getItem("distanceUnit")) applyOption(distanceUnitInput, localStorage.getItem("distanceUnit")); - if (localStorage.getItem("heightUnit")) applyOption(heightUnit, localStorage.getItem("heightUnit")); - - for (let i = 0; i < localStorage.length; i++) { - const stored = localStorage.key(i); - const value = localStorage.getItem(stored); - - if (stored === "speakerVoice") continue; - const input = document.getElementById(stored + "Input") || document.getElementById(stored); - const output = document.getElementById(stored + "Output"); - if (input) input.value = value; - if (output) output.value = value; - lock(stored); - - // add saved style presets to options - if (stored.slice(0, 5) === "style") applyOption(stylePreset, stored, stored.slice(5)); + const heightmapId = stored("template"); + if (heightmapId) { + const name = heightmapTemplates[heightmapId]?.name || precreatedHeightmaps[heightmapId]?.name || heightmapId; + applyOption(byId("templateInput"), heightmapId, name); } - if (localStorage.getItem("winds")) + if (stored("distanceUnit")) applyOption(distanceUnitInput, stored("distanceUnit")); + if (stored("heightUnit")) applyOption(heightUnit, stored("heightUnit")); + + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + + if (key === "speakerVoice") continue; + const input = byId(key + "Input") || byId(key); + const output = byId(key + "Output"); + + const value = stored(key); + if (input) input.value = value; + if (output) output.value = value; + lock(key); + + // add saved style presets to options + if (key.slice(0, 5) === "style") applyOption(stylePreset, key, key.slice(5)); + } + + if (stored("winds")) options.winds = localStorage .getItem("winds") .split(",") .map(w => +w); - if (localStorage.getItem("military")) options.military = JSON.parse(localStorage.getItem("military")); + if (stored("military")) options.military = JSON.parse(stored("military")); - if (localStorage.getItem("tooltipSize")) changeTooltipSize(localStorage.getItem("tooltipSize")); - if (localStorage.getItem("regions")) changeStatesNumber(localStorage.getItem("regions")); + if (stored("tooltipSize")) changeTooltipSize(stored("tooltipSize")); + if (stored("regions")) changeStatesNumber(stored("regions")); uiSizeInput.max = uiSizeOutput.max = getUImaxSize(); - if (localStorage.getItem("uiSize")) changeUIsize(localStorage.getItem("uiSize")); + if (stored("uiSize")) changeUIsize(stored("uiSize")); else changeUIsize(minmax(rn(mapWidthInput.value / 1280, 1), 1, 2.5)); // search params overwrite stored and default options @@ -503,8 +511,8 @@ function applyStoredOptions() { if (width) mapWidthInput.value = width; if (height) mapHeightInput.value = height; - const transparency = localStorage.getItem("transparency") || 5; - const themeColor = localStorage.getItem("themeColor"); + const transparency = stored("transparency") || 5; + const themeColor = stored("themeColor"); changeDialogsTheme(themeColor, transparency); setRendering(shapeRendering.value); @@ -549,22 +557,13 @@ function randomizeOptions() { // select heightmap template pseudo-randomly function randomizeHeightmapTemplate() { - const templates = { - volcano: 3, - highIsland: 19, - lowIsland: 9, - continents: 16, - archipelago: 18, - mediterranean: 5, - peninsula: 3, - pangea: 5, - isthmus: 2, - atoll: 1, - shattered: 7, - taklamakan: 1, - oldWorld: 11 - }; - document.getElementById("templateInput").value = rw(templates); + const templates = {}; + for (const key in heightmapTemplates) { + templates[key] = heightmapTemplates[key].probability || 0; + } + const template = rw(templates); + const name = heightmapTemplates[template].name; + applyOption(byId("templateInput"), template, name); } // select culture set pseudo-randomly