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(); addListeners(); export function open() { closeDialogs(".stable"); const $templateInput = byId("templateInput"); setSelected($templateInput.value); $("#heightmapSelection").dialog({ title: "Select Heightmap", resizable: false, position: {my: "center", at: "center", of: "svg"}, buttons: { Cancel: function () { $(this).dialog("close"); }, Select: function () { const id = getSelected(); $templateInput.value = id; lock("template"); $(this).dialog("close"); }, "New Map": function () { const id = getSelected(); $templateInput.value = id; lock("template"); const seed = getSeed(); Math.random = aleaPRNG(seed); regeneratePrompt({seed}); $(this).dialog("close"); } } }); } function appendStyleSheet() { const styles = /* css */ ` div.dialog > div.heightmap-selection { width: 70vw; height: 70vh; } .heightmap-selection_container { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 6px; } @media (max-width: 600px) { .heightmap-selection_container { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); grid-gap: 4px; } } @media (min-width: 2000px) { .heightmap-selection_container { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); grid-gap: 8px; } } .heightmap-selection_options { display: grid; grid-template-columns: 2fr 1fr; justify-items: start; } .heightmap-selection_options > div:first-child { display: grid; grid-template-columns: 1fr 1fr; align-items: center; } .heightmap-selection article { padding: 4px; border-radius: 8px; transition: all 0.1s ease-in-out; filter: drop-shadow(1px 1px 4px #999); } .heightmap-selection article:hover { background-color: #ddd; filter: drop-shadow(1px 1px 8px #999); cursor: pointer; } .heightmap-selection article.selected { background-color: #ccc; outline: 1px solid var(--dark-solid); filter: drop-shadow(1px 1px 8px #999); } .heightmap-selection article > div { display: flex; justify-content: space-between; padding: 2px 1px; } .heightmap-selection article > div > span.icon-cw:hover { color: #000; } .heightmap-selection article > div > span.icon-cw:active { color: #666; } .heightmap-selection article > img { width: 100%; aspect-ratio: ${graphWidth}/${graphHeight}; border-radius: 8px; object-fit: fill; } `; const style = document.createElement("style"); style.appendChild(document.createTextNode(styles)); document.head.appendChild(style); } function insertEditorHtml() { const heightmapSelectionHtml = /* html */ `

Heightmap templates

Pre-created heightmaps

Options

Color scheme
`; byId("dialogs").insertAdjacentHTML("beforeend", heightmapSelectionHtml); const sections = document.getElementsByClassName("heightmap-selection_container"); sections[0].innerHTML = templates .map(({id, name}) => { Math.random = aleaPRNG(initialSeed); const heights = generateHeightmap(id); const dataUrl = drawHeights(heights); return /* html */ `
${name}
${name}
`; }) .join(""); sections[1].innerHTML = heightmaps .map(({id, name}) => { drawPrecreatedHeightmap(id); return /* html */ `
${name}
${name}
`; }) .join(""); } function addListeners() { byId("heightmapSelection").on("click", event => { const article = event.target.closest("#heightmapSelection article"); if (!article) return; const id = article.dataset.id; if (event.target.matches("span.icon-cw")) { const seed = generateSeed(); article.dataset.seed = seed; Math.random = aleaPRNG(seed); drawTemplatePreview(id); } else setSelected(id); }); byId("heightmapSelectionRenderOcean").on("change", redrawAll); byId("heightmapSelectionColorScheme").on("change", redrawAll); byId("heightmapSelectionRedrawPreview").on("click", redrawAll); byId("heightmapSelectionEditTemplates").on("click", confirmHeightmapEdit); byId("heightmapSelectionImportHeightmap").on("click", confirmHeightmapEdit); } function getSelected() { return byId("heightmapSelection").querySelector(".selected")?.dataset?.id; } function setSelected(id) { const $heightmapSelection = byId("heightmapSelection"); $heightmapSelection.querySelector(".selected")?.classList?.remove("selected"); $heightmapSelection.querySelector(`[data-id="${id}"]`)?.classList?.add("selected"); } function getSeed() { return byId("heightmapSelection").querySelector(".selected")?.dataset?.seed; } function drawHeights(heights) { const canvas = document.createElement("canvas"); canvas.width = grid.cellsX; canvas.height = grid.cellsY; const ctx = canvas.getContext("2d"); const imageData = ctx.createImageData(grid.cellsX, grid.cellsY); const schemeId = byId("heightmapSelectionColorScheme").value; const scheme = getColorScheme(schemeId); const renderOcean = byId("heightmapSelectionRenderOcean").checked; const getHeight = height => (height < 20 ? (renderOcean ? height : 0) : height); for (let i = 0; i < heights.length; i++) { const color = scheme(1 - getHeight(heights[i]) / 100); const {r, g, b} = d3.color(color); const n = i * 4; imageData.data[n] = r; imageData.data[n + 1] = g; imageData.data[n + 2] = b; imageData.data[n + 3] = 255; } ctx.putImageData(imageData, 0, 0); return canvas.toDataURL("image/png"); } function generateHeightmap(id) { HeightmapGenerator.resetHeights(); const heights = HeightmapGenerator.fromTemplate(id); HeightmapGenerator.cleanup(); return heights; } function drawTemplatePreview(id) { const heights = generateHeightmap(id); const dataUrl = drawHeights(heights); const article = byId("heightmapSelection").querySelector(`[data-id="${id}"]`); article.querySelector("img").src = dataUrl; } async function drawPrecreatedHeightmap(id) { const heights = await HeightmapGenerator.fromPrecreated(id); const dataUrl = drawHeights(heights); const article = byId("heightmapSelection").querySelector(`[data-id="${id}"]`); article.querySelector("img").src = dataUrl; } function redrawAll() { const articles = byId("heightmapSelection").querySelectorAll(`article`); for (const article of articles) { const {id, seed} = article.dataset; Math.random = aleaPRNG(seed); const isTemplate = id in HeightmapTemplates; if (isTemplate) drawTemplatePreview(id); else drawPrecreatedHeightmap(id); } } function confirmHeightmapEdit() { const tool = this.dataset.tool; confirmationDialog({ title: this.dataset.tip, message: "Opening the tool will erase the current map. Are you sure you want to proceed?", confirm: "Continue", onConfirm: () => editHeightmap({mode: "erase", tool}) }); }