diff --git a/index.html b/index.html index e4cd8ea2..d2bbf3d2 100644 --- a/index.html +++ b/index.html @@ -2857,6 +2857,7 @@ + @@ -3756,6 +3757,7 @@ + @@ -4650,6 +4652,7 @@ + diff --git a/modules/io/formats.js b/modules/io/formats.js new file mode 100644 index 00000000..329d85db --- /dev/null +++ b/modules/io/formats.js @@ -0,0 +1,19 @@ +"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/markers-generator.js b/modules/markers-generator.js index e44d5afd..156e348c 100644 --- a/modules/markers-generator.js +++ b/modules/markers-generator.js @@ -794,7 +794,7 @@ window.Markers = (function () { const {cells, burgs} = pack; // Portals can only be added to burgs - if (cells.burg[cell]) return; + if (!cells.burg[cell]) return; const burgName = burgs[cells.burg[cell]].name; const name = `${burgName} Portal`; diff --git a/modules/ui/cultures-editor.js b/modules/ui/cultures-editor.js index 50b5bc63..65d5fd38 100644 --- a/modules/ui/cultures-editor.js +++ b/modules/ui/cultures-editor.js @@ -1,5 +1,6 @@ "use strict"; function editCultures() { + const cultureTypes = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; if (customization) return; closeDialogs("#culturesEditor, .stable"); if (!layerIsOn("toggleCultures")) toggleCultures(); @@ -37,6 +38,9 @@ function editCultures() { document.getElementById("culturesEditNamesBase").addEventListener("click", editNamesbase); document.getElementById("culturesAdd").addEventListener("click", enterAddCulturesMode); document.getElementById("culturesExport").addEventListener("click", downloadCulturesData); + document.getElementById("culturesImport").addEventListener("click", () => document.getElementById("culturesCSVToLoad").click()); + + document.getElementById("culturesCSVToLoad").addEventListener("change", uploadCulturesData); function refreshCulturesEditor() { culturesCollectStatistics(); @@ -169,8 +173,7 @@ function editCultures() { function getTypeOptions(type) { let options = ""; - const types = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; - types.forEach(t => (options += ``)); + cultureTypes.forEach(t => (options += ``)); return options; } @@ -366,7 +369,7 @@ function editCultures() { width: "24em", buttons: { Apply: function () { - applyPopulationChange(); + applyPopulationChange(rural, urban, ruralPop.value, urbanPop.value, culture); $(this).dialog("close"); }, Cancel: function () { @@ -375,32 +378,33 @@ function editCultures() { }, position: {my: "center", at: "center", of: "svg"} }); + } - function applyPopulationChange() { - const ruralChange = ruralPop.value / rural; - if (isFinite(ruralChange) && ruralChange !== 1) { - const cells = pack.cells.i.filter(i => pack.cells.culture[i] === culture); - cells.forEach(i => (pack.cells.pop[i] *= ruralChange)); - } - if (!isFinite(ruralChange) && +ruralPop.value > 0) { - const points = ruralPop.value / populationRate; - const cells = pack.cells.i.filter(i => pack.cells.culture[i] === culture); - const pop = rn(points / cells.length); - cells.forEach(i => (pack.cells.pop[i] = pop)); - } - - const urbanChange = urbanPop.value / urban; - if (isFinite(urbanChange) && urbanChange !== 1) { - burgs.forEach(b => (b.population = rn(b.population * urbanChange, 4))); - } - if (!isFinite(urbanChange) && +urbanPop.value > 0) { - const points = urbanPop.value / populationRate / urbanization; - const population = rn(points / burgs.length, 4); - burgs.forEach(b => (b.population = population)); - } - - refreshCulturesEditor(); + function applyPopulationChange(oldRural, oldUrban, newRural, newUrban, culture) { + const ruralChange = newRural / oldRural; + if (isFinite(ruralChange) && ruralChange !== 1) { + const cells = pack.cells.i.filter(i => pack.cells.culture[i] === culture); + cells.forEach(i => (pack.cells.pop[i] *= ruralChange)); } + if (!isFinite(ruralChange) && +newRural > 0) { + const points = newRural / populationRate; + const cells = pack.cells.i.filter(i => pack.cells.culture[i] === culture); + const pop = rn(points / cells.length); + cells.forEach(i => (pack.cells.pop[i] = pop)); + } + + const burgs = pack.burgs.filter(b => !b.removed && b.culture === culture); + const urbanChange = newUrban / oldUrban; + if (isFinite(urbanChange) && urbanChange !== 1) { + burgs.forEach(b => (b.population = rn(b.population * urbanChange, 4))); + } + if (!isFinite(urbanChange) && +newUrban > 0) { + const points = newUrban / populationRate / urbanization; + const population = rn(points / burgs.length, 4); + burgs.forEach(b => (b.population = population)); + } + + refreshCulturesEditor(); } function cultureRegenerateBurgs() { @@ -856,7 +860,7 @@ function editCultures() { function downloadCulturesData() { const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value; - let data = "Id,Culture,Color,Cells,Expansionism,Type,Area " + unit + ",Population,Namesbase,Emblems Shape\n"; // headers + let data = "Id,Culture,Color,Cells,Expansionism,Type,Area " + unit + ",Population,Namesbase,Emblems Shape,Origin\n"; // headers body.querySelectorAll(":scope > div").forEach(function (el) { data += el.dataset.id + ","; @@ -869,7 +873,8 @@ function editCultures() { data += el.dataset.population + ","; const base = +el.dataset.base; data += nameBases[base].name + ","; - data += el.dataset.emblems + "\n"; + data += el.dataset.emblems + ","; + data += pack.cultures[+el.dataset.id].origin + "\n"; }); const name = getFileName("Cultures") + ".csv"; @@ -881,4 +886,55 @@ function editCultures() { exitCulturesManualAssignment("close"); exitAddCultureMode(); } + async function uploadCulturesData() { + const csv = await Formats.csvParser(this.files[0]); + this.value = ""; + const cultures = pack.cultures; + const shapes = Object.keys(COA.shields.types) + .map(type => Object.keys(COA.shields[type])) + .flat(); + const populated = pack.cells.pop.map((c, i) => c? i: null).filter(c => c); + cultures.forEach((item) => {if (item.i) item.removed = true}); + for (const c of csv.iterator((a, b) => +a[0] > +b[0])) { + let current; + if (+c.id < cultures.length) { + current = cultures[c.id]; + const ratio = current.urban / (current.rural + current.urban); + applyPopulationChange(current.rural, current.urban, c.population*(1 - ratio), c.population*ratio, +c.id); + } else { + current = { + i: cultures.length, + center: ra(populated), + area: 0, + cells: 0, + origin: 0, + rural: 0, + urban: 0, + } + cultures.push(current) + } + current.name = c.culture; + current.code = abbreviate(current.name, cultures.map(c => c.code)); + current.color = c.color; + current.expansionism = +c.expansionism; + current.origin = +c.origin; + if (cultureTypes.includes(c.type)) + current.type = c.type; + else + current.type = "Generic"; + current.removed = false; + const shieldShape = c["emblems shape"].toLowerCase(); + if (shapes.includes(shieldShape)) + current.shield = shieldShape + else + current.shield = "heater"; + + const nbi = nameBases.findIndex(n => n.name==c.namesbase); + current.base = nbi==-1? 0: nbi; + } + const validId = cultures.filter(c => !c.removed).map(c => c.i); + cultures.forEach(item => item.origin = validId.includes(item.origin)? item.origin:0); + cultures[0].origin = null; + refreshCulturesEditor(); + } } diff --git a/modules/ui/options.js b/modules/ui/options.js index ad90d7fe..f9c9473c 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -105,7 +105,8 @@ function showSupporters() { Jonathan Williams,ojacid .,Brian Wilson,A Patreon of the Ahts,Shubham Jakhotiya,www15o,Jan Bundesmann,Angelique Badger,Joshua Xiong,Moist mongol, Frank Fewkes,jason baldrick,Game Master Pro,Andrew Kircher,Preston Mitchell,Chris Kohut,Emarandzeb,Trentin Bergeron,Damon Gallaty,Pleaseworkforonce, Jordan,William Markus,Sidr Dim,Alexander Whittaker,The Next Level,Patrick Valverde,Markus Peham,Daniel Cooper,the Beagles of Neorbus,Marley Moule, - Maximilian Schielke,Johnathan Xavier Hutchinson,Ele,Rita,Randy Ross,John Wick,RedSpaz,cameron cannon,Ian Grau-Fay,Kyle Barrett,Charlotte Wiland`; + Maximilian Schielke,Johnathan Xavier Hutchinson,Ele,Rita,Randy Ross,John Wick,RedSpaz,cameron cannon,Ian Grau-Fay,Kyle Barrett,Charlotte Wiland, + David Kaul,E. Jason Davis,Cyberate,Atenfox,Sea Wolf,Holly Loveless`; const array = supporters .replace(/(?:\r\n|\r|\n)/g, "")