From 4e884b10ee2a96f0166f20257f01408a66772fa9 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 26 Feb 2024 11:35:17 +0100 Subject: [PATCH 01/18] fix: check if heightmapColorSchemes is defined --- modules/io/load.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/io/load.js b/modules/io/load.js index 319c1ffb..034d977a 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -460,8 +460,8 @@ async function parseLoadedData(data, mapVersion) { resolveVersionConflicts(versionNumber); } - { - // add custom heightmap color scheme if any + // add custom heightmap color scheme if any + if (heightmapColorSchemes) { const oceanScheme = terrs.select("#oceanHeights").attr("scheme"); const landScheme = terrs.select("#landHeights").attr("scheme"); if (!(oceanScheme in heightmapColorSchemes)) addCustomColorScheme(oceanScheme); From 14ac83e6f5b867acf7b43dc690244b4cdaadd4fa Mon Sep 17 00:00:00 2001 From: Azgaar Date: Wed, 28 Feb 2024 00:16:07 +0100 Subject: [PATCH 02/18] fix: uploadCulturesData --- index.html | 2 +- modules/dynamic/editors/cultures-editor.js | 43 +++++++++++++++------- modules/io/formats.js | 24 ------------ modules/ui/editors.js | 2 +- versioning.js | 2 +- 5 files changed, 32 insertions(+), 41 deletions(-) delete mode 100644 modules/io/formats.js diff --git a/index.html b/index.html index fe622361..c71a7708 100644 --- a/index.html +++ b/index.html @@ -8063,7 +8063,7 @@ - + diff --git a/modules/dynamic/editors/cultures-editor.js b/modules/dynamic/editors/cultures-editor.js index fac79955..b7218372 100644 --- a/modules/dynamic/editors/cultures-editor.js +++ b/modules/dynamic/editors/cultures-editor.js @@ -857,8 +857,19 @@ function closeCulturesEditor() { } async function uploadCulturesData() { - const csv = await Formats.csvParser(this.files[0]); + const file = this.files[0]; this.value = ""; + const csv = await file.text(); + const data = d3.csvParse(csv, d => ({ + i: +d.Id, + name: d.Name, + color: d.Color, + expansionism: +d.Expansionism, + type: d.Type, + population: +d.Population, + emblemsShape: d["Emblems Shape"], + origins: d.Origins + })); const {cultures, cells} = pack; const shapes = Object.keys(COA.shields.types) @@ -870,20 +881,26 @@ async function uploadCulturesData() { if (item.i) item.removed = true; }); - for (const c of csv.iterator((a, b) => +a[0] > +b[0])) { + for (const culture of data) { let current; - if (+c.id < cultures.length) { - current = cultures[c.id]; + if (culture.i < cultures.length) { + current = cultures[culture.i]; const ratio = current.urban / (current.rural + current.urban); - applyPopulationChange(current.rural, current.urban, c.population * (1 - ratio), c.population * ratio, +c.id); + applyPopulationChange( + current.rural, + current.urban, + culture.population * (1 - ratio), + culture.population * ratio, + culture.i + ); } else { current = {i: cultures.length, center: ra(populated), area: 0, cells: 0, origin: 0, rural: 0, urban: 0}; cultures.push(current); } current.removed = false; - current.name = c.name; + current.name = culture.name; if (current.i) { current.code = abbreviate( @@ -891,10 +908,10 @@ async function uploadCulturesData() { cultures.map(c => c.code) ); - current.color = c.color; - current.expansionism = +c.expansionism; + current.color = culture.color; + current.expansionism = +culture.expansionism; - if (cultureTypes.includes(c.type)) current.type = c.type; + if (cultureTypes.includes(culture.type)) current.type = culture.type; else current.type = "Generic"; } @@ -913,13 +930,11 @@ async function uploadCulturesData() { current.origins = originIds.filter(id => id !== null); if (!current.origins.length) current.origins = [0]; } - c.origins = current.i ? restoreOrigins(c.origins) : [null]; - const shieldShape = c["emblems shape"].toLowerCase(); - if (shapes.includes(shieldShape)) current.shield = shieldShape; - else current.shield = "heater"; + culture.origins = current.i ? restoreOrigins(culture.origins || "") : [null]; + current.shield = shapes.includes(culture.emblemsShape) ? culture.emblemsShape : "heater"; - const nameBaseIndex = nameBases.findIndex(n => n.name == c.namesbase); + const nameBaseIndex = nameBases.findIndex(n => n.name == culture.namesbase); current.base = nameBaseIndex === -1 ? 0 : nameBaseIndex; } diff --git a/modules/io/formats.js b/modules/io/formats.js deleted file mode 100644 index 439e124e..00000000 --- a/modules/io/formats.js +++ /dev/null @@ -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}; -})(); diff --git a/modules/ui/editors.js b/modules/ui/editors.js index 368229ac..a7c0c9cb 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -1182,7 +1182,7 @@ async function editStates() { async function editCultures() { if (customization) return; - const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.96.00"); + const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.96.01"); Editor.open(); } diff --git a/versioning.js b/versioning.js index b2c673e1..e2cd1b6e 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.00"; // generator version, update each time +const version = "1.96.01"; // generator version, update each time { document.title += " v" + version; From 09f835c21043852af28e48a68cc7e993c951210e Mon Sep 17 00:00:00 2001 From: Azgaar Date: Fri, 1 Mar 2024 11:46:07 +0100 Subject: [PATCH 03/18] fix: elevation profile color scheme --- modules/io/export.js | 2 +- modules/ui/elevation-profile.js | 41 +++++++++++++++++++++++++++++---- utils/graphUtils.js | 7 +++--- versioning.js | 2 +- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/modules/io/export.js b/modules/io/export.js index 527705e5..27ecf202 100644 --- a/modules/io/export.js +++ b/modules/io/export.js @@ -364,7 +364,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") diff --git a/modules/ui/elevation-profile.js b/modules/ui/elevation-profile.js index 20e381a6..7c180d37 100644 --- a/modules/ui/elevation-profile.js +++ b/modules/ui/elevation-profile.js @@ -193,8 +193,15 @@ function showElevationProfile(data, routeLen, isRiver) { .attr("d", "M0,0 V4 L2,2 Z") .attr("fill", "darkgray"); - let colors = getColorScheme(terrs.attr("scheme")); - const landdef = chart.select("defs").append("linearGradient").attr("id", "landdef").attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%"); + const colors = getColorScheme("natural"); + const landdef = chart + .select("defs") + .append("linearGradient") + .attr("id", "landdef") + .attr("x1", "0%") + .attr("y1", "0%") + .attr("x2", "0%") + .attr("y2", "100%"); if (chartData.mah == chartData.mih) { landdef @@ -247,7 +254,14 @@ function showElevationProfile(data, routeLen, isRiver) { path += " L" + parseInt(xscale(extra.length) + +xOffset) + "," + parseInt(yscale(0) + +yOffset); path += " L" + parseInt(xscale(0) + +xOffset) + "," + parseInt(yscale(0) + +yOffset); path += "Z"; - chart.append("g").attr("id", "epland").append("path").attr("d", path).attr("stroke", "purple").attr("stroke-width", "0").attr("fill", "url(#landdef)"); + chart + .append("g") + .attr("id", "epland") + .append("path") + .attr("d", path) + .attr("stroke", "purple") + .attr("stroke-width", "0") + .attr("fill", "url(#landdef)"); // biome / heights let g = chart.append("g").attr("id", "epbiomes"); @@ -289,7 +303,14 @@ function showElevationProfile(data, routeLen, isRiver) { chartData.cell[k] + ")"; - g.append("rect").attr("stroke", c).attr("fill", c).attr("x", x).attr("y", y).attr("width", xscale(1)).attr("height", 15).attr("data-tip", dataTip); + g.append("rect") + .attr("stroke", c) + .attr("fill", c) + .attr("x", x) + .attr("y", y) + .attr("width", xscale(1)) + .attr("height", 15) + .attr("data-tip", dataTip); } const xAxis = d3 @@ -371,7 +392,17 @@ function showElevationProfile(data, routeLen, isRiver) { // arrow from burg name to graph line g.append("path") .attr("id", "eparrow" + b) - .attr("d", "M" + x1.toString() + "," + (y1 + 3).toString() + "L" + x1.toString() + "," + parseInt(chartData.points[k][1] - 3).toString()) + .attr( + "d", + "M" + + x1.toString() + + "," + + (y1 + 3).toString() + + "L" + + x1.toString() + + "," + + parseInt(chartData.points[k][1] - 3).toString() + ) .attr("stroke", "darkgray") .attr("fill", "lightgray") .attr("stroke-width", "1") diff --git a/utils/graphUtils.js b/utils/graphUtils.js index 99d3b52b..ec6fb59a 100644 --- a/utils/graphUtils.js +++ b/utils/graphUtils.js @@ -327,9 +327,10 @@ function drawCellsValue(data) { // helper function non-used for the main generation function drawPolygons(data) { - const max = d3.max(data), - min = d3.min(data), - scheme = getColorScheme(terrs.select("#landHeights").attr("scheme")); + const max = d3.max(data); + const min = d3.min(data); + const scheme = getColorScheme(terrs.select("#landHeights").attr("scheme")); + data = data.map(d => 1 - normalize(d, min, max)); debug.selectAll("polygon").remove(); diff --git a/versioning.js b/versioning.js index e2cd1b6e..bdaaca35 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.01"; // generator version, update each time +const version = "1.96.02"; // generator version, update each time { document.title += " v" + version; From ec5e44b94a86a6a713416642d97805253d2743fc Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 4 Mar 2024 19:04:27 +0100 Subject: [PATCH 04/18] fix: #1051 --- index.html | 2 +- modules/ui/tools.js | 17 ++++++++++------- versioning.js | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index c71a7708..5465e6c4 100644 --- a/index.html +++ b/index.html @@ -8064,7 +8064,7 @@ - + diff --git a/modules/ui/tools.js b/modules/ui/tools.js index 5d9d0d40..4d62305c 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -247,13 +247,16 @@ function recreateStates() { capitalsTree.add([x, y]); // update label id reference - labels - .select("#states") - .select(`#stateLabel${state.i}`) - .attr("id", `stateLabel${newId}`) - .select("textPath") - .attr("xlink:href", `#textPath_stateLabel${newId}`); - defs.select("#textPaths").select(`#textPath_stateLabel${state.i}`).attr("id", `textPath_stateLabel${newId}`); + byId(`textPath_stateLabel${state.i}`)?.setAttribute("id", `textPath_stateLabel${newId}`); + const $label = byId(`stateLabel${state.i}`); + if ($label) { + $label.setAttribute("id", `stateLabel${newId}`); + const $textPath = $label.querySelector("textPath"); + if ($textPath) { + $textPath.removeAttribute("href"); + $textPath.setAttribute("href", `#textPath_stateLabel${newId}`); + } + } // update emblem id reference byId(`stateCOA${state.i}`)?.setAttribute("id", `stateCOA${newId}`); diff --git a/versioning.js b/versioning.js index bdaaca35..2bcedad9 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.02"; // generator version, update each time +const version = "1.96.03"; // generator version, update each time { document.title += " v" + version; From 5aa1d65a1bfd9b903672e57229d6a8d10a01b07d Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 4 Mar 2024 19:23:33 +0100 Subject: [PATCH 05/18] chore: remove link to formats.js --- index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/index.html b/index.html index 5465e6c4..194e560b 100644 --- a/index.html +++ b/index.html @@ -8104,7 +8104,6 @@ - From bf817ee8ed03111ce3577355ce67b576303c0ff5 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 4 Mar 2024 19:51:49 +0100 Subject: [PATCH 06/18] fix: #1052 --- index.html | 2 +- modules/renderers/state-labels.js | 25 +++++++++++++++++++------ versioning.js | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 194e560b..16e69f49 100644 --- a/index.html +++ b/index.html @@ -8041,7 +8041,7 @@ - + diff --git a/modules/renderers/state-labels.js b/modules/renderers/state-labels.js index 0c82a4cd..b0cf3001 100644 --- a/modules/renderers/state-labels.js +++ b/modules/renderers/state-labels.js @@ -4,6 +4,10 @@ function drawStateLabels(list) { console.time("drawStateLabels"); + // temporary make the labels visible + const layerDisplay = labels.style("display"); + labels.style("display", null); + const {cells, states, features} = pack; const stateIds = cells.state; @@ -17,7 +21,11 @@ function drawStateLabels(list) { const MAX_ITERATIONS = 100; const labelPaths = getLabelPaths(); - drawLabelPath(); + const letterLength = checkExampleLetterLength(); + drawLabelPath(letterLength); + + // restore labels visibility + labels.style("display", layerDisplay); function getLabelPaths() { const labelPaths = []; @@ -110,17 +118,22 @@ function drawStateLabels(list) { } } - function drawLabelPath() { + function checkExampleLetterLength() { + const textGroup = d3.select("g#labels > g#states"); + const testLabel = textGroup.append("text").attr("x", 0).attr("y", 0).text("Example"); + const letterLength = testLabel.node().getComputedTextLength() / 7; // approximate length of 1 letter + testLabel.remove(); + + return letterLength; + } + + function drawLabelPath(letterLength) { const mode = options.stateLabelsMode || "auto"; const lineGen = d3.line().curve(d3.curveBundle.beta(1)); const textGroup = d3.select("g#labels > g#states"); const pathGroup = d3.select("defs > g#deftemp > g#textPaths"); - const testLabel = textGroup.append("text").attr("x", 0).attr("y", 0).text("Example"); - const letterLength = testLabel.node().getComputedTextLength() / 7; // approximate length of 1 letter - testLabel.remove(); - for (const [stateId, pathPoints] of labelPaths) { const state = states[stateId]; if (!state.i || state.removed) throw new Error("State must not be neutral or removed"); diff --git a/versioning.js b/versioning.js index 2bcedad9..2ea16dfe 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.03"; // generator version, update each time +const version = "1.96.04"; // generator version, update each time { document.title += " v" + version; From 721cec4e58815074b9bb1e15245f60f1ed54c1b6 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 4 Mar 2024 20:17:53 +0100 Subject: [PATCH 07/18] feat: manually added culture inherits parent culture in Tree (1.96.05) --- index.html | 2 +- modules/cultures-generator.js | 5 +++-- modules/dynamic/editors/cultures-editor.js | 1 + versioning.js | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 16e69f49..cf33e512 100644 --- a/index.html +++ b/index.html @@ -8040,7 +8040,7 @@ - + diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js index 0bb72d6d..7f89b76f 100644 --- a/modules/cultures-generator.js +++ b/modules/cultures-generator.js @@ -190,12 +190,13 @@ window.Cultures = (function () { name = Names.getCulture(culture, 5, 8, ""); base = pack.cultures[culture].base; } + const code = abbreviate( name, pack.cultures.map(c => c.code) ); const i = pack.cultures.length; - const color = d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex(); + const color = getRandomColor(); // define emblem shape let shield = culture.shield; @@ -214,7 +215,7 @@ window.Cultures = (function () { area: 0, rural: 0, urban: 0, - origins: [0], + origins: [pack.cells.culture[center]], code, shield }); diff --git a/modules/dynamic/editors/cultures-editor.js b/modules/dynamic/editors/cultures-editor.js index b7218372..7f886edb 100644 --- a/modules/dynamic/editors/cultures-editor.js +++ b/modules/dynamic/editors/cultures-editor.js @@ -822,6 +822,7 @@ function addCulture() { if (pack.cells.h[center] < 20) return tip("You cannot place culture center into the water. Please click on a land cell", false, "error"); + const occupied = pack.cultures.some(c => !c.removed && c.center === center); if (occupied) return tip("This cell is already a culture center. Please select a different cell", false, "error"); diff --git a/versioning.js b/versioning.js index 2ea16dfe..393373ca 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.04"; // generator version, update each time +const version = "1.96.05"; // generator version, update each time { document.title += " v" + version; From 73b39d217d6c30b9d2c681144c2fcc50d0e5ef05 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 4 Mar 2024 21:04:25 +0100 Subject: [PATCH 08/18] fix: #1048 --- index.html | 4 +- modules/dynamic/editors/states-editor.js | 47 +++++++------ modules/io/load.js | 90 ++++++++++++++++++------ modules/ui/editors.js | 35 +++++---- versioning.js | 2 +- 5 files changed, 114 insertions(+), 64 deletions(-) diff --git a/index.html b/index.html index cf33e512..89c77396 100644 --- a/index.html +++ b/index.html @@ -8063,7 +8063,7 @@ - + @@ -8101,7 +8101,7 @@ - + diff --git a/modules/dynamic/editors/states-editor.js b/modules/dynamic/editors/states-editor.js index 2adcbbc8..70a45017 100644 --- a/modules/dynamic/editors/states-editor.js +++ b/modules/dynamic/editors/states-editor.js @@ -624,28 +624,36 @@ function stateRemovePrompt(state) { }); } -function stateRemove(state) { - statesBody.select("#state" + state).remove(); - statesBody.select("#state-gap" + state).remove(); - statesHalo.select("#state-border" + state).remove(); - labels.select("#stateLabel" + state).remove(); - defs.select("#textPath_stateLabel" + state).remove(); +function stateRemove(stateId) { + statesBody.select("#state" + stateId).remove(); + statesBody.select("#state-gap" + stateId).remove(); + statesHalo.select("#state-border" + stateId).remove(); + labels.select("#stateLabel" + stateId).remove(); + defs.select("#textPath_stateLabel" + stateId).remove(); - unfog("focusState" + state); - pack.burgs.forEach(b => { - if (b.state === state) b.state = 0; + unfog("focusState" + stateId); + + pack.burgs.forEach(burg => { + if (burg.state === stateId) { + burg.state = 0; + if (burg.capital) { + burg.capital = 0; + moveBurgToGroup(burg.i, "towns"); + } + } }); + pack.cells.state.forEach((s, i) => { - if (s === state) pack.cells.state[i] = 0; + if (s === stateId) pack.cells.state[i] = 0; }); // remove emblem - const coaId = "stateCOA" + state; + const coaId = "stateCOA" + stateId; byId(coaId).remove(); - emblems.select(`#stateEmblems > use[data-i='${state}']`).remove(); + emblems.select(`#stateEmblems > use[data-i='${stateId}']`).remove(); // remove provinces - pack.states[state].provinces.forEach(p => { + pack.states[stateId].provinces.forEach(p => { pack.provinces[p] = {i: p, removed: true}; pack.cells.province.forEach((pr, i) => { if (pr === p) pack.cells.province[i] = 0; @@ -660,19 +668,14 @@ function stateRemove(state) { }); // remove military - pack.states[state].military.forEach(m => { - const id = `regiment${state}-${m.i}`; + pack.states[stateId].military.forEach(m => { + const id = `regiment${stateId}-${m.i}`; const index = notes.findIndex(n => n.id === id); if (index != -1) notes.splice(index, 1); }); - armies.select("g#army" + state).remove(); + armies.select("g#army" + stateId).remove(); - const capital = pack.states[state].capital; - pack.burgs[capital].capital = 0; - pack.burgs[capital].state = 0; - moveBurgToGroup(capital, "towns"); - - pack.states[state] = {i: state, removed: true}; + pack.states[stateId] = {i: stateId, removed: true}; debug.selectAll(".highlight").remove(); if (!layerIsOn("toggleStates")) toggleStates(); diff --git a/modules/io/load.js b/modules/io/load.js index 034d977a..3f6abf40 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -478,7 +478,7 @@ async function parseLoadedData(data, mapVersion) { 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); } @@ -486,7 +486,7 @@ async function parseLoadedData(data, mapVersion) { 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( @@ -495,14 +495,14 @@ async function parseLoadedData(data, mapVersion) { 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( @@ -511,14 +511,14 @@ async function parseLoadedData(data, mapVersion) { 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( @@ -527,7 +527,7 @@ async function parseLoadedData(data, mapVersion) { 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)); @@ -535,60 +535,110 @@ async function parseLoadedData(data, mapVersion) { 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 (!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 (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 }); @@ -598,7 +648,7 @@ async function parseLoadedData(data, mapVersion) { 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 diff --git a/modules/ui/editors.js b/modules/ui/editors.js index a7c0c9cb..890fb58f 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -243,25 +243,22 @@ function removeBurg(id) { } } -function toggleCapital(burg) { - const state = pack.burgs[burg].state; - if (!state) { - tip("Neutral lands cannot have a capital", false, "error"); - return; - } - if (pack.burgs[burg].capital) { - tip("To change capital please assign a capital status to another burg of this state", false, "error"); - return; - } - const old = pack.states[state].capital; +function toggleCapital(burgId) { + const {burgs, states} = pack; + if (burgs[burgId].capital) + return tip("To change capital please assign a capital status to another burg of this state", false, "error"); - // change statuses - pack.states[state].capital = burg; - pack.states[state].center = pack.burgs[burg].cell; - pack.burgs[burg].capital = 1; - pack.burgs[old].capital = 0; - moveBurgToGroup(burg, "cities"); - moveBurgToGroup(old, "towns"); + const stateId = burgs[burgId].state; + if (!stateId) return tip("Neutral lands cannot have a capital", false, "error"); + + const prevCapitalId = states[stateId].capital; + states[stateId].capital = burgId; + states[stateId].center = burgs[burgId].cell; + burgs[burgId].capital = 1; + burgs[prevCapitalId].capital = 0; + + moveBurgToGroup(burgId, "cities"); + moveBurgToGroup(prevCapitalId, "towns"); } function togglePort(burg) { @@ -1176,7 +1173,7 @@ function refreshAllEditors() { // dynamically loaded editors async function editStates() { if (customization) return; - const Editor = await import("../dynamic/editors/states-editor.js?v=1.96.00"); + const Editor = await import("../dynamic/editors/states-editor.js?v=1.96.06"); Editor.open(); } diff --git a/versioning.js b/versioning.js index 393373ca..67182953 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.05"; // generator version, update each time +const version = "1.96.06"; // generator version, update each time { document.title += " v" + version; From 1385354adca1a11b395c193a1ca49f7ad181d6e0 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 4 Mar 2024 21:40:32 +0100 Subject: [PATCH 09/18] fix: icons - use textContent insteod of innerHTML to avoid Google translate issues --- index.html | 10 +++-- modules/ui/editors.js | 4 +- modules/ui/military-overview.js | 72 ++++++++++++++++++++++----------- versioning.js | 2 +- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/index.html b/index.html index 89c77396..4a36f3d4 100644 --- a/index.html +++ b/index.html @@ -5597,7 +5597,9 @@ Crew Power Type - Sep. + + Separate + @@ -5763,7 +5765,7 @@
Select from the list or paste a Unicode character here: - + . See Emojipedia for reference
@@ -8063,7 +8065,7 @@ - + @@ -8087,7 +8089,7 @@ - + diff --git a/modules/ui/editors.js b/modules/ui/editors.js index 890fb58f..7ebed41d 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -1093,12 +1093,12 @@ function selectIcon(initial, callback) { input.oninput = e => callback(input.value); table.onclick = e => { if (e.target.tagName === "TD") { - input.value = e.target.innerHTML; + input.value = e.target.textContent; callback(input.value); } }; table.onmouseover = e => { - if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`); + if (e.target.tagName === "TD") tip(`Click to select ${e.target.textContent} icon`); }; $("#iconSelector").dialog({ diff --git a/modules/ui/military-overview.js b/modules/ui/military-overview.js index 35a23ed4..7c83c757 100644 --- a/modules/ui/military-overview.js +++ b/modules/ui/military-overview.js @@ -54,7 +54,9 @@ function overviewMilitary() { const insert = html => document.getElementById("militaryTotal").insertAdjacentHTML("beforebegin", html); for (const u of options.military) { const label = capitalize(u.name.replace(/_/g, " ")); - insert(`
${label} 
`); + insert( + `
${label} 
` + ); } header.querySelectorAll(".removable").forEach(function (e) { e.addEventListener("click", function () { @@ -76,7 +78,9 @@ function overviewMilitary() { const rate = (total / population) * 100; const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" "); - const lineData = options.military.map(u => `
${getForces(u)}
`).join(" "); + const lineData = options.military + .map(u => `
${getForces(u)}
`) + .join(" "); lines += /* html */ `
${lineData} -
${si(total)}
+
${si( + total + )}
${si(population)}
-
${rn(rate, 2)}%
+
${rn( + rate, + 2 + )}%
s.military.reduce((s, r) => s + (r.u[u.name] || 0), 0); - options.military.forEach(u => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u))); + options.military.forEach( + u => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u)) + ); const population = rn((s.rural + s.urban * urbanization) * populationRate); const total = (line.dataset.total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0)); @@ -237,7 +248,16 @@ function overviewMilitary() { position: {my: "center", at: "center", of: "svg"}, buttons: { Apply: applyMilitaryOptions, - Add: () => addUnitLine({icon: "🛡️", name: "custom" + militaryOptionsTable.rows.length, rural: 0.2, urban: 0.5, crew: 1, power: 1, type: "melee"}), + Add: () => + addUnitLine({ + icon: "🛡️", + name: "custom" + militaryOptionsTable.rows.length, + rural: 0.2, + urban: 0.5, + crew: 1, + power: 1, + type: "melee" + }), Restore: restoreDefaultUnits, Cancel: function () { $(this).dialog("close"); @@ -262,7 +282,7 @@ function overviewMilitary() { if (el.tagName !== "BUTTON") return; const type = el.dataset.type; - if (type === "icon") return selectIcon(el.innerHTML, v => (el.innerHTML = v)); + if (type === "icon") return selectIcon(el.textContent, v => (el.textContent = v)); if (type === "biomes") { const {i, name, color} = biomesData; const biomesArray = Array(i.length).fill(null); @@ -294,7 +314,9 @@ function overviewMilitary() { function addUnitLine(unit) { const {type, icon, name, rural, urban, power, crew, separate} = unit; const row = document.createElement("tr"); - const typeOptions = types.map(t => ``).join(" "); + const typeOptions = types + .map(t => ``) + .join(" "); const getLimitButton = attr => ` + row.innerHTML = /* html */ ` ${getLimitButton("biomes")} ${getLimitButton("states")} @@ -344,7 +368,9 @@ function overviewMilitary() { const lines = filtered.map( ({i, name, fullName, color}) => ` - + ` ); @@ -387,22 +413,21 @@ function overviewMilitary() { function applyMilitaryOptions() { const unitLines = Array.from(tableBody.querySelectorAll("tr")); const names = unitLines.map(r => r.querySelector("input").value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, "_")); - if (new Set(names).size !== names.length) { - tip("All units should have unique names", false, "error"); - return; - } + if (new Set(names).size !== names.length) return tip("All units should have unique names", false, "error"); $("#militaryOptions").dialog("close"); + options.military = unitLines.map((r, i) => { const elements = Array.from(r.querySelectorAll("input, button, select")); - const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] = elements.map(el => { - const {type, value} = el.dataset || {}; - if (type === "icon") return el.innerHTML || "⠀"; - if (type) return value ? value.split(",").map(v => parseInt(v)) : null; - if (el.type === "number") return +el.value || 0; - if (el.type === "checkbox") return +el.checked || 0; - return el.value; - }); + const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] = + elements.map(el => { + const {type, value} = el.dataset || {}; + if (type === "icon") return el.textContent || "⠀"; + if (type) return value ? value.split(",").map(v => parseInt(v)) : null; + if (el.type === "number") return +el.value || 0; + if (el.type === "checkbox") return +el.checked || 0; + return el.value; + }); const unit = {icon, name: names[i], rural, urban, crew, power, type, separate}; if (biomes) unit.biomes = biomes; @@ -419,7 +444,8 @@ function overviewMilitary() { } function militaryRecalculate() { - alertMessage.innerHTML = "Are you sure you want to recalculate military forces for all states?
Regiments for all states will be regenerated"; + alertMessage.innerHTML = + "Are you sure you want to recalculate military forces for all states?
Regiments for all states will be regenerated"; $("#alert").dialog({ resizable: false, title: "Remove regiment", diff --git a/versioning.js b/versioning.js index 67182953..bf163fcc 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.06"; // generator version, update each time +const version = "1.96.07"; // generator version, update each time { document.title += " v" + version; From 56e6e472869e25e8205a99254ce2162e02f4c2ef Mon Sep 17 00:00:00 2001 From: TheRealStanPines <161970745+TheRealStanPines@users.noreply.github.com> Date: Tue, 5 Mar 2024 14:08:21 -0700 Subject: [PATCH 10/18] Update burgs-and-states.js (#1054) * Update burgs-and-states.js Khanate title for Mongolian Empires and Kingdoms, Khaganate for Turkic. * Update burgs-and-states.js I also added Beylik form name Generation. * Update burgs-and-states.js --- modules/burgs-and-states.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js index ff433f46..390165dc 100644 --- a/modules/burgs-and-states.js +++ b/modules/burgs-and-states.js @@ -859,9 +859,10 @@ window.BurgsAndStates = (function () { if (P(0.3) && s.diplomacy.includes("Vassal")) return "Protectorate"; // some vassals } - if (base === 16 && (form === "Empire" || form === "Kingdom")) return "Khaganate"; // Turkic + if (base === 31 && (form === "Empire" || form === "Kingdom")) return "Khanate"; // Mongolian + if (base === 16 && (form === "Principality" )) return "Beylik"; // Turkic if (base === 5 && (form === "Empire" || form === "Kingdom")) return "Tsardom"; // Ruthenian - if ([16, 31].includes(base) && (form === "Empire" || form === "Kingdom")) return "Khaganate"; // Turkic, Mongolian + if (base === 16 && (form === "Empire" || form === "Kingdom")) return "Khaganate"; // Turkic if (base === 12 && (form === "Kingdom" || form === "Grand Duchy")) return "Shogunate"; // Japanese if ([18, 17].includes(base) && form === "Empire") return "Caliphate"; // Arabic, Berber if (base === 18 && (form === "Grand Duchy" || form === "Duchy")) return "Emirate"; // Arabic From d6c3c46a5e02d23a1f58e7eda8e675d34f0f5331 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Thu, 14 Mar 2024 13:56:12 +0400 Subject: [PATCH 11/18] feat: generate watabou preview links for villages (#1056) Co-authored-by: Azgaar --- index.html | 76 +++++------ main.js | 5 +- modules/burgs-and-states.js | 14 +- modules/dynamic/auto-update.js | 16 +++ modules/io/load.js | 2 +- modules/ui/burg-editor.js | 226 ++++++++++++++++----------------- modules/ui/burgs-overview.js | 2 +- modules/ui/editors.js | 95 ++++++++++---- versioning.js | 3 +- 9 files changed, 247 insertions(+), 192 deletions(-) diff --git a/index.html b/index.html index 4a36f3d4..9ac253c8 100644 --- a/index.html +++ b/index.html @@ -3329,6 +3329,22 @@
+
+
Temperature:
+ , like in + + +
+ +
+
Elevation:
+ above sea level +
+
Features:
- -
-
Temperature:
- , like in - - -
- -
-
Elevation:
- above sea level -
-
-
- See in City Generator by Watabou. -
- Seed: +
+
+ Burg preview: +
+
-
- +
@@ -3460,7 +3450,7 @@
- + @@ -8044,7 +8034,7 @@ - + @@ -8061,11 +8051,11 @@ - + - + @@ -8082,12 +8072,12 @@ - + - + @@ -8103,7 +8093,7 @@ - + diff --git a/main.js b/main.js index 0fa7f790..efc115cf 100644 --- a/main.js +++ b/main.js @@ -185,12 +185,13 @@ const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoomDebouced); // default options, based on Earth data let options = { pinNotes: false, - showMFCGMap: true, winds: [225, 45, 225, 315, 135, 315], temperatureEquator: 27, temperatureNorthPole: -30, temperatureSouthPole: -15, - stateLabelsMode: "auto" + stateLabelsMode: "auto", + showBurgPreview: true, + villageMaxPopulation: 2000 }; let mapCoordinates = {}; // map coordinates on globe diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js index 390165dc..f4f6463a 100644 --- a/modules/burgs-and-states.js +++ b/modules/burgs-and-states.js @@ -252,13 +252,15 @@ window.BurgsAndStates = (function () { .filter(b => (newburg ? b.i == newburg.i : b.i && !b.removed)) .forEach(b => { const pop = b.population; - b.citadel = b.capital || (pop > 50 && P(0.75)) || P(0.5) ? 1 : 0; - b.plaza = pop > 50 || (pop > 30 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.25) ? 1 : 0; - b.walls = b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.2) ? 1 : 0; - b.shanty = pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4)) ? 1 : 0; + b.citadel = Number(b.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1)); + b.plaza = Number(pop > 20 || (pop > 10 && P(0.8)) || (pop > 4 && P(0.7)) || P(0.6)); + b.walls = Number(b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1)); + b.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4))); const religion = cells.religion[b.cell]; const theocracy = pack.states[b.state].form === "Theocracy"; - b.temple = (religion && theocracy) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5)) ? 1 : 0; + b.temple = Number( + (religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5)) + ); }); }; @@ -860,7 +862,7 @@ window.BurgsAndStates = (function () { } if (base === 31 && (form === "Empire" || form === "Kingdom")) return "Khanate"; // Mongolian - if (base === 16 && (form === "Principality" )) return "Beylik"; // Turkic + if (base === 16 && form === "Principality") return "Beylik"; // Turkic if (base === 5 && (form === "Empire" || form === "Kingdom")) return "Tsardom"; // Ruthenian if (base === 16 && (form === "Empire" || form === "Kingdom")) return "Khaganate"; // Turkic if (base === 12 && (form === "Kingdom" || form === "Grand Duchy")) return "Shogunate"; // Japanese diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js index 51c5ee4c..a77ddd45 100644 --- a/modules/dynamic/auto-update.js +++ b/modules/dynamic/auto-update.js @@ -827,4 +827,20 @@ export function resolveVersionConflicts(version) { }); }); } + + if (version < 1.97) { + // v1.97.00 changed MFCG link to an arbitrary preview URL + options.villageMaxPopulation = 2000; + options.showBurgPreview = options.showMFCGMap; + delete options.showMFCGMap; + + pack.burgs.forEach(burg => { + if (!burg.i || burg.removed) return; + + if (burg.MFCG) { + burg.link = getBurgLink(burg); + delete burg.MFCG; + } + }); + } } diff --git a/modules/io/load.js b/modules/io/load.js index 3f6abf40..d07679d4 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -456,7 +456,7 @@ async function parseLoadedData(data, mapVersion) { { // dynamically import and run auto-update script const versionNumber = parseFloat(params[0]); - const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.96.00"); + const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.97.00"); resolveVersionConflicts(versionNumber); } diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js index dd34b38a..a90a11d8 100644 --- a/modules/ui/burg-editor.js +++ b/modules/ui/burg-editor.js @@ -21,38 +21,37 @@ function editBurg(id) { modules.editBurg = true; // add listeners - document.getElementById("burgGroupShow").addEventListener("click", showGroupSection); - document.getElementById("burgGroupHide").addEventListener("click", hideGroupSection); - document.getElementById("burgSelectGroup").addEventListener("change", changeGroup); - document.getElementById("burgInputGroup").addEventListener("change", createNewGroup); - document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput); - document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup); + byId("burgGroupShow").addEventListener("click", showGroupSection); + byId("burgGroupHide").addEventListener("click", hideGroupSection); + byId("burgSelectGroup").addEventListener("change", changeGroup); + byId("burgInputGroup").addEventListener("change", createNewGroup); + byId("burgAddGroup").addEventListener("click", toggleNewGroupInput); + byId("burgRemoveGroup").addEventListener("click", removeBurgsGroup); - document.getElementById("burgName").addEventListener("input", changeName); - document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom); - document.getElementById("burgType").addEventListener("input", changeType); - document.getElementById("burgCulture").addEventListener("input", changeCulture); - document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture); - document.getElementById("burgPopulation").addEventListener("change", changePopulation); + byId("burgName").addEventListener("input", changeName); + byId("burgNameReRandom").addEventListener("click", generateNameRandom); + byId("burgType").addEventListener("input", changeType); + byId("burgCulture").addEventListener("input", changeCulture); + byId("burgNameReCulture").addEventListener("click", generateNameCulture); + byId("burgPopulation").addEventListener("change", changePopulation); burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature)); - document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed); - document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed); - document.getElementById("addCustomMFCGBurgLink").addEventListener("click", addCustomMfcgLink); + byId("burgLinkOpen").addEventListener("click", openBurgLink); + byId("burgLinkEdit").addEventListener("click", changeBurgLink); - document.getElementById("burgStyleShow").addEventListener("click", showStyleSection); - document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection); - document.getElementById("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle); - document.getElementById("burgEditIconStyle").addEventListener("click", editGroupIconStyle); - document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle); + byId("burgStyleShow").addEventListener("click", showStyleSection); + byId("burgStyleHide").addEventListener("click", hideStyleSection); + byId("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle); + byId("burgEditIconStyle").addEventListener("click", editGroupIconStyle); + byId("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle); - document.getElementById("burgEmblem").addEventListener("click", openEmblemEdit); - document.getElementById("burgToggleMFCGMap").addEventListener("click", toggleMFCGMap); - document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit); - document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg); - document.getElementById("burglLegend").addEventListener("click", editBurgLegend); - document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton); - document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg); - document.getElementById("burgTemperatureGraph").addEventListener("click", showTemperatureGraph); + byId("burgEmblem").addEventListener("click", openEmblemEdit); + byId("burgTogglePreview").addEventListener("click", toggleBurgPreview); + byId("burgEditEmblem").addEventListener("click", openEmblemEdit); + byId("burgRelocate").addEventListener("click", toggleRelocateBurg); + byId("burglLegend").addEventListener("click", editBurgLegend); + byId("burgLock").addEventListener("click", toggleBurgLockButton); + byId("burgRemove").addEventListener("click", removeSelectedBurg); + byId("burgTemperatureGraph").addEventListener("click", showTemperatureGraph); function updateBurgValues() { const id = +elSelected.attr("data-id"); @@ -60,46 +59,46 @@ function editBurg(id) { const province = pack.cells.province[b.cell]; const provinceName = province ? pack.provinces[province].fullName + ", " : ""; const stateName = pack.states[b.state].fullName || pack.states[b.state].name; - document.getElementById("burgProvinceAndState").innerHTML = provinceName + stateName; + byId("burgProvinceAndState").innerHTML = provinceName + stateName; - document.getElementById("burgName").value = b.name; - document.getElementById("burgType").value = b.type || "Generic"; - document.getElementById("burgPopulation").value = rn(b.population * populationRate * urbanization); - document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none"; + byId("burgName").value = b.name; + byId("burgType").value = b.type || "Generic"; + byId("burgPopulation").value = rn(b.population * populationRate * urbanization); + byId("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none"; // update list and select culture - const cultureSelect = document.getElementById("burgCulture"); + const cultureSelect = byId("burgCulture"); cultureSelect.options.length = 0; const cultures = pack.cultures.filter(c => !c.removed); cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture))); const temperature = grid.cells.temp[pack.cells.g[b.cell]]; - document.getElementById("burgTemperature").innerHTML = convertTemperature(temperature); - document.getElementById("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature); - document.getElementById("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]); + byId("burgTemperature").innerHTML = convertTemperature(temperature); + byId("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature); + byId("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]); // toggle features - if (b.capital) document.getElementById("burgCapital").classList.remove("inactive"); - else document.getElementById("burgCapital").classList.add("inactive"); - if (b.port) document.getElementById("burgPort").classList.remove("inactive"); - else document.getElementById("burgPort").classList.add("inactive"); - if (b.citadel) document.getElementById("burgCitadel").classList.remove("inactive"); - else document.getElementById("burgCitadel").classList.add("inactive"); - if (b.walls) document.getElementById("burgWalls").classList.remove("inactive"); - else document.getElementById("burgWalls").classList.add("inactive"); - if (b.plaza) document.getElementById("burgPlaza").classList.remove("inactive"); - else document.getElementById("burgPlaza").classList.add("inactive"); - if (b.temple) document.getElementById("burgTemple").classList.remove("inactive"); - else document.getElementById("burgTemple").classList.add("inactive"); - if (b.shanty) document.getElementById("burgShanty").classList.remove("inactive"); - else document.getElementById("burgShanty").classList.add("inactive"); + if (b.capital) byId("burgCapital").classList.remove("inactive"); + else byId("burgCapital").classList.add("inactive"); + if (b.port) byId("burgPort").classList.remove("inactive"); + else byId("burgPort").classList.add("inactive"); + if (b.citadel) byId("burgCitadel").classList.remove("inactive"); + else byId("burgCitadel").classList.add("inactive"); + if (b.walls) byId("burgWalls").classList.remove("inactive"); + else byId("burgWalls").classList.add("inactive"); + if (b.plaza) byId("burgPlaza").classList.remove("inactive"); + else byId("burgPlaza").classList.add("inactive"); + if (b.temple) byId("burgTemple").classList.remove("inactive"); + else byId("burgTemple").classList.add("inactive"); + if (b.shanty) byId("burgShanty").classList.remove("inactive"); + else byId("burgShanty").classList.add("inactive"); //toggle lock updateBurgLockIcon(); // select group const group = elSelected.node().parentNode.id; - const select = document.getElementById("burgSelectGroup"); + const select = byId("burgSelectGroup"); select.options.length = 0; // remove all options burgLabels.selectAll("g").each(function () { @@ -109,20 +108,13 @@ function editBurg(id) { // set emlem image const coaID = "burgCOA" + id; COArenderer.trigger(coaID, b.coa); - document.getElementById("burgEmblem").setAttribute("href", "#" + coaID); + byId("burgEmblem").setAttribute("href", "#" + coaID); - if (options.showMFCGMap) { - document.getElementById("mfcgPreviewSection").style.display = "block"; - updateMFCGFrame(b); - - if (b.link) { - document.getElementById("mfcgBurgSeedSection").style.display = "none"; - } else { - document.getElementById("mfcgBurgSeedSection").style.display = "inline-block"; - document.getElementById("mfcgBurgSeed").value = getBurgSeed(b); - } + if (options.showBurgPreview) { + byId("burgPreviewSection").style.display = "block"; + updateBurgPreview(b); } else { - document.getElementById("mfcgPreviewSection").style.display = "none"; + byId("burgPreviewSection").style.display = "none"; } } @@ -141,15 +133,15 @@ function editBurg(id) { function showGroupSection() { document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none")); - document.getElementById("burgGroupSection").style.display = "inline-block"; + byId("burgGroupSection").style.display = "inline-block"; } function hideGroupSection() { document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block")); - document.getElementById("burgGroupSection").style.display = "none"; - document.getElementById("burgInputGroup").style.display = "none"; - document.getElementById("burgInputGroup").value = ""; - document.getElementById("burgSelectGroup").style.display = "inline-block"; + byId("burgGroupSection").style.display = "none"; + byId("burgInputGroup").style.display = "none"; + byId("burgInputGroup").value = ""; + byId("burgSelectGroup").style.display = "inline-block"; } function changeGroup() { @@ -178,7 +170,7 @@ function editBurg(id) { .replace(/ /g, "_") .replace(/[^\w\s]/gi, ""); - if (document.getElementById(group)) { + if (byId(group)) { tip("Element with this id already exists. Please provide a unique name", false, "error"); return; } @@ -206,10 +198,10 @@ function editBurg(id) { // just rename if only 1 element left const count = elSelected.node().parentNode.childElementCount; if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) { - document.getElementById("burgSelectGroup").selectedOptions[0].remove(); - document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true)); + byId("burgSelectGroup").selectedOptions[0].remove(); + byId("burgSelectGroup").options.add(new Option(group, group, false, true)); toggleNewGroupInput(); - document.getElementById("burgInputGroup").value = ""; + byId("burgInputGroup").value = ""; labelG.id = group; iconG.id = group; if (anchor) anchorG.id = group; @@ -217,9 +209,9 @@ function editBurg(id) { } // create new groups - document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true)); + byId("burgSelectGroup").options.add(new Option(group, group, false, true)); toggleNewGroupInput(); - document.getElementById("burgInputGroup").value = ""; + byId("burgInputGroup").value = ""; addBurgsGroup(group); moveBurgToGroup(id, group); @@ -300,7 +292,10 @@ function editBurg(id) { function changePopulation() { const id = +elSelected.attr("data-id"); + const burg = pack.burgs[id]; + pack.burgs[id].population = rn(burgPopulation.value / populationRate / urbanization, 4); + updateBurgPreview(burg); } function toggleFeature() { @@ -314,9 +309,9 @@ function editBurg(id) { if (burg[feature]) this.classList.remove("inactive"); else if (!burg[feature]) this.classList.add("inactive"); - if (burg.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block"; - else document.getElementById("burgEditAnchorStyle").style.display = "none"; - updateMFCGFrame(burg); + if (burg.port) byId("burgEditAnchorStyle").style.display = "inline-block"; + else byId("burgEditAnchorStyle").style.display = "none"; + updateBurgPreview(burg); } function toggleBurgLockButton() { @@ -331,22 +326,22 @@ function editBurg(id) { const id = +elSelected.attr("data-id"); const b = pack.burgs[id]; if (b.lock) { - document.getElementById("burgLock").classList.remove("icon-lock-open"); - document.getElementById("burgLock").classList.add("icon-lock"); + byId("burgLock").classList.remove("icon-lock-open"); + byId("burgLock").classList.add("icon-lock"); } else { - document.getElementById("burgLock").classList.remove("icon-lock"); - document.getElementById("burgLock").classList.add("icon-lock-open"); + byId("burgLock").classList.remove("icon-lock"); + byId("burgLock").classList.add("icon-lock-open"); } } function showStyleSection() { document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none")); - document.getElementById("burgStyleSection").style.display = "inline-block"; + byId("burgStyleSection").style.display = "inline-block"; } function hideStyleSection() { document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block")); - document.getElementById("burgStyleSection").style.display = "none"; + byId("burgStyleSection").style.display = "none"; } function editGroupLabelStyle() { @@ -364,39 +359,38 @@ function editBurg(id) { editStyle("anchors", g); } - function updateMFCGFrame(burg) { - const mfcgURL = getMFCGlink(burg); - document.getElementById("mfcgPreview").setAttribute("src", mfcgURL + "&preview=1"); - document.getElementById("mfcgLink").setAttribute("href", mfcgURL); + function updateBurgPreview(burg) { + const src = getBurgLink(burg) + "&preview=1"; + + // recreate object to force reload (Chrome bug) + const container = byId("burgPreviewObject"); + container.innerHTML = ""; + const object = document.createElement("object"); + object.style.width = "100%"; + object.data = src; + container.insertBefore(object, null); } - function changeSeed() { + function openBurgLink() { const id = +elSelected.attr("data-id"); const burg = pack.burgs[id]; - const burgSeed = +this.value; - burg.MFCG = burgSeed; - updateMFCGFrame(burg); + + openURL(getBurgLink(burg)); } - function randomizeSeed() { + function changeBurgLink() { const id = +elSelected.attr("data-id"); const burg = pack.burgs[id]; - const burgSeed = rand(1e9 - 1); - burg.MFCG = burgSeed; - updateMFCGFrame(burg); - document.getElementById("mfcgBurgSeed").value = burgSeed; - } - function addCustomMfcgLink() { - const id = +elSelected.attr("data-id"); - const burg = pack.burgs[id]; - const message = - "Enter custom link to the burg map. It can be a link to Medieval Fantasy City Generator or other tool. Keep empty to use MFCG seed"; - prompt(message, {default: burg.link || "", required: false}, link => { - if (link) burg.link = link; - else delete burg.link; - updateMFCGFrame(burg); - }); + prompt( + "Provide custom link to the burg map. It can be a link to Medieval Fantasy City Generator, a different tool, or just an image. Leave empty to use the default map", + {default: getBurgLink(burg), required: false}, + link => { + if (link) burg.link = link; + else delete burg.link; + updateBurgPreview(burg); + } + ); } function openEmblemEdit() { @@ -405,16 +399,16 @@ function editBurg(id) { editEmblem("burg", "burgCOA" + id, burg); } - function toggleMFCGMap() { - options.showMFCGMap = !options.showMFCGMap; - document.getElementById("mfcgPreviewSection").style.display = options.showMFCGMap ? "block" : "none"; - document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap ? "icon-map" : "icon-map-o"; + function toggleBurgPreview() { + options.showBurgPreview = !options.showBurgPreview; + byId("burgPreviewSection").style.display = options.showBurgPreview ? "block" : "none"; + byId("burgTogglePreview").className = options.showBurgPreview ? "icon-map" : "icon-map-o"; } function toggleRelocateBurg() { - const toggler = document.getElementById("toggleCells"); - document.getElementById("burgRelocate").classList.toggle("pressed"); - if (document.getElementById("burgRelocate").classList.contains("pressed")) { + const toggler = byId("toggleCells"); + byId("burgRelocate").classList.toggle("pressed"); + if (byId("burgRelocate").classList.contains("pressed")) { viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick); tip("Click on map to relocate burg. Hold Shift for continuous move", true); if (!layerIsOn("toggleCells")) { @@ -534,7 +528,7 @@ function editBurg(id) { } function closeBurgEditor() { - document.getElementById("burgRelocate").classList.remove("pressed"); + byId("burgRelocate").classList.remove("pressed"); burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false); unselect(); } diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js index d0ad25c2..561722ce 100644 --- a/modules/ui/burgs-overview.js +++ b/modules/ui/burgs-overview.js @@ -514,7 +514,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) { data += b.temple ? "temple," : ","; data += b.shanty ? "shanty town," : ","; data += b.coa ? JSON.stringify(b.coa).replace(/"/g, "").replace(/,/g, ";") + "," : ","; - data += getMFCGlink(b); + data += getBurgLink(b); data += "\n"; }); diff --git a/modules/ui/editors.js b/modules/ui/editors.js index 7ebed41d..58c1c18b 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -288,16 +288,20 @@ function togglePort(burg) { .attr("height", size); } -function getBurgSeed(burg) { - return burg.MFCG || Number(`${seed}${String(burg.i).padStart(4, 0)}`); -} - -function getMFCGlink(burg) { +function getBurgLink(burg) { if (burg.link) return burg.link; + const population = burg.population * populationRate * urbanization; + if (population >= options.villageMaxPopulation || burg.citadel || burg.walls || burg.temple || burg.shanty) + return createMfcgLink(burg); + + return createVillageGeneratorLink(burg); +} + +function createMfcgLink(burg) { const {cells} = pack; const {i, name, population: burgPopulation, cell} = burg; - const seed = getBurgSeed(burg); + const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0); const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385); const size = minmax(Math.ceil(sizeRaw), 6, 100); @@ -305,11 +309,19 @@ function getMFCGlink(burg) { const river = cells.r[cell] ? 1 : 0; const coast = Number(burg.port > 0); - const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : null; + const sea = (() => { + if (!coast || !cells.haven[cell]) return null; + + // calculate see direction: 0 = south, 0.5 = west, 1 = north, 1.5 = east + const p1 = cells.p[cell]; + const p2 = cells.p[cells.haven[cell]]; + let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90; + if (deg < 0) deg += 360; + return rn(normalize(deg, 0, 360) * 2, 2); + })(); - const biome = cells.biome[cell]; const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8]; - const farms = +arableBiomes.includes(biome); + const farms = +arableBiomes.includes(cells.biome[cell]); const citadel = +burg.citadel; const urban_castle = +(citadel && each(2)(i)); @@ -321,19 +333,12 @@ function getMFCGlink(burg) { const temple = +burg.temple; const shantytown = +burg.shanty; - function getSeaDirections(i) { - const p1 = cells.p[i]; - const p2 = cells.p[cells.haven[i]]; - let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90; - if (deg < 0) deg += 360; - return rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east - } - - const parameters = { + const url = new URL("https://watabou.github.io/city-generator/"); + url.search = new URLSearchParams({ name, population, size, - seed, + seed: burgSeed, river, coast, farms, @@ -345,14 +350,60 @@ function getMFCGlink(burg) { walls, shantytown, gates: -1 - }; - const url = new URL("https://watabou.github.io/city-generator/"); - url.search = new URLSearchParams(parameters); + }); if (sea) url.searchParams.append("sea", sea); return url.toString(); } +function createVillageGeneratorLink(burg) { + const {cells, features} = pack; + const {i, population, cell} = burg; + + const pop = rn(population * populationRate * urbanization); + const burgSeed = seed + String(i).padStart(4, 0); + const tags = []; + + if (cells.r[cell] && cells.haven[cell]) tags.push("estuary"); + else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district"); + else if (burg.port) tags.push("coast"); + else if (cells.conf[cell]) tags.push("confluence"); + else if (cells.r[cell]) tags.push("river"); + else if (pop < 200 && each(4)(cell)) tags.push("pond"); + + const roadsAround = cells.c[cell].filter(c => cells.h[c] >= 20 && cells.road[c]).length; + if (roadsAround > 1) tags.push("highway"); + else if (roadsAround === 1) tags.push("dead end"); + else tags.push("isolated"); + + const biome = cells.biome[cell]; + const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8]; + if (!arableBiomes.includes(biome)) tags.push("uncultivated"); + else if (each(6)(cell)) tags.push("farmland"); + + const temp = grid.cells.temp[cells.g[cell]]; + if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards"); + + if (!burg.plaza) tags.push("no square"); + + if (pop < 100) tags.push("sparse"); + else if (pop > 300) tags.push("dense"); + + const width = (() => { + if (pop > 1500) return 1600; + if (pop > 1000) return 1400; + if (pop > 500) return 1000; + if (pop > 200) return 800; + if (pop > 100) return 600; + return 400; + })(); + const height = rn(width / 2.2); + + const url = new URL("https://watabou.github.io/village-generator/"); + url.search = new URLSearchParams({pop, name: "", seed: burgSeed, width, height, tags}); + return url.toString(); +} + // draw legend box function drawLegend(name, data) { legend.selectAll("*").remove(); // fully redraw every time diff --git a/versioning.js b/versioning.js index bf163fcc..6b7cc11d 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.96.07"; // generator version, update each time +const version = "1.97.00"; // generator version, update each time { document.title += " v" + version; @@ -28,6 +28,7 @@ const version = "1.96.07"; // generator version, update each time
    Latest changes: +
  • Preview villages map
  • Ability to render ocean heightmap
  • Scale bar styling features
  • Vignette visual layer and vignette styling options
  • From 2eb9936f6c2d405a2930559849aadd07d16ded51 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Thu, 14 Mar 2024 11:30:21 +0100 Subject: [PATCH 12/18] chore: update readme --- README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index efd8224e..3ab245b2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) - # Fantasy Map Generator Azgaar's _Fantasy Map Generator_ is a free web application that helps fantasy writers, game masters, and cartographers create and edit fantasy maps. @@ -8,19 +6,17 @@ Link: [azgaar.github.io/Fantasy-Map-Generator](https://azgaar.github.io/Fantasy- Refer to the [project wiki](https://github.com/Azgaar/Fantasy-Map-Generator/wiki) for guidance. The current progress is tracked in [Trello](https://trello.com/b/7x832DG4/fantasy-map-generator). Some details are covered in my old blog [_Fantasy Maps for fun and glory_](https://azgaar.wordpress.com). -[![preview](https://cdn.discordapp.com/attachments/587406457725779968/594840629213659136/preview1.png)](https://i.redd.it/8bf81ir2cy631.png) +[![preview](https://github.com/Azgaar/Fantasy-Map-Generator/assets/26469650/9502eae9-92e0-4d0d-9f17-a2ba4a565c01)](https://github.com/Azgaar/Fantasy-Map-Generator/assets/26469650/11a42446-4bd5-4526-9cb1-3ef97c868992) -[![preview](https://cdn.discordapp.com/attachments/587406457725779968/594840633911279636/preview2.png)](https://cdn.discordapp.com/attachments/515359185664344071/593888810782162964/The_Wichin_Island_sepia.png) +[![preview](https://github.com/Azgaar/Fantasy-Map-Generator/assets/26469650/e751a9e5-7986-4638-b8a9-362395ef7583)](https://github.com/Azgaar/Fantasy-Map-Generator/assets/26469650/e751a9e5-7986-4638-b8a9-362395ef7583) -[![preview](https://cdn.discordapp.com/attachments/587406457725779968/594840632296734720/preview3.png)](https://cdn.discordapp.com/attachments/515359096925454350/593891237984206848/The_Wichin_Island_-_diplomacy.png) +[![preview](https://github.com/Azgaar/Fantasy-Map-Generator/assets/26469650/b0d0efde-a0d1-4e80-8818-ea3dd83c2323)](https://github.com/Azgaar/Fantasy-Map-Generator/assets/26469650/b0d0efde-a0d1-4e80-8818-ea3dd83c2323) Join our [Discord server](https://discordapp.com/invite/X7E84HU) and [Reddit community](https://www.reddit.com/r/FantasyMapGenerator) to share your creations, discuss the Generator, suggest ideas and get the most recent updates. -Contact me via [email](mailto:azgaar.fmg@yandex.by) if you have non-public suggestions. For bug reports please use [GitHub issues](https://github.com/Azgaar/Fantasy-Map-Generator/issues) or _#bugs_ channel on Discord. If you are facing performance issues, please read [the tips](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Tips#performance-tips). +Contact me via [email](mailto:azgaar.fmg@yandex.com) if you have non-public suggestions. For bug reports please use [GitHub issues](https://github.com/Azgaar/Fantasy-Map-Generator/issues) or _#fmg-bugs_ channel on Discord. If you are facing performance issues, please read [the tips](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Tips#performance-tips). -Electron desktop application is available in [releases](https://github.com/Azgaar/Fantasy-Map-Generator/releases). Download archive for your architecture, unzip and run. - -Pull requests are highly welcomed. The codebase is messy and requires re-design, but I will appreciate if you start with minor changes. Check out the [data model](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Data-model) before contributing. +Pull requests are highly welcomed. The codebase is messy and requires re-design. I will appreciate if you start with minor changes. Check out the [data model](https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Data-model) before contributing. You can support the project on [Patreon](https://www.patreon.com/azgaar). From 5803f44b996f6c2727610dc9faca2d73f05b4fa6 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Fri, 15 Mar 2024 01:04:46 +0100 Subject: [PATCH 13/18] fix: add new army --- index.html | 2 +- modules/military-generator.js | 14 +++++++------- versioning.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index 9ac253c8..f9b54ef9 100644 --- a/index.html +++ b/index.html @@ -8037,7 +8037,7 @@ - + diff --git a/modules/military-generator.js b/modules/military-generator.js index be56658f..a480bfaa 100644 --- a/modules/military-generator.js +++ b/modules/military-generator.js @@ -381,29 +381,29 @@ window.Military = (function () { .text(d => d.icon); }; - const drawRegiment = function (reg, s) { + const drawRegiment = function (reg, stateId) { const size = +armies.attr("box-size"); const w = reg.n ? size * 4 : size * 6; const h = size * 2; const x1 = rn(reg.x - w / 2, 2); const y1 = rn(reg.y - size, 2); - let army = armies.select("g#army" + s); + let army = armies.select("g#army" + stateId); if (!army.size()) { - const baseColor = pack.states[s].color[0] === "#" ? pack.states[s].color : "#999"; - const darkerColor = d3.color(army.attr("fill")).darker().hex(); + const baseColor = pack.states[stateId].color[0] === "#" ? pack.states[stateId].color : "#999"; + const darkerColor = d3.color(baseColor).darker().hex(); army = armies .append("g") - .attr("id", "army" + s) + .attr("id", "army" + stateId) .attr("fill", baseColor) .attr("color", darkerColor); } const g = army .append("g") - .attr("id", "regiment" + s + "-" + reg.i) + .attr("id", "regiment" + stateId + "-" + reg.i) .attr("data-name", reg.name) - .attr("data-state", s) + .attr("data-state", stateId) .attr("data-id", reg.i); g.append("rect").attr("x", x1).attr("y", y1).attr("width", w).attr("height", h); g.append("text").attr("x", reg.x).attr("y", reg.y).text(getTotal(reg)); diff --git a/versioning.js b/versioning.js index 6b7cc11d..0c547679 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.97.00"; // generator version, update each time +const version = "1.97.01"; // generator version, update each time { document.title += " v" + version; From 72b6314d34831740f4623b417d2429f06b8d4b9c Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 16 Mar 2024 13:10:49 +0100 Subject: [PATCH 14/18] fix: adding a capital for neutrals --- index.html | 2 +- modules/io/load.js | 4 +++- versioning.js | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index f9b54ef9..725a0e3a 100644 --- a/index.html +++ b/index.html @@ -8093,7 +8093,7 @@ - + diff --git a/modules/io/load.js b/modules/io/load.js index d07679d4..c038eb5c 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -539,6 +539,8 @@ async function parseLoadedData(data, mapVersion) { }); pack.burgs.forEach(burg => { + 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; @@ -627,7 +629,7 @@ async function parseLoadedData(data, mapVersion) { return; } - if (stateBurgs.length && !capitalBurgs.length) { + 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; diff --git a/versioning.js b/versioning.js index 0c547679..0eea1e86 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.97.01"; // generator version, update each time +const version = "1.97.02"; // generator version, update each time { document.title += " v" + version; From 7f587400ecab9cd3d9f3c6c83653dd0789f64902 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 17 Mar 2024 02:03:56 +0100 Subject: [PATCH 15/18] feat: improve tiles export UX --- index.html | 10 +-- modules/io/export.js | 138 +++++++++++++++++++++++++----------------- modules/ui/options.js | 38 +++++------- versioning.js | 2 +- 4 files changed, 102 insertions(+), 86 deletions(-) diff --git a/index.html b/index.html index 725a0e3a..4bf4095c 100644 --- a/index.html +++ b/index.html @@ -6032,7 +6032,7 @@
@@ -8057,7 +8059,7 @@ - + diff --git a/modules/ui/world-configurator.js b/modules/ui/world-configurator.js index c47beaca..0f25057e 100644 --- a/modules/ui/world-configurator.js +++ b/modules/ui/world-configurator.js @@ -113,9 +113,11 @@ function editWorld() { function toKilometer(v) { if (unit === "km") return v; - else if (unit === "mi") return v * 1.60934; - else if (unit === "lg") return v * 5.556; - else if (unit === "vr") return v * 1.0668; + if (unit === "mi") return v * 1.60934; + if (unit === "lg") return v * 4.828; + if (unit === "vr") return v * 1.0668; + if (unit === "nmi") return v * 1.852; + if (unit === "nlg") return v * 5.556; return 0; // 0 if distanceUnitInput is a custom unit } diff --git a/versioning.js b/versioning.js index 1574e729..5f3d0848 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.97.04"; // generator version, update each time +const version = "1.97.05"; // generator version, update each time { document.title += " v" + version;