diff --git a/index.html b/index.html index 64023f4f..0a24a113 100644 --- a/index.html +++ b/index.html @@ -3413,6 +3413,8 @@
Altitude:
m + +
diff --git a/modules/dynamic/editors/states-editor.js b/modules/dynamic/editors/states-editor.js index 69d2618b..1636f0da 100644 --- a/modules/dynamic/editors/states-editor.js +++ b/modules/dynamic/editors/states-editor.js @@ -572,8 +572,11 @@ function stateChangeExpansionism(state, line, value) { function toggleFog(state, cl) { if (customization) return; + // Skip virtual/sky states that have no geometry + if (pack.states[state]?.skyRealm) return; const path = statesBody.select("#state" + state).attr("d"), id = "focusState" + state; + if (!path) return; // nothing to fog/unfog cl.contains("inactive") ? fog(id, path) : unfog(id); cl.toggle("inactive"); } diff --git a/modules/routes-generator.js b/modules/routes-generator.js index a47d044f..5ca969ac 100644 --- a/modules/routes-generator.js +++ b/modules/routes-generator.js @@ -74,8 +74,8 @@ window.Routes = (function () { }; for (const burg of burgs) { - if (burg.i && !burg.removed && !burg.flying && !burg.skyPort) { - // Exclude flying / sky port burgs from land road/trail graphs + if (burg.i && !burg.removed && !burg.flying) { + // Exclude only flying burgs from land road/trail graphs const {feature, capital, port} = burg; addBurg(burgsByFeature, feature, burg); diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js index 52a01535..ecaf3ad1 100644 --- a/modules/ui/burg-editor.js +++ b/modules/ui/burg-editor.js @@ -81,8 +81,10 @@ function editBurg(id) { const altitudeRow = byId("burgAltitudeRow"); if (b.flying) { altitudeRow.style.display = "flex"; - byId("burgAltitude").value = b.altitude ?? 1000; - byId("burgElevation").innerHTML = `${b.altitude ?? 1000} m (sky altitude)`; + const defAlt = getDefaultSkyAltitude(); + const altitude = b.altitude ?? defAlt; + byId("burgAltitude").value = altitude; + byId("burgElevation").innerHTML = `${altitude} m (sky altitude)`; } else { altitudeRow.style.display = "none"; byId("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]); @@ -318,20 +320,8 @@ function editBurg(id) { const turnOn = this.classList.contains("inactive"); if (feature === "port") togglePort(id); else if (feature === "skyPort") { + // Sky port should not change land restrictions or state burg.skyPort = +turnOn; - // Assign to Sky State when turning on (if not a state capital) - if (turnOn) { - try { - if (!burg.capital) { - const skyId = ensureSkyState(id); - if (burg.state !== skyId) { - // Reassign cell ownership - pack.cells.state[burg.cell] = skyId; - burg.state = skyId; - } - } - } catch (e) { ERROR && console.error(e); } - } // Regenerate routes to reflect air network regenerateRoutes(); if (layerIsOn("toggleBurgIcons")) drawBurgIcons(); @@ -342,11 +332,15 @@ function editBurg(id) { try { const skyId = ensureSkyState(id); if (burg.state !== skyId) { - pack.cells.state[burg.cell] = skyId; burg.state = skyId; } - if (burg.altitude == null) burg.altitude = 1000; + if (burg.altitude == null) burg.altitude = getDefaultSkyAltitude(); } catch (e) { ERROR && console.error(e); } + } else { + // Turning flying off: restore ground state ownership for the burg + // Do not change cells.state here; use the underlying cell state + const groundState = pack.cells.state[burg.cell] || 0; + burg.state = groundState; } regenerateRoutes(); if (layerIsOn("toggleBurgIcons")) drawBurgIcons(); @@ -532,9 +526,10 @@ function editBurg(id) { cells.burg[burg.cell] = 0; cells.burg[cell] = id; burg.cell = cell; - + if (isWater) { + // Set target state based on terrain and sky features - if (isWater || burg.flying) { + if (isWater) { const skyId = ensureSkyState(id); cells.state[cell] = skyId; burg.state = skyId; @@ -555,6 +550,12 @@ function editBurg(id) { if (burg.flying) byId("burgElevation").innerHTML = `${burg.altitude} m (sky altitude)`; } + function getDefaultSkyAltitude() { + const el = byId("burgDefaultSkyAltitude"); + const v = el ? +el.value : NaN; + return Number.isFinite(v) && v >= 0 ? v : 1000; + } + function editBurgLegend() { const id = elSelected.attr("data-id"); const name = elSelected.text(); diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js index c33ef695..095d8271 100644 --- a/modules/ui/burgs-overview.js +++ b/modules/ui/burgs-overview.js @@ -293,7 +293,13 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) { const burg = pack.burgs[id]; burg.flying = 1; burg.skyPort = 1; - if (burg.altitude == null) burg.altitude = 1000; + try { + const defAltEl = byId("burgDefaultSkyAltitude"); + const defAlt = defAltEl ? +defAltEl.value : NaN; + burg.altitude = burg.altitude ?? (Number.isFinite(defAlt) && defAlt >= 0 ? defAlt : 1000); + } catch (e) { + burg.altitude = burg.altitude ?? 1000; + } const skyStateId = ensureSkyState(id); if (burg.state !== skyStateId) burg.state = skyStateId; // Keep as non-sea port diff --git a/modules/ui/diplomacy-editor.js b/modules/ui/diplomacy-editor.js index 0f363b7b..b77860a8 100644 --- a/modules/ui/diplomacy-editor.js +++ b/modules/ui/diplomacy-editor.js @@ -151,6 +151,7 @@ function editDiplomacy() { const state = +event.target.dataset.id; if (customization || !state) return; const d = regions.select("#state" + state).attr("d"); + if (!d) return; // no geometry to highlight (e.g., Sky Realm) const path = debug .append("path") diff --git a/modules/ui/editors.js b/modules/ui/editors.js index b0188edc..87b64521 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -259,10 +259,22 @@ function ensureSkyState(anchorBurgId) { lock: 1, skyRealm: 1 }; + // initialize minimal diplomacy for the new Sky State + try { + const diplomacy = states.map(s => (s && s.i && !s.removed ? "Neutral" : "x")); + diplomacy[i] = "x"; + newState.diplomacy = diplomacy; + // extend existing states' diplomacy; skip state 0 (chronicle holder) + for (const s of states) { + if (s && s.i && !s.removed && Array.isArray(s.diplomacy)) s.diplomacy.push("Neutral"); + } + } catch (err) { + // fail-safe: do nothing if diplomacy cannot be set up + } + states.push(newState); - // Assign the burg and its cell to the Sky State - if (cells && typeof b.cell === "number") cells.state[b.cell] = i; + // Do not assign the cell itself to Sky state; keep underlying ground state mapping if (b) { b.state = i; b.capital = 1; @@ -361,8 +373,8 @@ function togglePort(burg) { function getBurgLink(burg) { if (burg.link) return burg.link; - // Sky burgs: force MFCG with sky-friendly parameters - if (burg.flying || burg.skyPort) return createMfcgLink(burg, true); + // Sky-only: use sky generator options only for truly flying burgs + if (burg.flying) return createMfcgLink(burg, true); const population = burg.population * populationRate * urbanization; if (population >= options.villageMaxPopulation || burg.citadel || burg.walls || burg.temple || burg.shanty) diff --git a/modules/ui/military-overview.js b/modules/ui/military-overview.js index 46a86138..1b229fe1 100644 --- a/modules/ui/military-overview.js +++ b/modules/ui/military-overview.js @@ -176,6 +176,7 @@ function overviewMilitary() { if (!layerIsOn("toggleStates")) return; const d = regions.select("#state" + state).attr("d"); + if (!d) return; // no geometry to highlight (e.g., Sky Realm) const path = debug .append("path") diff --git a/versioning.js b/versioning.js index 05152dd6..b618c103 100644 --- a/versioning.js +++ b/versioning.js @@ -22,7 +22,7 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o if (loadingScreenVersion) loadingScreenVersion.innerText = `v${VERSION}`; const storedVersion = localStorage.getItem("version"); - if (compareVersions(storedVersion, VERSION, {major: true, minor: true, patch: false}).isOlder) { + if (compareVersions(storedVersion, VERSION, { major: true, minor: true, patch: false }).isOlder) { setTimeout(showUpdateWindow, 6000); } @@ -45,14 +45,14 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o
  • Styling: separate #burgIcons > #skyburgs group for sky burg icons
  • -

    Join our Discord server and Reddit community to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.

    +

    Join our Discord server and Reddit community to ask questions, share maps, discuss the Generator and Worldbuilding, report bugs and propose new features.

    Thanks for all supporters on Patreon!`; $("#alert").dialog({ resizable: false, title: "Fantasy Map Generator update", width: "28em", - position: {my: "center center-4em", at: "center", of: "svg"}, + position: { my: "center center-4em", at: "center", of: "svg" }, buttons: { "Clear cache": () => cleanupData(), "Don't show again": function () { @@ -100,8 +100,8 @@ function isValidVersion(versionString) { return !isNaN(major) && !isNaN(minor) && !isNaN(patch); } -function compareVersions(version1, version2, options = {major: true, minor: true, patch: true}) { - if (!isValidVersion(version1) || !isValidVersion(version2)) return {isEqual: false, isNewer: false, isOlder: false}; +function compareVersions(version1, version2, options = { major: true, minor: true, patch: true }) { + if (!isValidVersion(version1) || !isValidVersion(version2)) return { isEqual: false, isNewer: false, isOlder: false }; let [major1, minor1, patch1] = version1.split(".").map(Number); let [major2, minor2, patch2] = version2.split(".").map(Number); @@ -114,5 +114,5 @@ function compareVersions(version1, version2, options = {major: true, minor: true const isNewer = major1 > major2 || (major1 === major2 && (minor1 > minor2 || (minor1 === minor2 && patch1 > patch2))); const isOlder = major1 < major2 || (major1 === major2 && (minor1 < minor2 || (minor1 === minor2 && patch1 < patch2))); - return {isEqual, isNewer, isOlder}; + return { isEqual, isNewer, isOlder }; }