From ac063d4f07b14f10537ae654db7992c098283bbc Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 4 Jun 2022 00:42:16 +0300 Subject: [PATCH 01/29] perf: load jszip dynamically --- index.html | 3 +-- modules/io/export.js | 3 ++- versioning.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 60807aa0..c8320121 100644 --- a/index.html +++ b/index.html @@ -6163,12 +6163,11 @@ - - + diff --git a/modules/io/export.js b/modules/io/export.js index 2389b89e..fded29db 100644 --- a/modules/io/export.js +++ b/modules/io/export.js @@ -75,7 +75,8 @@ async function saveTiles() { return new Promise(async (resolve, reject) => { // download schema const urlSchema = await getMapURL("tiles", {debug: true, fullMap: true}); - const zip = new JSZip(); + await import("../../libs/jszip.min.js"); + const zip = new window.JSZip(); const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); diff --git a/versioning.js b/versioning.js index aa41662b..f60cbf5c 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.84.10"; // generator version, update each time +const version = "1.84.11"; // generator version, update each time { document.title += " v" + version; From 55f8b2a491b1e67f27e9a518dade184854f59b56 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 4 Jun 2022 14:46:27 +0300 Subject: [PATCH 02/29] perf: deferred css load --- index.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index c8320121..8291f9b0 100644 --- a/index.html +++ b/index.html @@ -107,9 +107,10 @@ } } - - - + + + + Date: Sat, 4 Jun 2022 19:48:17 +0300 Subject: [PATCH 03/29] fix: improve adjectives generation rules --- index.html | 3 +- utils/languageUtils.js | 174 +++++++++++++++++++++++++++++++++++++++++ utils/stringUtils.js | 58 -------------- versioning.js | 2 +- 4 files changed, 177 insertions(+), 60 deletions(-) create mode 100644 utils/languageUtils.js diff --git a/index.html b/index.html index 8291f9b0..deca46d7 100644 --- a/index.html +++ b/index.html @@ -6093,7 +6093,8 @@ - + + diff --git a/utils/languageUtils.js b/utils/languageUtils.js new file mode 100644 index 00000000..87f5d67d --- /dev/null +++ b/utils/languageUtils.js @@ -0,0 +1,174 @@ +"use strict"; + +// chars that serve as vowels +const VOWELS = `aeiouyɑ'əøɛœæɶɒɨɪɔɐʊɤɯаоиеёэыуюяàèìòùỳẁȁȅȉȍȕáéíóúýẃőűâêîôûŷŵäëïöüÿẅãẽĩõũỹąęįǫųāēīōūȳăĕĭŏŭǎěǐǒǔȧėȯẏẇạẹịọụỵẉḛḭṵṳ`; +function vowel(c) { + return VOWELS.includes(c); +} + +// remove vowels from the end of the string +function trimVowels(string, minLength = 3) { + while (string.length > minLength && vowel(last(string))) { + string = string.slice(0, -1); + } + return string; +} + +const adjectivizationRules = [ + {name: "guo", probability: 1, condition: new RegExp(" Guo$"), action: noun => noun.slice(0, -4)}, + { + name: "orszag", + probability: 1, + condition: new RegExp("orszag$"), + action: noun => (noun.length < 9 ? noun + "ian" : noun.slice(0, -6)) + }, + { + name: "stan", + probability: 1, + condition: new RegExp("stan$"), + action: noun => (noun.length < 9 ? noun + "i" : trimVowels(noun.slice(0, -4))) + }, + { + name: "land", + probability: 1, + condition: new RegExp("land$"), + action: noun => { + if (noun.length > 9) return noun.slice(0, -4); + const root = trimVowels(noun.slice(0, -4), 0); + if (root.length < 3) return noun + "ic"; + if (root.length < 4) return root + "lish"; + return root + "ish"; + } + }, + { + name: "que", + probability: 1, + condition: new RegExp("que$"), + action: noun => noun.replace(/que$/, "can") + }, + { + name: "a", + probability: 1, + condition: new RegExp("a$"), + action: noun => noun + "n" + }, + { + name: "o", + probability: 1, + condition: new RegExp("o$"), + action: noun => noun.replace(/o$/, "an") + }, + { + name: "u", + probability: 1, + condition: new RegExp("u$"), + action: noun => noun + "an" + }, + { + name: "i", + probability: 1, + condition: new RegExp("i$"), + action: noun => noun + "an" + }, + { + name: "e", + probability: 1, + condition: new RegExp("e$"), + action: noun => noun + "an" + }, + { + name: "ay", + probability: 1, + condition: new RegExp("ay$"), + action: noun => noun + "an" + }, + { + name: "os", + probability: 1, + condition: new RegExp("os$"), + action: noun => { + const root = trimVowels(noun.slice(0, -2), 0); + if (root.length < 4) return noun.slice(0, -1); + return root + "ian"; + } + }, + { + name: "es", + probability: 1, + condition: new RegExp("es$"), + action: noun => { + const root = trimVowels(noun.slice(0, -2), 0); + if (root.length > 7) return noun.slice(0, -1); + return root + "ian"; + } + }, + { + name: "l", + probability: 0.8, + condition: new RegExp("l$"), + action: noun => noun + "ese" + }, + { + name: "n", + probability: 0.8, + condition: new RegExp("n$"), + action: noun => noun + "ese" + }, + { + name: "ad", + probability: 0.8, + condition: new RegExp("ad$"), + action: noun => noun + "ian" + }, + { + name: "an", + probability: 0.8, + condition: new RegExp("an$"), + action: noun => noun + "ian" + }, + { + name: "ish", + probability: 0.25, + condition: new RegExp("^[a-zA-Z]{6}$"), + action: noun => trimVowels(noun.slice(0, -1)) + "ish" + }, + { + name: "an", + probability: 0.5, + condition: new RegExp("^[a-zA-Z]{0-7}$"), + action: noun => trimVowels(noun) + "an" + } +]; + +// get adjective form from noun +function getAdjective(noun) { + for (const rule of adjectivizationRules) { + if (P(rule.probability) && rule.condition.test(noun)) { + return rule.action(noun); + } + } + return noun; // no rule applied, return noun as is +} + +// get ordinal from integer: 1 => 1st +const nth = n => n + (["st", "nd", "rd"][((((n + 90) % 100) - 10) % 10) - 1] || "th"); + +// get two-letters code (abbreviation) from string +function abbreviate(name, restricted = []) { + const parsed = name.replace("Old ", "O ").replace(/[()]/g, ""); // remove Old prefix and parentheses + const words = parsed.split(" "); + const letters = words.join(""); + + let code = words.length === 2 ? words[0][0] + words[1][0] : letters.slice(0, 2); + for (let i = 1; i < letters.length - 1 && restricted.includes(code); i++) { + code = letters[0] + letters[i].toUpperCase(); + } + return code; +} + +// conjunct array: [A,B,C] => "A, B and C" +function list(array) { + if (!Intl.ListFormat) return array.join(", "); + const conjunction = new Intl.ListFormat(window.lang || "en", {style: "long", type: "conjunction"}); + return conjunction.format(array); +} diff --git a/utils/stringUtils.js b/utils/stringUtils.js index eddc88f6..6325d278 100644 --- a/utils/stringUtils.js +++ b/utils/stringUtils.js @@ -13,64 +13,6 @@ function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } -// check if char is vowel or can serve as vowel -function vowel(c) { - return `aeiouyɑ'əøɛœæɶɒɨɪɔɐʊɤɯаоиеёэыуюяàèìòùỳẁȁȅȉȍȕáéíóúýẃőűâêîôûŷŵäëïöüÿẅãẽĩõũỹąęįǫųāēīōūȳăĕĭŏŭǎěǐǒǔȧėȯẏẇạẹịọụỵẉḛḭṵṳ`.includes(c); -} - -// remove vowels from the end of the string -function trimVowels(string) { - while (string.length > 3 && vowel(last(string))) { - string = string.slice(0, -1); - } - return string; -} - -// get adjective form from noun -function getAdjective(string) { - // special cases for some suffixes - if (string.length > 8 && string.slice(-6) === "orszag") return string.slice(0, -6); - if (string.length > 6 && string.slice(-4) === "stan") return string.slice(0, -4); - if (P(0.5) && string.slice(-4) === "land") return string + "ic"; - if (string.slice(-4) === " Guo") string = string.slice(0, -4); - - // don't change is name ends on suffix - if (string.slice(-2) === "an") return string; - if (string.slice(-3) === "ese") return string; - if (string.slice(-1) === "i") return string; - - const end = string.slice(-1); // last letter of string - if (end === "a") return (string += "n"); - if (end === "o") return (string = trimVowels(string) + "an"); - if (vowel(end) || end === "c") return (string += "an"); // ceiuy - if (end === "m" || end === "n") return (string += "ese"); - if (end === "q") return (string += "i"); - return trimVowels(string) + "ian"; -} - -// get ordinal out of integer: 1 => 1st -const nth = n => n + (["st", "nd", "rd"][((((n + 90) % 100) - 10) % 10) - 1] || "th"); - -// get two-letters code (abbreviation) from string -function abbreviate(name, restricted = []) { - const parsed = name.replace("Old ", "O ").replace(/[()]/g, ""); // remove Old prefix and parentheses - const words = parsed.split(" "); - const letters = words.join(""); - - let code = words.length === 2 ? words[0][0] + words[1][0] : letters.slice(0, 2); - for (let i = 1; i < letters.length - 1 && restricted.includes(code); i++) { - code = letters[0] + letters[i].toUpperCase(); - } - return code; -} - -// conjunct array: [A,B,C] => "A, B and C" -function list(array) { - if (!Intl.ListFormat) return array.join(", "); - const conjunction = new Intl.ListFormat(window.lang || "en", {style: "long", type: "conjunction"}); - return conjunction.format(array); -} - // split string into 2 almost equal parts not breaking words function splitInTwo(str) { const half = str.length / 2; diff --git a/versioning.js b/versioning.js index f60cbf5c..09db2364 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.84.11"; // generator version, update each time +const version = "1.84.12"; // generator version, update each time { document.title += " v" + version; From 3694d77b87de5b24cb90e934d42275a7ee001747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gergely=20M=C3=A9sz=C3=A1ros=2C=20Ph=2ED?= Date: Sat, 4 Jun 2022 18:49:51 +0200 Subject: [PATCH 04/29] Detect heightmap based on pack data, convert to warning. (#827) * Detect heightmap based on pack data, convert to warning. * removing childMap alltogether --- modules/submap.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/modules/submap.js b/modules/submap.js index 397518c7..85408487 100644 --- a/modules/submap.js +++ b/modules/submap.js @@ -5,7 +5,7 @@ main function: resample(options); */ window.Submap = (function () { - const isWater = (map, id) => (map.grid.cells.h[map.pack.cells.g[id]] < 20 ? true : false); + const isWater = (pack, id) => pack.cells.h[id] < 20; const inMap = (x, y) => x > 0 && x < graphWidth && y > 0 && y < graphHeight; function resample(parentMap, options) { @@ -126,7 +126,6 @@ window.Submap = (function () { // generatePrecipitation(); stage("Cell cleanup."); reGraph(); - const childMap = {grid, pack}; // remove misclassified cells stage("Define coastline."); @@ -180,9 +179,8 @@ window.Submap = (function () { let d = Infinity; oldChildren.forEach(oid => { // this should be always true, unless some algo modded the height! - if (isWater(parentMap, oid) !== isWater(childMap, id)) { + if (isWater(parentMap.pack, oid) !== isWater(pack, id)) { console.warn(`cell sank because of addLakesInDepressions: ${oid}`); - return; } const [oldpx, oldpy] = oldCells.p[oid]; const nd = distance(projection(oldpx, oldpy)); @@ -197,7 +195,7 @@ window.Submap = (function () { } } - if (isWater(childMap, id) !== isWater(parentMap, oldid)) { + if (isWater(pack, id) !== isWater(parentMap.pack, oldid)) { WARN && console.warn("Type discrepancy detected:", id, oldid, `${pack.cells.t[id]} != ${oldCells.t[oldid]}`); } @@ -360,7 +358,6 @@ window.Submap = (function () { function copyBurgs(parentMap, projection, options) { const cells = pack.cells; - const childMap = {grid, pack}; pack.burgs = parentMap.pack.burgs; // remap burgs to the best new cell @@ -386,7 +383,7 @@ window.Submap = (function () { // already occupied searchFunc = findNearest(isFreeLand, _ => true, 3); - if (isWater(childMap, cityCell) || b.port) + if (isWater(pack, cityCell) || b.port) // burg is in water or port searchFunc = findNearest(isFreeLand, nearCoast, 6); @@ -402,7 +399,7 @@ window.Submap = (function () { [b.x, b.y] = b.port ? getMiddlePoint(newCell, neighbor) : cells.p[newCell]; if (b.port) b.port = cells.f[neighbor]; // copy feature number b.cell = newCell; - if (b.port && !isWater(childMap, neighbor)) console.error("betrayal! negihbor must be water!", b); + if (b.port && !isWater(pack, neighbor)) console.error("betrayal! negihbor must be water!", b); } else { b.cell = cityCell; } From 44e98d853f77cede342542894e4456d07cfdbe71 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sat, 4 Jun 2022 20:10:23 +0300 Subject: [PATCH 05/29] feat: change load from cloud format and order --- index.html | 2821 +++++++++++++++++++++++++++++++++-------- modules/io/cloud.js | 16 +- modules/ui/options.js | 48 +- versioning.js | 2 +- 4 files changed, 2362 insertions(+), 525 deletions(-) diff --git a/index.html b/index.html index deca46d7..2cd104f0 100644 --- a/index.html +++ b/index.html @@ -110,7 +110,12 @@ - + - - - + + + - + - - - + + + - + - + @@ -325,18 +394,36 @@ > ◄ - - -

Layers preset:

- @@ -365,8 +452,13 @@ onclick="removePreset()" > -

Displayed layers and layers order:

-
    +

    + Displayed layers and layers order: +

    +
    • View mode:

      - - + +
-

Style preset:

+

+ Style preset: +

+ - @@ -748,31 +859,77 @@ @@ -917,10 +1077,20 @@ - + @@ -1166,21 +1336,45 @@ @@ -1248,7 +1442,9 @@
-

Map settings (new map to apply):

+

+ Map settings (new map to apply): +

+ Ensure the element visibility is toggled on!
- +
Density - + .4
State Size - +
Province Size - +
Burg Size - +
px - + - + @@ -1459,7 +1689,9 @@
- +
- + Map seed @@ -1283,7 +1487,9 @@
Points number @@ -1300,7 +1506,14 @@ Map name - + @@ -1313,8 +1526,23 @@ Year and era - - + + @@ -1430,7 +1658,9 @@
-

Generator settings:

+

+ Generator settings: +

@@ -1473,7 +1705,9 @@ - + @@ -1658,10 +1905,18 @@
Interface size @@ -1625,10 +1859,23 @@ value="1" /> max - + - +
- -
@@ -1670,43 +1925,99 @@

Click to configure:

- - - - - - - + + + + + + + - - + +

Click to overview:

- - - - - + + + + +

Click to regenerate:

- - + + - - + + - + - + - +

Click to add:

- - - + + + - +

Click to create a new map:

@@ -1757,27 +2102,60 @@
-
+
-
+
Depressions filling max iterations:
- - + +
Depression depth threshold:
- - + +

Statistics:

- Land cells: 0 Mean height: 0 + Land cells: 0 + Mean height: 0

Cell info:

@@ -1790,30 +2168,54 @@

- Fantasy Map Generator is a free - open source tool which procedurally generates - fantasy maps. You may use auto-generated maps as they are, edit them or even create a new map from scratch. Check out the - Quick start, - Q&A, - Video tutorial, and - hotkeys for guidance. + Fantasy Map Generator is a + free + open source + tool which procedurally generates fantasy maps. You may use auto-generated maps as they are, edit them or + even create a new map from scratch. Check out the + Quick start, Q&A, + Video tutorial, and + hotkeys for + guidance.

Join our Discord server and - Reddit community to ask questions, get help and share maps. + Reddit community to ask + questions, get help and share maps.

- The project is under active development. Creator and main maintainer: Azgaar. To track the development progress see the - devboard. For older versions see the - changelog. Please report bugs - here. You can also contact me directly via - email. + The project is under active development. Creator and main maintainer: Azgaar. To track the development + progress see the + devboard. For older + versions see the + changelog. + Please report bugs + here. You can also + contact me directly via email.

-
- +
+
-

Special thanks to all supporters on Patreon!

+

+ Special thanks to + all supporters on Patreon! +

@@ -1857,22 +2269,33 @@ />
  • - Post to Tumblr
  • - Pin it
  • - Submit to Reddit
  • - Join Discord server + Join Discord server
  • @@ -1889,7 +2312,9 @@
    - +
    @@ -1901,8 +2326,13 @@
    @@ -1910,7 +2340,8 @@
    @@ -1927,7 +2358,16 @@
    @@ -1946,8 +2386,14 @@
    Meridian length:
    px = - - + +
    Coords:
    @@ -2013,15 +2459,28 @@ @@ -2031,21 +2490,45 @@ - +
    @@ -2260,60 +2798,135 @@
    Size:
    - +
    - - + + + + + + - - - - - + + + + + + + + + + + + + + +
    - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2326,14 +2939,30 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -2343,26 +2972,66 @@
    - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2378,7 +3047,9 @@ - + + + @@ -2413,22 +3084,39 @@ - +