diff --git a/index.html b/index.html
index 9dfb924a..c7887d63 100644
--- a/index.html
+++ b/index.html
@@ -7771,7 +7771,6 @@
-
@@ -7783,33 +7782,39 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
+
+
+
+
+
+
-
+
@@ -7829,7 +7834,7 @@
-
+
diff --git a/modules/activeZooming.js b/modules/activeZooming.js
new file mode 100644
index 00000000..20097bbb
--- /dev/null
+++ b/modules/activeZooming.js
@@ -0,0 +1,92 @@
+window.handleZoom = function (isScaleChanged, isPositionChanged) {
+ viewbox.attr("transform", `translate(${viewX} ${viewY}) scale(${scale})`);
+
+ if (isPositionChanged) drawCoordinates();
+
+ if (isScaleChanged) {
+ invokeActiveZooming();
+ drawScaleBar(scale);
+ }
+
+ // zoom image converter overlay
+ if (customization === 1) {
+ const canvas = document.getElementById("canvas");
+ if (!canvas || canvas.style.opacity === "0") return;
+
+ const img = document.getElementById("imageToConvert");
+ if (!img) return;
+
+ const ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.setTransform(scale, 0, 0, scale, viewX, viewY);
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
+ }
+};
+
+// active zooming feature
+export function invokeActiveZooming() {
+ if (coastline.select("#sea_island").size() && +coastline.select("#sea_island").attr("auto-filter")) {
+ // toggle shade/blur filter for coatline on zoom
+ const filter = scale > 1.5 && scale <= 2.6 ? null : scale > 2.6 ? "url(#blurFilter)" : "url(#dropShadow)";
+ coastline.select("#sea_island").attr("filter", filter);
+ }
+
+ // rescale labels on zoom
+ if (labels.style("display") !== "none") {
+ labels.selectAll("g").each(function () {
+ if (this.id === "burgLabels") return;
+ const desired = +this.dataset.size;
+ const relative = Math.max(rn((desired + desired / scale) / 2, 2), 1);
+ if (rescaleLabels.checked) this.setAttribute("font-size", relative);
+
+ const hidden = hideLabels.checked && (relative * scale < 6 || relative * scale > 60);
+ if (hidden) this.classList.add("hidden");
+ else this.classList.remove("hidden");
+ });
+ }
+
+ // rescale emblems on zoom
+ if (emblems.style("display") !== "none") {
+ emblems.selectAll("g").each(function () {
+ const size = this.getAttribute("font-size") * scale;
+ const hidden = hideEmblems.checked && (size < 25 || size > 300);
+ if (hidden) this.classList.add("hidden");
+ else this.classList.remove("hidden");
+ if (!hidden && window.COArenderer && this.children.length && !this.children[0].getAttribute("href"))
+ renderGroupCOAs(this);
+ });
+ }
+
+ // turn off ocean pattern if scale is big (improves performance)
+ oceanPattern
+ .select("rect")
+ .attr("fill", scale > 10 ? "#fff" : "url(#oceanic)")
+ .attr("opacity", scale > 10 ? 0.2 : null);
+
+ // change states halo width
+ if (!customization) {
+ const desired = +statesHalo.attr("data-width");
+ const haloSize = rn(desired / scale ** 0.8, 2);
+ statesHalo.attr("stroke-width", haloSize).style("display", haloSize > 0.1 ? "block" : "none");
+ }
+
+ // rescale map markers
+ +markers.attr("rescale") &&
+ pack.markers?.forEach(marker => {
+ const {i, x, y, size = 30, hidden} = marker;
+ const el = !hidden && document.getElementById(`marker${i}`);
+ if (!el) return;
+
+ const zoomedSize = Math.max(rn(size / 5 + 24 / scale, 2), 1);
+ el.setAttribute("width", zoomedSize);
+ el.setAttribute("height", zoomedSize);
+ el.setAttribute("x", rn(x - zoomedSize / 2, 1));
+ el.setAttribute("y", rn(y - zoomedSize, 1));
+ });
+
+ // rescale rulers to have always the same size
+ if (ruler.style("display") !== "none") {
+ const size = rn((10 / scale ** 0.3) * 2, 2);
+ ruler.selectAll("text").attr("font-size", size);
+ }
+}
diff --git a/modules/biomes.js b/modules/biomes.js
new file mode 100644
index 00000000..045e0310
--- /dev/null
+++ b/modules/biomes.js
@@ -0,0 +1,76 @@
+window.Biomes = (function () {
+ const getDefault = () => {
+ const name = [
+ "Marine",
+ "Hot desert",
+ "Cold desert",
+ "Savanna",
+ "Grassland",
+ "Tropical seasonal forest",
+ "Temperate deciduous forest",
+ "Tropical rainforest",
+ "Temperate rainforest",
+ "Taiga",
+ "Tundra",
+ "Glacier",
+ "Wetland"
+ ];
+
+ const color = [
+ "#466eab",
+ "#fbe79f",
+ "#b5b887",
+ "#d2d082",
+ "#c8d68f",
+ "#b6d95d",
+ "#29bc56",
+ "#7dcb35",
+ "#409c43",
+ "#4b6b32",
+ "#96784b",
+ "#d5e7eb",
+ "#0b9131"
+ ];
+ const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12];
+ const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150];
+ const icons = [
+ {},
+ {dune: 3, cactus: 6, deadTree: 1},
+ {dune: 9, deadTree: 1},
+ {acacia: 1, grass: 9},
+ {grass: 1},
+ {acacia: 8, palm: 1},
+ {deciduous: 1},
+ {acacia: 5, palm: 3, deciduous: 1, swamp: 1},
+ {deciduous: 6, swamp: 1},
+ {conifer: 1},
+ {grass: 1},
+ {},
+ {swamp: 1}
+ ];
+ const cost = [10, 200, 150, 60, 50, 70, 70, 80, 90, 200, 1000, 5000, 150]; // biome movement cost
+ const biomesMartix = [
+ // hot ↔ cold [>19°C; <-4°C]; dry ↕ wet
+ new Uint8Array([1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10]),
+ new Uint8Array([3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 9, 9, 9, 10, 10, 10]),
+ new Uint8Array([5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 9, 9, 9, 9, 10, 10, 10]),
+ new Uint8Array([5, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10]),
+ new Uint8Array([7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10])
+ ];
+
+ // parse icons weighted array into a simple array
+ for (let i = 0; i < icons.length; i++) {
+ const parsed = [];
+ for (const icon in icons[i]) {
+ for (let j = 0; j < icons[i][icon]; j++) {
+ parsed.push(icon);
+ }
+ }
+ icons[i] = parsed;
+ }
+
+ return {i: d3.range(0, name.length), name, color, biomesMartix, habitability, iconsDensity, icons, cost};
+ };
+
+ return {getDefault};
+})();
diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js
index e9f942e5..04ca2648 100644
--- a/modules/burgs-and-states.js
+++ b/modules/burgs-and-states.js
@@ -1,4 +1,6 @@
-"use strict";
+import {TIME} from "/src/config/logging";
+import {findCell} from "/src/utils/graphUtils";
+import {layerIsOn} from "./ui/layers";
window.BurgsAndStates = (function () {
const generate = function () {
diff --git a/modules/coa-generator.js b/modules/coa-generator.js
index 9491cc0d..56deb52b 100644
--- a/modules/coa-generator.js
+++ b/modules/coa-generator.js
@@ -1,5 +1,3 @@
-"use strict";
-
window.COA = (function () {
const tinctures = {
field: {metals: 3, colours: 4, stains: +P(0.03), patterns: 1},
@@ -305,7 +303,19 @@ window.COA = (function () {
Highland: {tower: 1, raven: 1, wolfHeadErased: 1, wolfPassant: 1, goat: 1, axe: 1},
River: {tower: 1, garb: 1, rake: 1, boat: 1, pike: 2, bullHeadCaboshed: 1, apple: 1, plough: 1},
Lake: {cancer: 2, escallop: 1, pike: 2, heron: 1, boat: 1, boat2: 2},
- Nomadic: {pot: 1, buckle: 1, wheel: 2, sabre: 2, sabresCrossed: 1, bow: 2, arrow: 1, horseRampant: 1, horseSalient: 1, crescent: 1, camel: 3},
+ Nomadic: {
+ pot: 1,
+ buckle: 1,
+ wheel: 2,
+ sabre: 2,
+ sabresCrossed: 1,
+ bow: 2,
+ arrow: 1,
+ horseRampant: 1,
+ horseSalient: 1,
+ crescent: 1,
+ camel: 3
+ },
Hunting: {
bugleHorn: 2,
bugleHorn2: 1,
@@ -322,7 +332,19 @@ window.COA = (function () {
// selection based on type
City: {key: 3, bell: 2, lute: 1, tower: 1, castle: 1, mallet: 1, cannon: 1, anvil: 1},
Capital: {crown: 2, orb: 1, lute: 1, castle: 3, tower: 1, crown2: 2},
- Сathedra: {chalice: 1, orb: 1, crosier: 2, lamb: 1, monk: 2, angel: 3, crossLatin: 2, crossPatriarchal: 1, crossOrthodox: 1, crossCalvary: 1, agnusDei: 3},
+ Сathedra: {
+ chalice: 1,
+ orb: 1,
+ crosier: 2,
+ lamb: 1,
+ monk: 2,
+ angel: 3,
+ crossLatin: 2,
+ crossPatriarchal: 1,
+ crossOrthodox: 1,
+ crossCalvary: 1,
+ agnusDei: 3
+ },
// specific cases
natural: {fountain: "azure", garb: "or", raven: "sable"}, // charges to mainly use predefined colours
sinister: [
@@ -508,7 +530,22 @@ window.COA = (function () {
},
// charges
inescutcheon: {e: 4, jln: 1},
- mascle: {e: 15, abcdefgzi: 3, beh: 3, bdefh: 4, acegi: 1, kn: 3, joe: 2, abc: 3, jlh: 8, jleh: 1, df: 3, abcpqh: 4, pqe: 3, eknpq: 3},
+ mascle: {
+ e: 15,
+ abcdefgzi: 3,
+ beh: 3,
+ bdefh: 4,
+ acegi: 1,
+ kn: 3,
+ joe: 2,
+ abc: 3,
+ jlh: 8,
+ jleh: 1,
+ df: 3,
+ abcpqh: 4,
+ pqe: 3,
+ eknpq: 3
+ },
lionRampant: {e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1},
lionPassant: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1},
wolfPassant: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1},
@@ -681,18 +718,41 @@ window.COA = (function () {
const coa = {t1};
let charge = P(usedPattern ? 0.5 : 0.93) ? true : false; // 80% for charge
- const linedOrdinary = (charge && P(0.3)) || P(0.5) ? (parent?.ordinaries && P(kinship) ? parent.ordinaries[0].ordinary : rw(ordinaries.lined)) : null;
+ const linedOrdinary =
+ (charge && P(0.3)) || P(0.5)
+ ? parent?.ordinaries && P(kinship)
+ ? parent.ordinaries[0].ordinary
+ : rw(ordinaries.lined)
+ : null;
const ordinary = (!charge && P(0.65)) || P(0.3) ? (linedOrdinary ? linedOrdinary : rw(ordinaries.straight)) : null; // 36% for ordinary
const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary);
- const divisioned = rareDivided ? P(0.03) : charge && ordinary ? P(0.03) : charge ? P(0.3) : ordinary ? P(0.7) : P(0.995); // 33% for division
- const division = divisioned ? (parent?.division && P(kinship - 0.1) ? parent.division.division : rw(divisions.variants)) : null;
+ const divisioned = rareDivided
+ ? P(0.03)
+ : charge && ordinary
+ ? P(0.03)
+ : charge
+ ? P(0.3)
+ : ordinary
+ ? P(0.7)
+ : P(0.995); // 33% for division
+ const division = divisioned
+ ? parent?.division && P(kinship - 0.1)
+ ? parent.division.division
+ : rw(divisions.variants)
+ : null;
if (charge)
- charge = parent?.charges && P(kinship - 0.1) ? parent.charges[0].charge : type && type !== "Generic" && P(0.2) ? rw(charges[type]) : selectCharge();
+ charge =
+ parent?.charges && P(kinship - 0.1)
+ ? parent.charges[0].charge
+ : type && type !== "Generic" && P(0.2)
+ ? rw(charges[type])
+ : selectCharge();
if (division) {
const t = getTincture("division", usedTinctures, P(0.98) ? coa.t1 : null);
coa.division = {division, t};
- if (divisions[division]) coa.division.line = usedPattern || (ordinary && P(0.7)) ? "straight" : rw(divisions[division]);
+ if (divisions[division])
+ coa.division.line = usedPattern || (ordinary && P(0.7)) ? "straight" : rw(divisions[division]);
}
if (ordinary) {
@@ -768,7 +828,14 @@ window.COA = (function () {
// counterchanged, 40%
else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(0.8)) {
// place 2 charges in division standard positions
- const [p1, p2] = division === "perPale" ? ["p", "q"] : division === "perFess" ? ["k", "n"] : division === "perBend" ? ["l", "m"] : ["j", "o"]; // perBendSinister
+ const [p1, p2] =
+ division === "perPale"
+ ? ["p", "q"]
+ : division === "perFess"
+ ? ["k", "n"]
+ : division === "perBend"
+ ? ["l", "m"]
+ : ["j", "o"]; // perBendSinister
coa.charges[0].p = p1;
const charge = selectCharge(charges.single);
diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js
index b1072c22..8eff92a4 100644
--- a/modules/cultures-generator.js
+++ b/modules/cultures-generator.js
@@ -1,4 +1,4 @@
-"use strict";
+import {TIME} from "/src/config/logging";
window.Cultures = (function () {
let cells;
diff --git a/modules/define-globals.js b/modules/define-globals.js
new file mode 100644
index 00000000..4b7ab2b2
--- /dev/null
+++ b/modules/define-globals.js
@@ -0,0 +1,33 @@
+"use strict";
+// define global vabiable, each to be refactored and de-globalized 1-by-1
+
+let grid = {}; // initial graph based on jittered square grid and data
+let pack = {}; // packed graph and data
+let seed;
+let mapId;
+let mapHistory = [];
+let elSelected;
+
+let notes = [];
+let customization = 0;
+
+let rulers;
+let biomesData;
+let nameBases;
+
+let color;
+let lineGen;
+
+// defined in main.js
+let graphWidth;
+let graphHeight;
+let svgWidth;
+let svgHeight;
+
+let options = {};
+let mapCoordinates = {};
+let populationRate;
+let distanceScale;
+let urbanization;
+let urbanDensity;
+let statesNeutral;
diff --git a/modules/define-svg.js b/modules/define-svg.js
new file mode 100644
index 00000000..5f59577e
--- /dev/null
+++ b/modules/define-svg.js
@@ -0,0 +1,177 @@
+"use strict";
+// temporary define svg elements as globals
+
+let svg,
+ defs,
+ viewbox,
+ scaleBar,
+ legend,
+ ocean,
+ oceanLayers,
+ oceanPattern,
+ lakes,
+ landmass,
+ texture,
+ terrs,
+ biomes,
+ cells,
+ gridOverlay,
+ coordinates,
+ compass,
+ rivers,
+ terrain,
+ relig,
+ cults,
+ regions,
+ statesBody,
+ statesHalo,
+ provs,
+ zones,
+ borders,
+ stateBorders,
+ provinceBorders,
+ routes,
+ roads,
+ trails,
+ searoutes,
+ temperature,
+ coastline,
+ ice,
+ prec,
+ population,
+ emblems,
+ labels,
+ icons,
+ burgLabels,
+ burgIcons,
+ anchors,
+ armies,
+ markers,
+ fogging,
+ ruler,
+ debug;
+
+function defineSvg(width, height) {
+ // append svg layers (in default order)
+ svg = d3.select("#map");
+ defs = svg.select("#deftemp");
+ viewbox = svg.select("#viewbox");
+ scaleBar = svg.select("#scaleBar");
+ legend = svg.append("g").attr("id", "legend");
+ ocean = viewbox.append("g").attr("id", "ocean");
+ oceanLayers = ocean.append("g").attr("id", "oceanLayers");
+ oceanPattern = ocean.append("g").attr("id", "oceanPattern");
+ lakes = viewbox.append("g").attr("id", "lakes");
+ landmass = viewbox.append("g").attr("id", "landmass");
+ texture = viewbox.append("g").attr("id", "texture");
+ terrs = viewbox.append("g").attr("id", "terrs");
+ biomes = viewbox.append("g").attr("id", "biomes");
+ cells = viewbox.append("g").attr("id", "cells");
+ gridOverlay = viewbox.append("g").attr("id", "gridOverlay");
+ coordinates = viewbox.append("g").attr("id", "coordinates");
+ compass = viewbox.append("g").attr("id", "compass");
+ rivers = viewbox.append("g").attr("id", "rivers");
+ terrain = viewbox.append("g").attr("id", "terrain");
+ relig = viewbox.append("g").attr("id", "relig");
+ cults = viewbox.append("g").attr("id", "cults");
+ regions = viewbox.append("g").attr("id", "regions");
+ statesBody = regions.append("g").attr("id", "statesBody");
+ statesHalo = regions.append("g").attr("id", "statesHalo");
+ provs = viewbox.append("g").attr("id", "provs");
+ zones = viewbox.append("g").attr("id", "zones").style("display", "none");
+ borders = viewbox.append("g").attr("id", "borders");
+ stateBorders = borders.append("g").attr("id", "stateBorders");
+ provinceBorders = borders.append("g").attr("id", "provinceBorders");
+ routes = viewbox.append("g").attr("id", "routes");
+ roads = routes.append("g").attr("id", "roads");
+ trails = routes.append("g").attr("id", "trails");
+ searoutes = routes.append("g").attr("id", "searoutes");
+ temperature = viewbox.append("g").attr("id", "temperature");
+ coastline = viewbox.append("g").attr("id", "coastline");
+ ice = viewbox.append("g").attr("id", "ice").style("display", "none");
+ prec = viewbox.append("g").attr("id", "prec").style("display", "none");
+ population = viewbox.append("g").attr("id", "population");
+ emblems = viewbox.append("g").attr("id", "emblems").style("display", "none");
+ labels = viewbox.append("g").attr("id", "labels");
+ icons = viewbox.append("g").attr("id", "icons");
+ burgIcons = icons.append("g").attr("id", "burgIcons");
+ anchors = icons.append("g").attr("id", "anchors");
+ armies = viewbox.append("g").attr("id", "armies").style("display", "none");
+ markers = viewbox.append("g").attr("id", "markers");
+ fogging = viewbox
+ .append("g")
+ .attr("id", "fogging-cont")
+ .attr("mask", "url(#fog)")
+ .append("g")
+ .attr("id", "fogging")
+ .style("display", "none");
+ ruler = viewbox.append("g").attr("id", "ruler").style("display", "none");
+ debug = viewbox.append("g").attr("id", "debug");
+
+ // lake and coast groups
+ lakes.append("g").attr("id", "freshwater");
+ lakes.append("g").attr("id", "salt");
+ lakes.append("g").attr("id", "sinkhole");
+ lakes.append("g").attr("id", "frozen");
+ lakes.append("g").attr("id", "lava");
+ lakes.append("g").attr("id", "dry");
+ coastline.append("g").attr("id", "sea_island");
+ coastline.append("g").attr("id", "lake_island");
+
+ labels.append("g").attr("id", "states");
+ labels.append("g").attr("id", "addedLabels");
+
+ burgLabels = labels.append("g").attr("id", "burgLabels");
+ burgIcons.append("g").attr("id", "cities");
+ burgLabels.append("g").attr("id", "cities");
+ anchors.append("g").attr("id", "cities");
+
+ burgIcons.append("g").attr("id", "towns");
+ burgLabels.append("g").attr("id", "towns");
+ anchors.append("g").attr("id", "towns");
+
+ // population groups
+ population.append("g").attr("id", "rural");
+ population.append("g").attr("id", "urban");
+
+ // emblem groups
+ emblems.append("g").attr("id", "burgEmblems");
+ emblems.append("g").attr("id", "provinceEmblems");
+ emblems.append("g").attr("id", "stateEmblems");
+
+ // fogging
+ fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
+ fogging
+ .append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", "100%")
+ .attr("height", "100%")
+ .attr("fill", "#e8f0f6")
+ .attr("filter", "url(#splotch)");
+
+ // assign events separately as not a viewbox child
+ scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => editUnits());
+
+ legend
+ .on("mousemove", () => tip("Drag to change the position. Click to hide the legend"))
+ .on("click", () => clearLegend());
+
+ landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
+
+ oceanPattern
+ .append("rect")
+ .attr("fill", "url(#oceanic)")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", width)
+ .attr("height", height);
+
+ oceanLayers
+ .append("rect")
+ .attr("id", "oceanBase")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", width)
+ .attr("height", height);
+}
diff --git a/modules/dynamic/editors/states-editor.js b/modules/dynamic/editors/states-editor.js
index b16246fd..5299be21 100644
--- a/modules/dynamic/editors/states-editor.js
+++ b/modules/dynamic/editors/states-editor.js
@@ -422,8 +422,8 @@ function editStateName(state) {
position: {my: "center", at: "center", of: "svg"}
});
- if (modules.editStateName) return;
- modules.editStateName = true;
+ if (fmg.modules.editStateName) return;
+ fmg.modules.editStateName = true;
// add listeners
byId("stateNameEditorShortCulture").on("click", regenerateShortNameCuture);
diff --git a/modules/dynamic/heightmap-selection.js b/modules/dynamic/heightmap-selection.js
index 9b78d118..0f44f74f 100644
--- a/modules/dynamic/heightmap-selection.js
+++ b/modules/dynamic/heightmap-selection.js
@@ -262,7 +262,7 @@ function getName(id) {
}
function getGraph(currentGraph) {
- const newGraph = shouldRegenerateGrid(currentGraph) ? generateGrid() : deepCopy(currentGraph);
+ const newGraph = shouldRegenerateGrid(currentGraph) ? generateGrid() : structuredClone(currentGraph);
delete newGraph.cells.h;
return newGraph;
}
diff --git a/modules/heightmap-generator.js b/modules/heightmap-generator.js
index 66696953..1e8c2567 100644
--- a/modules/heightmap-generator.js
+++ b/modules/heightmap-generator.js
@@ -1,4 +1,6 @@
-"use strict";
+import {TIME} from "/src/config/logging";
+import {createTypedArray} from "/src/utils";
+import {findGridCell} from "/src/utils/graphUtils";
window.HeightmapGenerator = (function () {
let grid = null;
@@ -388,8 +390,12 @@ window.HeightmapGenerator = (function () {
const vert = direction === "vertical";
const startX = vert ? Math.floor(Math.random() * graphWidth * 0.4 + graphWidth * 0.3) : 5;
const startY = vert ? 5 : Math.floor(Math.random() * graphHeight * 0.4 + graphHeight * 0.3);
- const endX = vert ? Math.floor(graphWidth - startX - graphWidth * 0.1 + Math.random() * graphWidth * 0.2) : graphWidth - 5;
- const endY = vert ? graphHeight - 5 : Math.floor(graphHeight - startY - graphHeight * 0.1 + Math.random() * graphHeight * 0.2);
+ const endX = vert
+ ? Math.floor(graphWidth - startX - graphWidth * 0.1 + Math.random() * graphWidth * 0.2)
+ : graphWidth - 5;
+ const endY = vert
+ ? graphHeight - 5
+ : Math.floor(graphHeight - startY - graphHeight * 0.1 + Math.random() * graphHeight * 0.2);
const start = findGridCell(startX, startY, grid);
const end = findGridCell(endX, endY, grid);
diff --git a/modules/lakes.js b/modules/lakes.js
index 093ff84f..fa6427b6 100644
--- a/modules/lakes.js
+++ b/modules/lakes.js
@@ -1,5 +1,3 @@
-"use strict";
-
window.Lakes = (function () {
const setClimateData = function (h) {
const cells = pack.cells;
@@ -12,7 +10,10 @@ window.Lakes = (function () {
f.flux = f.shoreline.reduce((acc, c) => acc + grid.cells.prec[cells.g[c]], 0);
// temperature and evaporation to detect closed lakes
- f.temp = f.cells < 6 ? grid.cells.temp[cells.g[f.firstCell]] : rn(d3.mean(f.shoreline.map(c => grid.cells.temp[cells.g[c]])), 1);
+ f.temp =
+ f.cells < 6
+ ? grid.cells.temp[cells.g[f.firstCell]]
+ : rn(d3.mean(f.shoreline.map(c => grid.cells.temp[cells.g[c]])), 1);
const height = (f.height - 18) ** heightExponentInput.value; // height in meters
const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp); // based on Penman formula, [1-11]
f.evaporation = rn(evaporation * f.cells);
diff --git a/modules/markers-generator.js b/modules/markers-generator.js
index 6845c815..d7a12ca1 100644
--- a/modules/markers-generator.js
+++ b/modules/markers-generator.js
@@ -1,4 +1,4 @@
-"use strict";
+import {TIME} from "/src/config/logging";
window.Markers = (function () {
let config = [];
@@ -20,6 +20,7 @@ window.Markers = (function () {
list: function to select candidates
add: function to add marker legend
*/
+ // prettier-ignore
return [
{type: "volcanoes", icon: "🌋", dx: 52, px: 13, min: 10, each: 500, multiplier: 1, list: listVolcanoes, add: addVolcano},
{type: "hot-springs", icon: "♨️", dy: 52, min: 30, each: 1200, multiplier: 1, list: listHotSprings, add: addHotSpring},
@@ -199,7 +200,13 @@ window.Markers = (function () {
function listBridges({cells, burgs}) {
const meanFlux = d3.mean(cells.fl.filter(fl => fl));
return cells.i.filter(
- i => !occupied[i] && cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux
+ i =>
+ !occupied[i] &&
+ cells.burg[i] &&
+ cells.t[i] !== 1 &&
+ burgs[cells.burg[i]].population > 20 &&
+ cells.r[i] &&
+ cells.fl[i] > meanFlux
);
}
@@ -441,7 +448,21 @@ window.Markers = (function () {
"rat tails",
"pig ears"
];
- const types = ["hot", "cold", "fire", "ice", "smoky", "misty", "shiny", "sweet", "bitter", "salty", "sour", "sparkling", "smelly"];
+ const types = [
+ "hot",
+ "cold",
+ "fire",
+ "ice",
+ "smoky",
+ "misty",
+ "shiny",
+ "sweet",
+ "bitter",
+ "salty",
+ "sour",
+ "sparkling",
+ "smelly"
+ ];
const drinks = [
"wine",
"brandy",
@@ -469,7 +490,11 @@ window.Markers = (function () {
const typeName = P(0.3) ? "inn" : "tavern";
const isAnimalThemed = P(0.7);
const animal = ra(animals);
- const name = isAnimalThemed ? (P(0.6) ? ra(colors) + " " + animal : ra(adjectives) + " " + animal) : ra(adjectives) + " " + capitalize(typeName);
+ const name = isAnimalThemed
+ ? P(0.6)
+ ? ra(colors) + " " + animal
+ : ra(adjectives) + " " + animal
+ : ra(adjectives) + " " + capitalize(typeName);
const meal = isAnimalThemed && P(0.3) ? animal : ra(courses);
const course = `${ra(methods)} ${meal}`.toLowerCase();
const drink = `${P(0.5) ? ra(types) : ra(colors)} ${ra(drinks)}`.toLowerCase();
@@ -478,18 +503,26 @@ window.Markers = (function () {
}
function listLighthouses({cells}) {
- return cells.i.filter(i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.road[c]));
+ return cells.i.filter(
+ i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.road[c])
+ );
}
function addLighthouse(id, cell) {
const {cells} = pack;
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]);
- notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to serve as a beacon for ships in the open sea`});
+ notes.push({
+ id,
+ name: getAdjective(proper) + " Lighthouse" + name,
+ legend: `A lighthouse to serve as a beacon for ships in the open sea`
+ });
}
function listWaterfalls({cells}) {
- return cells.i.filter(i => cells.r[i] && !occupied[i] && cells.h[i] >= 50 && cells.c[i].some(c => cells.h[c] < 40 && cells.r[c]));
+ return cells.i.filter(
+ i => cells.r[i] && !occupied[i] && cells.h[i] >= 50 && cells.c[i].some(c => cells.h[c] < 40 && cells.r[c])
+ );
}
function addWaterfall(id, cell) {
@@ -509,7 +542,9 @@ window.Markers = (function () {
}
function listBattlefields({cells}) {
- return cells.i.filter(i => !occupied[i] && cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25);
+ return cells.i.filter(
+ i => !occupied[i] && cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25
+ );
}
function addBattlefield(id, cell) {
@@ -555,7 +590,9 @@ window.Markers = (function () {
}
function listSeaMonsters({cells, features}) {
- return cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i] && features[cells.f[i]].type === "ocean");
+ return cells.i.filter(
+ i => !occupied[i] && cells.h[i] < 20 && cells.road[i] && features[cells.f[i]].type === "ocean"
+ );
}
function addSeaMonster(id, cell) {
@@ -589,7 +626,17 @@ window.Markers = (function () {
"horrifying",
"feared"
];
- const subjects = ["Locals", "Elders", "Inscriptions", "Tipplers", "Legends", "Whispers", "Rumors", "Journeying folk", "Tales"];
+ const subjects = [
+ "Locals",
+ "Elders",
+ "Inscriptions",
+ "Tipplers",
+ "Legends",
+ "Whispers",
+ "Rumors",
+ "Journeying folk",
+ "Tales"
+ ];
const species = [
"Ogre",
"Troll",
@@ -625,13 +672,21 @@ window.Markers = (function () {
const monster = ra(species);
const toponym = Names.getCulture(cells.culture[cell]);
const name = `${toponym} ${monster}`;
- const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`;
+ const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra(
+ modusOperandi
+ )}`;
notes.push({id, name, legend});
}
// Sacred mountains spawn on lonely mountains
function listSacredMountains({cells}) {
- return cells.i.filter(i => !occupied[i] && cells.h[i] >= 70 && cells.c[i].some(c => cells.culture[c]) && cells.c[i].every(c => cells.h[c] < 60));
+ return cells.i.filter(
+ i =>
+ !occupied[i] &&
+ cells.h[i] >= 70 &&
+ cells.c[i].some(c => cells.culture[c]) &&
+ cells.c[i].every(c => cells.h[c] < 60)
+ );
}
function addSacredMountain(id, cell) {
@@ -674,7 +729,9 @@ window.Markers = (function () {
// Sacred palm groves spawn on oasises
function listSacredPalmGroves({cells}) {
- return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 1 && cells.pop[i] > 1 && cells.road[i]);
+ return cells.i.filter(
+ i => !occupied[i] && cells.culture[i] && cells.biome[i] === 1 && cells.pop[i] > 1 && cells.road[i]
+ );
}
function addSacredPalmGrove(id, cell) {
@@ -765,7 +822,20 @@ window.Markers = (function () {
function addStatue(id, cell) {
const {cells} = pack;
- const variants = ["Statue", "Obelisk", "Monument", "Column", "Monolith", "Pillar", "Megalith", "Stele", "Runestone", "Sculpture", "Effigy", "Idol"];
+ const variants = [
+ "Statue",
+ "Obelisk",
+ "Monument",
+ "Column",
+ "Monolith",
+ "Pillar",
+ "Megalith",
+ "Stele",
+ "Runestone",
+ "Sculpture",
+ "Effigy",
+ "Idol"
+ ];
const scripts = {
cypriot: "𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿 ",
geez: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጰጸፀፈፐ ",
@@ -820,7 +890,16 @@ window.Markers = (function () {
}
function addCircuses(id, cell) {
- const adjectives = ["Fantastical", "Wonderous", "Incomprehensible", "Magical", "Extraordinary", "Unmissable", "World-famous", "Breathtaking"];
+ const adjectives = [
+ "Fantastical",
+ "Wonderous",
+ "Incomprehensible",
+ "Magical",
+ "Extraordinary",
+ "Unmissable",
+ "World-famous",
+ "Breathtaking"
+ ];
const adjective = ra(adjectives);
const name = `Travelling ${adjective} Circus`;
@@ -932,8 +1011,26 @@ window.Markers = (function () {
function addDances(id, cell) {
const {cells, burgs} = pack;
const burgName = burgs[cells.burg[cell]].name;
- const socialTypes = ["gala", "dance", "performance", "ball", "soiree", "jamboree", "exhibition", "carnival", "festival", "jubilee"];
- const people = ["great and the good", "nobility", "local elders", "foreign dignitaries", "spiritual leaders", "suspected revolutionaries"];
+ const socialTypes = [
+ "gala",
+ "dance",
+ "performance",
+ "ball",
+ "soiree",
+ "jamboree",
+ "exhibition",
+ "carnival",
+ "festival",
+ "jubilee"
+ ];
+ const people = [
+ "great and the good",
+ "nobility",
+ "local elders",
+ "foreign dignitaries",
+ "spiritual leaders",
+ "suspected revolutionaries"
+ ];
const socialType = ra(socialTypes);
const name = `${burgName} ${socialType}`;
diff --git a/modules/military-generator.js b/modules/military-generator.js
index 648f5637..7990fe0e 100644
--- a/modules/military-generator.js
+++ b/modules/military-generator.js
@@ -1,4 +1,4 @@
-"use strict";
+import {TIME} from "/src/config/logging";
window.Military = (function () {
const generate = function () {
@@ -10,7 +10,18 @@ window.Military = (function () {
const expn = d3.sum(valid.map(s => s.expansionism)); // total expansion
const area = d3.sum(valid.map(s => s.area)); // total area
- const rate = {x: 0, Ally: -0.2, Friendly: -0.1, Neutral: 0, Suspicion: 0.1, Enemy: 1, Unknown: 0, Rival: 0.5, Vassal: 0.5, Suzerain: -0.5};
+ const rate = {
+ x: 0,
+ Ally: -0.2,
+ Friendly: -0.1,
+ Neutral: 0,
+ Suspicion: 0.1,
+ Enemy: 1,
+ Unknown: 0,
+ Rival: 0.5,
+ Vassal: 0.5,
+ Suzerain: -0.5
+ };
const stateModifier = {
melee: {Nomadic: 0.5, Highland: 1.2, Lake: 1, Naval: 0.7, Hunting: 1.2, River: 1.1},
@@ -24,14 +35,59 @@ window.Military = (function () {
};
const cellTypeModifier = {
- nomadic: {melee: 0.2, ranged: 0.5, mounted: 3, machinery: 0.4, naval: 0.3, armored: 1.6, aviation: 1, magical: 0.5},
- wetland: {melee: 0.8, ranged: 2, mounted: 0.3, machinery: 1.2, naval: 1.0, armored: 0.2, aviation: 0.5, magical: 0.5},
- highland: {melee: 1.2, ranged: 1.6, mounted: 0.3, machinery: 3, naval: 1.0, armored: 0.8, aviation: 0.3, magical: 2}
+ nomadic: {
+ melee: 0.2,
+ ranged: 0.5,
+ mounted: 3,
+ machinery: 0.4,
+ naval: 0.3,
+ armored: 1.6,
+ aviation: 1,
+ magical: 0.5
+ },
+ wetland: {
+ melee: 0.8,
+ ranged: 2,
+ mounted: 0.3,
+ machinery: 1.2,
+ naval: 1.0,
+ armored: 0.2,
+ aviation: 0.5,
+ magical: 0.5
+ },
+ highland: {
+ melee: 1.2,
+ ranged: 1.6,
+ mounted: 0.3,
+ machinery: 3,
+ naval: 1.0,
+ armored: 0.8,
+ aviation: 0.3,
+ magical: 2
+ }
};
const burgTypeModifier = {
- nomadic: {melee: 0.3, ranged: 0.8, mounted: 3, machinery: 0.4, naval: 1.0, armored: 1.6, aviation: 1, magical: 0.5},
- wetland: {melee: 1, ranged: 1.6, mounted: 0.2, machinery: 1.2, naval: 1.0, armored: 0.2, aviation: 0.5, magical: 0.5},
+ nomadic: {
+ melee: 0.3,
+ ranged: 0.8,
+ mounted: 3,
+ machinery: 0.4,
+ naval: 1.0,
+ armored: 1.6,
+ aviation: 1,
+ magical: 0.5
+ },
+ wetland: {
+ melee: 1,
+ ranged: 1.6,
+ mounted: 0.2,
+ machinery: 1.2,
+ naval: 1.0,
+ armored: 0.2,
+ aviation: 0.5,
+ magical: 0.5
+ },
highland: {melee: 1.2, ranged: 2, mounted: 0.3, machinery: 3, naval: 1.0, armored: 0.8, aviation: 0.3, magical: 2}
};
@@ -40,8 +96,16 @@ window.Military = (function () {
const d = s.diplomacy;
const expansionRate = minmax(s.expansionism / expn / (s.area / area), 0.25, 4); // how much state expansionism is realized
- const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? 0.8 : d.some(d => d === "Suspicion") ? 0.5 : 0.1; // peacefulness
- const neighborsRateRaw = s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5);
+ const diplomacyRate = d.some(d => d === "Enemy")
+ ? 1
+ : d.some(d => d === "Rival")
+ ? 0.8
+ : d.some(d => d === "Suspicion")
+ ? 0.5
+ : 0.1; // peacefulness
+ const neighborsRateRaw = s.neighbors
+ .map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion"))
+ .reduce((s, r) => (s += rate[r]), 0.5);
const neighborsRate = minmax(neighborsRateRaw, 0.3, 3); // neighbors rate
s.alert = minmax(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1, 5); // alert rate (area modifier)
s.temp.platoons = [];
@@ -86,8 +150,10 @@ window.Military = (function () {
let modifier = cells.pop[i] / 100; // basic rural army in percentages
if (culture !== stateObj.culture) modifier = stateObj.form === "Union" ? modifier / 1.2 : modifier / 2; // non-dominant culture
- if (religion !== cells.religion[stateObj.center]) modifier = stateObj.form === "Theocracy" ? modifier / 2.2 : modifier / 1.4; // non-dominant religion
- if (cells.f[i] !== cells.f[stateObj.center]) modifier = stateObj.type === "Naval" ? modifier / 1.2 : modifier / 1.8; // different landmass
+ if (religion !== cells.religion[stateObj.center])
+ modifier = stateObj.form === "Theocracy" ? modifier / 2.2 : modifier / 1.4; // non-dominant religion
+ if (cells.f[i] !== cells.f[stateObj.center])
+ modifier = stateObj.type === "Naval" ? modifier / 1.2 : modifier / 1.8; // different landmass
const type = getType(i);
for (const unit of options.military) {
@@ -111,7 +177,17 @@ window.Military = (function () {
n = 1;
}
- stateObj.temp.platoons.push({cell: i, a: total, t: total, x, y, u: unit.name, n, s: unit.separate, type: unit.type});
+ stateObj.temp.platoons.push({
+ cell: i,
+ a: total,
+ t: total,
+ x,
+ y,
+ u: unit.name,
+ n,
+ s: unit.separate,
+ type: unit.type
+ });
}
}
@@ -153,7 +229,17 @@ window.Military = (function () {
n = 1;
}
- stateObj.temp.platoons.push({cell: b.cell, a: total, t: total, x, y, u: unit.name, n, s: unit.separate, type: unit.type});
+ stateObj.temp.platoons.push({
+ cell: b.cell,
+ a: total,
+ t: total,
+ x,
+ y,
+ u: unit.name,
+ n,
+ s: unit.separate,
+ type: unit.type
+ });
}
}
@@ -379,7 +465,13 @@ window.Military = (function () {
// get default regiment emblem
const getEmblem = function (r) {
if (!r.n && !Object.values(r.u).length) return "🔰"; // "Newbie" regiment without troops
- if (!r.n && pack.states[r.state].form === "Monarchy" && pack.cells.burg[r.cell] && pack.burgs[pack.cells.burg[r.cell]].capital) return "👑"; // "Royal" regiment based in capital
+ if (
+ !r.n &&
+ pack.states[r.state].form === "Monarchy" &&
+ pack.cells.burg[r.cell] &&
+ pack.burgs[pack.cells.burg[r.cell]].capital
+ )
+ return "👑"; // "Royal" regiment based in capital
const mainUnit = Object.entries(r.u).sort((a, b) => b[1] - a[1])[0][0]; // unit with more troops in regiment
const unit = options.military.find(u => u.name === mainUnit);
return unit.icon;
@@ -400,7 +492,9 @@ window.Military = (function () {
.map(t => `— ${t}: ${r.u[t]}`)
.join("\r\n")
: null;
- const troops = composition ? `\r\n\r\nRegiment composition in ${options.year} ${options.eraShort}:\r\n${composition}.` : "";
+ const troops = composition
+ ? `\r\n\r\nRegiment composition in ${options.year} ${options.eraShort}:\r\n${composition}.`
+ : "";
const campaign = s.campaigns ? ra(s.campaigns) : null;
const year = campaign ? rand(campaign.start, campaign.end) : gauss(options.year - 100, 150, 1, options.year - 6);
@@ -409,5 +503,16 @@ window.Military = (function () {
notes.push({id: `regiment${s.i}-${r.i}`, name: `${r.icon} ${r.name}`, legend});
};
- return {generate, redraw, getDefaultOptions, getName, generateNote, drawRegiments, drawRegiment, moveRegiment, getTotal, getEmblem};
+ return {
+ generate,
+ redraw,
+ getDefaultOptions,
+ getName,
+ generateNote,
+ drawRegiments,
+ drawRegiment,
+ moveRegiment,
+ getTotal,
+ getEmblem
+ };
})();
diff --git a/modules/names-generator.js b/modules/names-generator.js
index d7078abb..8d80437b 100644
--- a/modules/names-generator.js
+++ b/modules/names-generator.js
@@ -1,5 +1,3 @@
-"use strict";
-
window.Names = (function () {
let chains = [];
@@ -142,7 +140,11 @@ window.Names = (function () {
// generate short name for base
const getBaseShort = function (base) {
if (nameBases[base] === undefined) {
- tip(`Namebase ${base} does not exist. Please upload custom namebases of change the base in Cultures Editor`, false, "error");
+ tip(
+ `Namebase ${base} does not exist. Please upload custom namebases of change the base in Cultures Editor`,
+ false,
+ "error"
+ );
base = 1;
}
const min = nameBases[base].min - 1;
@@ -165,7 +167,8 @@ window.Names = (function () {
// remove -sk/-ev/-ov for Ruthenian
else if (base === 12) return vowel(name.slice(-1)) ? name : name + "u";
// Japanese ends on any vowel or -u
- else if (base === 18 && P(0.4)) name = vowel(name.slice(0, 1).toLowerCase()) ? "Al" + name.toLowerCase() : "Al " + name; // Arabic starts with -Al
+ else if (base === 18 && P(0.4))
+ name = vowel(name.slice(0, 1).toLowerCase()) ? "Al" + name.toLowerCase() : "Al " + name; // Arabic starts with -Al
// no suffix for fantasy bases
if (base > 32 && base < 42) return name;
@@ -304,5 +307,16 @@ window.Names = (function () {
];
};
- return {getBase, getCulture, getCultureShort, getBaseShort, getState, updateChain, clearChains, getNameBases, getMapName, calculateChain};
+ return {
+ getBase,
+ getCulture,
+ getCultureShort,
+ getBaseShort,
+ getState,
+ updateChain,
+ clearChains,
+ getNameBases,
+ getMapName,
+ calculateChain
+ };
})();
diff --git a/modules/ocean-layers.js b/modules/ocean-layers.js
index f21e4722..ae042c82 100644
--- a/modules/ocean-layers.js
+++ b/modules/ocean-layers.js
@@ -1,4 +1,4 @@
-"use strict";
+import {TIME} from "/src/config/logging";
window.OceanLayers = (function () {
let cells, vertices, pointsN, used;
diff --git a/modules/religions-generator.js b/modules/religions-generator.js
index b7b3464c..139a3558 100644
--- a/modules/religions-generator.js
+++ b/modules/religions-generator.js
@@ -1,4 +1,5 @@
-"use strict";
+import {TIME} from "/src/config/logging";
+import {findAll} from "/src/utils/graphUtils";
window.Religions = (function () {
// name generation approach and relative chance to be selected
diff --git a/modules/river-generator.js b/modules/river-generator.js
index 957fe6fc..af1f81a8 100644
--- a/modules/river-generator.js
+++ b/modules/river-generator.js
@@ -1,4 +1,4 @@
-"use strict";
+import {TIME} from "/src/config/logging";
window.Rivers = (function () {
const generate = function (allowErosion = true) {
@@ -48,7 +48,9 @@ window.Rivers = (function () {
cells.fl[i] += prec[cells.g[i]] / cellsNumberModifier; // add flux from precipitation
// create lake outlet if lake is not in deep depression and flux > evaporation
- const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
+ const lakes = lakeOutCells[i]
+ ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation)
+ : [];
for (const lake of lakes) {
const lakeCell = cells.c[i].find(c => h[c] < 20 && cells.f[c] === lake.i);
cells.fl[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet
@@ -191,7 +193,18 @@ window.Rivers = (function () {
const length = getApproximateLength(meanderedPoints);
const width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, 0));
- pack.rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells});
+ pack.rivers.push({
+ i: riverId,
+ source,
+ mouth,
+ discharge,
+ length,
+ width,
+ widthFactor,
+ sourceWidth: 0,
+ parent,
+ cells: riverCells
+ });
}
}
diff --git a/modules/routes-generator.js b/modules/routes-generator.js
index e4ec3374..86b5ce69 100644
--- a/modules/routes-generator.js
+++ b/modules/routes-generator.js
@@ -1,3 +1,6 @@
+import {TIME} from "/src/config/logging";
+import {findCell} from "/src/utils/graphUtils";
+
window.Routes = (function () {
const getRoads = function () {
TIME && console.time("generateMainRoads");
@@ -39,7 +42,10 @@ window.Routes = (function () {
if (!i) {
// build trail from the first burg on island
// to the farthest one on the same island or the closest road
- const farthest = d3.scan(isle, (a, c) => (c.y - b.y) ** 2 + (c.x - b.x) ** 2 - ((a.y - b.y) ** 2 + (a.x - b.x) ** 2));
+ const farthest = d3.scan(
+ isle,
+ (a, c) => (c.y - b.y) ** 2 + (c.x - b.x) ** 2 - ((a.y - b.y) ** 2 + (a.x - b.x) ** 2)
+ );
const to = isle[farthest].cell;
if (cells.road[to]) return;
const [from, exit] = findLandPath(b.cell, to, true);
@@ -131,7 +137,8 @@ window.Routes = (function () {
const getBurgCoords = b => [burgs[b].x, burgs[b].y];
const getPathPoints = cells => cells.map(i => (Array.isArray(i) ? i : burg[i] ? getBurgCoords(burg[i]) : p[i]));
const getPath = segment => round(lineGen(getPathPoints(segment)), 1);
- const getPathsHTML = (paths, type) => paths.map((path, i) => ``).join("");
+ const getPathsHTML = (paths, type) =>
+ paths.map((path, i) => ``).join("");
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
roads.html(getPathsHTML(main, "road"));
diff --git a/modules/ui/battle-screen.js b/modules/ui/battle-screen.js
index 2936549c..a2ad9cb9 100644
--- a/modules/ui/battle-screen.js
+++ b/modules/ui/battle-screen.js
@@ -32,17 +32,27 @@ class Battle {
close: () => Battle.prototype.context.cancelResults()
});
- if (modules.Battle) return;
- modules.Battle = true;
+ if (fmg.modules.Battle) return;
+ fmg.modules.Battle = true;
// add listeners
document.getElementById("battleType").addEventListener("click", ev => this.toggleChange(ev));
- document.getElementById("battleType").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
- document.getElementById("battleNameShow").addEventListener("click", () => Battle.prototype.context.showNameSection());
- document.getElementById("battleNamePlace").addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
+ document
+ .getElementById("battleType")
+ .nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
+ document
+ .getElementById("battleNameShow")
+ .addEventListener("click", () => Battle.prototype.context.showNameSection());
+ document
+ .getElementById("battleNamePlace")
+ .addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
document.getElementById("battleNameFull").addEventListener("change", ev => Battle.prototype.context.changeName(ev));
- document.getElementById("battleNameCulture").addEventListener("click", () => Battle.prototype.context.generateName("culture"));
- document.getElementById("battleNameRandom").addEventListener("click", () => Battle.prototype.context.generateName("random"));
+ document
+ .getElementById("battleNameCulture")
+ .addEventListener("click", () => Battle.prototype.context.generateName("culture"));
+ document
+ .getElementById("battleNameRandom")
+ .addEventListener("click", () => Battle.prototype.context.generateName("random"));
document.getElementById("battleNameHide").addEventListener("click", this.hideNameSection);
document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
document.getElementById("battleRoll").addEventListener("click", () => Battle.prototype.context.randomize());
@@ -52,11 +62,19 @@ class Battle {
document.getElementById("battleWiki").addEventListener("click", () => wiki("Battle-Simulator"));
document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChange(ev));
- document.getElementById("battlePhase_attackers").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
+ document
+ .getElementById("battlePhase_attackers")
+ .nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
document.getElementById("battlePhase_defenders").addEventListener("click", ev => this.toggleChange(ev));
- document.getElementById("battlePhase_defenders").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
- document.getElementById("battleDie_attackers").addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
- document.getElementById("battleDie_defenders").addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
+ document
+ .getElementById("battlePhase_defenders")
+ .nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
+ document
+ .getElementById("battleDie_attackers")
+ .addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
+ document
+ .getElementById("battleDie_defenders")
+ .addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
}
defineType() {
@@ -82,8 +100,12 @@ class Battle {
document.getElementById("battleType").className = "icon-button-" + this.type;
const sideSpecific = document.getElementById("battlePhases_" + this.type + "_attackers");
- const attackers = sideSpecific ? sideSpecific.content : document.getElementById("battlePhases_" + this.type).content;
- const defenders = sideSpecific ? document.getElementById("battlePhases_" + this.type + "_defenders").content : attackers;
+ const attackers = sideSpecific
+ ? sideSpecific.content
+ : document.getElementById("battlePhases_" + this.type).content;
+ const defenders = sideSpecific
+ ? document.getElementById("battlePhases_" + this.type + "_defenders").content
+ : attackers;
document.getElementById("battlePhase_attackers").nextElementSibling.innerHTML = "";
document.getElementById("battlePhase_defenders").nextElementSibling.innerHTML = "";
@@ -146,19 +168,30 @@ class Battle {
${regiment.icon}`;
const body = `
`;
- let initial = `| ${icon} | ${regiment.name.slice(0, 24)} | `;
- let casualties = `
| ${state.fullName.slice(0, 26)} | `;
+ let initial = `
| ${icon} | ${regiment.name.slice(0, 24)} | `;
+ let casualties = `
| ${state.fullName.slice(
+ 0,
+ 26
+ )} | `;
let survivors = `
| Distance to base: ${distance} ${distanceUnitInput.value} | `;
for (const u of options.military) {
- initial += `${regiment.u[u.name] || 0} | `;
+ initial += `${
+ regiment.u[u.name] || 0
+ } | `;
casualties += `0 | `;
- survivors += `${regiment.u[u.name] || 0} | `;
+ survivors += `${
+ regiment.u[u.name] || 0
+ } | `;
}
initial += `${regiment.a || 0} |
`;
casualties += `0 | `;
- survivors += `${regiment.a || 0} | `;
+ survivors += `${
+ regiment.a || 0
+ } | `;
const div = side === "attackers" ? battleAttackers : battleDefenders;
div.innerHTML += body + initial + casualties + survivors + "";
@@ -173,17 +206,23 @@ class Battle {
.filter(s => s.military && !s.removed)
.map(s => s.military)
.flat();
- const distance = reg => rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
- const isAdded = reg => context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
+ const distance = reg =>
+ rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
+ const isAdded = reg =>
+ context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
body.innerHTML = regiments
.map(r => {
const s = pack.states[r.state],
added = isAdded(r),
dist = added ? "0 " + distanceUnitInput.value : distance(r);
- return `
-
+
${s.name.slice(0, 11)}
${r.icon}
${r.name.slice(0, 24)}
@@ -267,7 +306,10 @@ class Battle {
}
generateName(type) {
- const place = type === "culture" ? Names.getCulture(pack.cells.culture[this.cell], null, null, "") : Names.getBase(rand(nameBases.length - 1));
+ const place =
+ type === "culture"
+ ? Names.getCulture(pack.cells.culture[this.cell], null, null, "")
+ : Names.getBase(rand(nameBases.length - 1));
document.getElementById("battleNamePlace").value = this.place = place;
document.getElementById("battleNameFull").value = this.name = this.defineName();
$("#battleScreen").dialog({title: this.name});
@@ -286,35 +328,161 @@ class Battle {
calculateStrength(side) {
const scheme = {
// field battle phases
- skirmish: {melee: 0.2, ranged: 2.4, mounted: 0.1, machinery: 3, naval: 1, armored: 0.2, aviation: 1.8, magical: 1.8}, // ranged excel
+ skirmish: {
+ melee: 0.2,
+ ranged: 2.4,
+ mounted: 0.1,
+ machinery: 3,
+ naval: 1,
+ armored: 0.2,
+ aviation: 1.8,
+ magical: 1.8
+ }, // ranged excel
melee: {melee: 2, ranged: 1.2, mounted: 1.5, machinery: 0.5, naval: 0.2, armored: 2, aviation: 0.8, magical: 0.8}, // melee excel
pursue: {melee: 1, ranged: 1, mounted: 4, machinery: 0.05, naval: 1, armored: 1, aviation: 1.5, magical: 0.6}, // mounted excel
- retreat: {melee: 0.1, ranged: 0.01, mounted: 0.5, machinery: 0.01, naval: 0.2, armored: 0.1, aviation: 0.8, magical: 0.05}, // reduced
+ retreat: {
+ melee: 0.1,
+ ranged: 0.01,
+ mounted: 0.5,
+ machinery: 0.01,
+ naval: 0.2,
+ armored: 0.1,
+ aviation: 0.8,
+ magical: 0.05
+ }, // reduced
// naval battle phases
shelling: {melee: 0, ranged: 0.2, mounted: 0, machinery: 2, naval: 2, armored: 0, aviation: 0.1, magical: 0.5}, // naval and machinery excel
- boarding: {melee: 1, ranged: 0.5, mounted: 0.5, machinery: 0, naval: 0.5, armored: 0.4, aviation: 0, magical: 0.2}, // melee excel
+ boarding: {
+ melee: 1,
+ ranged: 0.5,
+ mounted: 0.5,
+ machinery: 0,
+ naval: 0.5,
+ armored: 0.4,
+ aviation: 0,
+ magical: 0.2
+ }, // melee excel
chase: {melee: 0, ranged: 0.15, mounted: 0, machinery: 1, naval: 1, armored: 0, aviation: 0.15, magical: 0.5}, // reduced
- withdrawal: {melee: 0, ranged: 0.02, mounted: 0, machinery: 0.5, naval: 0.1, armored: 0, aviation: 0.1, magical: 0.3}, // reduced
+ withdrawal: {
+ melee: 0,
+ ranged: 0.02,
+ mounted: 0,
+ machinery: 0.5,
+ naval: 0.1,
+ armored: 0,
+ aviation: 0.1,
+ magical: 0.3
+ }, // reduced
// siege phases
- blockade: {melee: 0.25, ranged: 0.25, mounted: 0.2, machinery: 0.5, naval: 0.2, armored: 0.1, aviation: 0.25, magical: 0.25}, // no active actions
- sheltering: {melee: 0.3, ranged: 0.5, mounted: 0.2, machinery: 0.5, naval: 0.2, armored: 0.1, aviation: 0.25, magical: 0.25}, // no active actions
+ blockade: {
+ melee: 0.25,
+ ranged: 0.25,
+ mounted: 0.2,
+ machinery: 0.5,
+ naval: 0.2,
+ armored: 0.1,
+ aviation: 0.25,
+ magical: 0.25
+ }, // no active actions
+ sheltering: {
+ melee: 0.3,
+ ranged: 0.5,
+ mounted: 0.2,
+ machinery: 0.5,
+ naval: 0.2,
+ armored: 0.1,
+ aviation: 0.25,
+ magical: 0.25
+ }, // no active actions
sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel
- bombardment: {melee: 0.2, ranged: 0.5, mounted: 0.2, machinery: 3, naval: 1, armored: 0.5, aviation: 1, magical: 1}, // machinery excel
- storming: {melee: 1, ranged: 0.6, mounted: 0.5, machinery: 1, naval: 0.1, armored: 0.1, aviation: 0.5, magical: 0.5}, // melee excel
+ bombardment: {
+ melee: 0.2,
+ ranged: 0.5,
+ mounted: 0.2,
+ machinery: 3,
+ naval: 1,
+ armored: 0.5,
+ aviation: 1,
+ magical: 1
+ }, // machinery excel
+ storming: {
+ melee: 1,
+ ranged: 0.6,
+ mounted: 0.5,
+ machinery: 1,
+ naval: 0.1,
+ armored: 0.1,
+ aviation: 0.5,
+ magical: 0.5
+ }, // melee excel
defense: {melee: 2, ranged: 3, mounted: 1, machinery: 1, naval: 0.1, armored: 1, aviation: 0.5, magical: 1}, // ranged excel
- looting: {melee: 1.6, ranged: 1.6, mounted: 0.5, machinery: 0.2, naval: 0.02, armored: 0.2, aviation: 0.1, magical: 0.3}, // melee excel
- surrendering: {melee: 0.1, ranged: 0.1, mounted: 0.05, machinery: 0.01, naval: 0.01, armored: 0.02, aviation: 0.01, magical: 0.03}, // reduced
+ looting: {
+ melee: 1.6,
+ ranged: 1.6,
+ mounted: 0.5,
+ machinery: 0.2,
+ naval: 0.02,
+ armored: 0.2,
+ aviation: 0.1,
+ magical: 0.3
+ }, // melee excel
+ surrendering: {
+ melee: 0.1,
+ ranged: 0.1,
+ mounted: 0.05,
+ machinery: 0.01,
+ naval: 0.01,
+ armored: 0.02,
+ aviation: 0.01,
+ magical: 0.03
+ }, // reduced
// ambush phases
surprise: {melee: 2, ranged: 2.4, mounted: 1, machinery: 1, naval: 1, armored: 1, aviation: 0.8, magical: 1.2}, // increased
- shock: {melee: 0.5, ranged: 0.5, mounted: 0.5, machinery: 0.4, naval: 0.3, armored: 0.1, aviation: 0.4, magical: 0.5}, // reduced
+ shock: {
+ melee: 0.5,
+ ranged: 0.5,
+ mounted: 0.5,
+ machinery: 0.4,
+ naval: 0.3,
+ armored: 0.1,
+ aviation: 0.4,
+ magical: 0.5
+ }, // reduced
// langing phases
- landing: {melee: 0.8, ranged: 0.6, mounted: 0.6, machinery: 0.5, naval: 0.5, armored: 0.5, aviation: 0.5, magical: 0.6}, // reduced
- flee: {melee: 0.1, ranged: 0.01, mounted: 0.5, machinery: 0.01, naval: 0.5, armored: 0.1, aviation: 0.2, magical: 0.05}, // reduced
- waiting: {melee: 0.05, ranged: 0.5, mounted: 0.05, machinery: 0.5, naval: 2, armored: 0.05, aviation: 0.5, magical: 0.5}, // reduced
+ landing: {
+ melee: 0.8,
+ ranged: 0.6,
+ mounted: 0.6,
+ machinery: 0.5,
+ naval: 0.5,
+ armored: 0.5,
+ aviation: 0.5,
+ magical: 0.6
+ }, // reduced
+ flee: {
+ melee: 0.1,
+ ranged: 0.01,
+ mounted: 0.5,
+ machinery: 0.01,
+ naval: 0.5,
+ armored: 0.1,
+ aviation: 0.2,
+ magical: 0.05
+ }, // reduced
+ waiting: {
+ melee: 0.05,
+ ranged: 0.5,
+ mounted: 0.05,
+ machinery: 0.5,
+ naval: 2,
+ armored: 0.05,
+ aviation: 0.5,
+ magical: 0.5
+ }, // reduced
// air battle phases
maneuvering: {melee: 0, ranged: 0.1, mounted: 0, machinery: 0.2, naval: 0, armored: 0, aviation: 1, magical: 0.2}, // aviation
@@ -324,7 +492,8 @@ class Battle {
const forces = this.getJoinedForces(this[side].regiments);
const phase = this[side].phase;
const adjuster = Math.max(populationRate / 10, 10); // population adjuster, by default 100
- this[side].power = d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
+ this[side].power =
+ d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
const UIvalue = this[side].power ? Math.max(this[side].power | 0, 1) : 0;
document.getElementById("battlePower_" + side).innerHTML = UIvalue;
}
@@ -723,11 +892,13 @@ class Battle {
const status = battleStatus[+P(0.7)];
const result = `The ${this.getTypeName(this.type)} ended in ${status}`;
- const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(this.attackers.regiments, 1)} and ${getSide(
- this.defenders.regiments,
- 0
- )}. ${result}.
- \r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(this.defenders.casualties)}%`;
+ const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(
+ this.attackers.regiments,
+ 1
+ )} and ${getSide(this.defenders.regiments, 0)}. ${result}.
+ \r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(
+ this.defenders.casualties
+ )}%`;
notes.push({id: `marker${i}`, name: this.name, legend});
tip(`${this.name} is over. ${result}`, true, "success", 4000);
diff --git a/modules/ui/biomes-editor.js b/modules/ui/biomes-editor.js
index 0cfc5ee2..b3752e9e 100644
--- a/modules/ui/biomes-editor.js
+++ b/modules/ui/biomes-editor.js
@@ -12,8 +12,8 @@ function editBiomes() {
const animate = d3.transition().duration(2000).ease(d3.easeSinIn);
refreshBiomesEditor();
- if (modules.editBiomes) return;
- modules.editBiomes = true;
+ if (fmg.modules.editBiomes) return;
+ fmg.modules.editBiomes = true;
$("#biomesEditor").dialog({
title: "Biomes Editor",
@@ -88,7 +88,9 @@ function editBiomes() {
const rural = b.rural[i] * populationRate;
const urban = b.urban[i] * populationRate * urbanization;
const population = rn(rural + urban);
- const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}`;
+ const populationTip = `Total population: ${si(population)}; Rural population: ${si(
+ rural
+ )}; Urban population: ${si(urban)}`;
totalArea += area;
totalPopulation += population;
@@ -104,7 +106,9 @@ function editBiomes() {
data-color=${b.color[i]}
>
-
+
%
${si(population)}
- ${i > 12 && !b.cells[i] ? '
' : ""}
+ ${
+ i > 12 && !b.cells[i]
+ ? '
'
+ : ""
+ }
`;
}
@@ -403,7 +411,14 @@ function editBiomes() {
// change of append new element
if (exists.size()) exists.attr("data-biome", biomeNew).attr("fill", color).attr("stroke", color);
- else temp.append("polygon").attr("data-cell", i).attr("data-biome", biomeNew).attr("points", getPackPolygon(i)).attr("fill", color).attr("stroke", color);
+ else
+ temp
+ .append("polygon")
+ .attr("data-cell", i)
+ .attr("data-biome", biomeNew)
+ .attr("points", getPackPolygon(i))
+ .attr("fill", color)
+ .attr("stroke", color);
});
}
diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js
index ea3a76f2..4e0a10e0 100644
--- a/modules/ui/burg-editor.js
+++ b/modules/ui/burg-editor.js
@@ -17,8 +17,8 @@ function editBurg(id) {
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"}
});
- if (modules.editBurg) return;
- modules.editBurg = true;
+ if (fmg.modules.editBurg) return;
+ fmg.modules.editBurg = true;
// add listeners
document.getElementById("burgGroupShow").addEventListener("click", showGroupSection);
@@ -284,7 +284,9 @@ function editBurg(id) {
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${
basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
}?
-
Please note that capital or locked burgs will not be deleted.
Burgs to be removed: ${burgsToRemove.length}`;
+
Please note that capital or locked burgs will not be deleted.
Burgs to be removed: ${
+ burgsToRemove.length
+ }`;
$("#alert").dialog({
resizable: false,
title: "Remove burg group",
@@ -433,7 +435,8 @@ function editBurg(id) {
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";
+ 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;
diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js
index bf819465..5a1f02cf 100644
--- a/modules/ui/burgs-overview.js
+++ b/modules/ui/burgs-overview.js
@@ -11,8 +11,8 @@ function overviewBurgs() {
burgsOverviewAddLines();
$("#burgsOverview").dialog();
- if (modules.overviewBurgs) return;
- modules.overviewBurgs = true;
+ if (fmg.modules.overviewBurgs) return;
+ fmg.modules.overviewBurgs = true;
$("#burgsOverview").dialog({
title: "Burgs Overview",
@@ -93,7 +93,9 @@ function overviewBurgs() {
data-type="${type}"
>
-
+
w:
-
+
`;
@@ -1042,8 +1081,8 @@ function editHeightmap(options) {
viewbox.select("#heights").selectAll("*").remove();
updateHistory();
- if (modules.openImageConverter) return;
- modules.openImageConverter = true;
+ if (fmg.modules.openImageConverter) return;
+ fmg.modules.openImageConverter = true;
// add color pallete
void (function createColorPallete() {
@@ -1245,7 +1284,8 @@ function editHeightmap(options) {
const assinged = []; // store assigned heights
unassigned.forEach(el => {
const clr = el.dataset.color;
- const height = type === "hue" ? getHeightByHue(clr) : type === "lum" ? getHeightByLum(clr) : getHeightByScheme(clr);
+ const height =
+ type === "hue" ? getHeightByHue(clr) : type === "lum" ? getHeightByLum(clr) : getHeightByScheme(clr);
const colorTo = color(1 - (height < 20 ? (height - 5) / 100 : height / 100));
viewbox
.select("#heights")
diff --git a/modules/ui/hotkeys.js b/modules/ui/hotkeys.js
index d8fcaaf8..288ec5fa 100644
--- a/modules/ui/hotkeys.js
+++ b/modules/ui/hotkeys.js
@@ -13,7 +13,6 @@ function handleKeydown(event) {
}
function handleKeyup(event) {
- if (!modules.editors) return; // if editors are not loaded, do nothing
if (!allowHotkeys()) return; // in some cases (e.g. in a textarea) hotkeys are not allowed
event.stopPropagation();
diff --git a/modules/ui/ice-editor.js b/modules/ui/ice-editor.js
index f07cb6f9..b0d2a986 100644
--- a/modules/ui/ice-editor.js
+++ b/modules/ui/ice-editor.js
@@ -18,8 +18,8 @@ function editIce() {
close: closeEditor
});
- if (modules.editIce) return;
- modules.editIce = true;
+ if (fmg.modules.editIce) return;
+ fmg.modules.editIce = true;
// add listeners
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
diff --git a/modules/ui/labels-editor.js b/modules/ui/labels-editor.js
index 8bd04cdd..9995faf8 100644
--- a/modules/ui/labels-editor.js
+++ b/modules/ui/labels-editor.js
@@ -22,8 +22,8 @@ function editLabel() {
selectLabelGroup(text);
updateValues(textPath);
- if (modules.editLabel) return;
- modules.editLabel = true;
+ if (fmg.modules.editLabel) return;
+ fmg.modules.editLabel = true;
// add listeners
document.getElementById("labelGroupShow").addEventListener("click", showGroupSection);
@@ -78,7 +78,9 @@ function editLabel() {
}
function updateValues(textPath) {
- document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")].map(tspan => tspan.textContent).join("|");
+ document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")]
+ .map(tspan => tspan.textContent)
+ .join("|");
document.getElementById("labelStartOffset").value = parseFloat(textPath.getAttribute("startOffset"));
document.getElementById("labelRelativeSize").value = parseFloat(textPath.getAttribute("font-size"));
}
@@ -298,7 +300,13 @@ function editLabel() {
function changeText() {
const input = document.getElementById("labelText").value;
const el = elSelected.select("textPath").node();
- const example = d3.select(elSelected.node().parentNode).append("text").attr("x", 0).attr("x", 0).attr("font-size", el.getAttribute("font-size")).node();
+ const example = d3
+ .select(elSelected.node().parentNode)
+ .append("text")
+ .attr("x", 0)
+ .attr("x", 0)
+ .attr("font-size", el.getAttribute("font-size"))
+ .node();
const lines = input.split("|");
const top = (lines.length - 1) / -2; // y offset
@@ -313,7 +321,8 @@ function editLabel() {
el.innerHTML = inner;
example.remove();
- if (elSelected.attr("id").slice(0, 10) === "stateLabel") tip("Use States Editor to change an actual state name, not just a label", false, "warning");
+ if (elSelected.attr("id").slice(0, 10) === "stateLabel")
+ tip("Use States Editor to change an actual state name, not just a label", false, "warning");
}
function generateRandomName() {
diff --git a/modules/ui/lakes-editor.js b/modules/ui/lakes-editor.js
index 972a9cb8..2fcab524 100644
--- a/modules/ui/lakes-editor.js
+++ b/modules/ui/lakes-editor.js
@@ -19,8 +19,8 @@ function editLake() {
drawLakeVertices();
viewbox.on("touchmove mousemove", null);
- if (modules.editLake) return;
- modules.editLake = true;
+ if (fmg.modules.editLake) return;
+ fmg.modules.editLake = true;
// add listeners
document.getElementById("lakeName").addEventListener("input", changeName);
@@ -48,7 +48,8 @@ function editLake() {
document.getElementById("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v]));
- document.getElementById("lakeShoreLength").value = si(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
+ document.getElementById("lakeShoreLength").value =
+ si(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i));
const heights = lakeCells.map(i => cells.h[i]);
@@ -91,7 +92,9 @@ function editLake() {
.attr("r", 0.4)
.attr("data-v", d => d)
.call(d3.drag().on("drag", dragVertex))
- .on("mousemove", () => tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights"));
+ .on("mousemove", () =>
+ tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights")
+ );
}
function dragVertex() {
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index 304f455a..54e11d96 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -1,5 +1,5 @@
-// UI module stub to control map layers
-"use strict";
+import {TIME} from "/src/config/logging";
+import {invokeActiveZooming} from "../activeZooming";
let presets = {}; // global object
restoreCustomPresets(); // run on-load
@@ -946,7 +946,7 @@ function toggleStates(event) {
}
}
-function drawStates() {
+export function drawStates() {
TIME && console.time("drawStates");
regions.selectAll("path").remove();
@@ -1110,7 +1110,7 @@ function toggleBorders(event) {
}
// draw state and province borders
-function drawBorders() {
+export function drawBorders() {
TIME && console.time("drawBorders");
borders.selectAll("path").remove();
@@ -1554,7 +1554,7 @@ function toggleRivers(event) {
}
}
-function drawRivers() {
+export function drawRivers() {
TIME && console.time("drawRivers");
rivers.selectAll("*").remove();
@@ -1870,7 +1870,7 @@ function drawEmblems() {
TIME && console.timeEnd("drawEmblems");
}
-function layerIsOn(el) {
+export function layerIsOn(el) {
const buttonoff = document.getElementById(el).classList.contains("buttonoff");
return !buttonoff;
}
diff --git a/modules/ui/military-overview.js b/modules/ui/military-overview.js
index 35a23ed4..c55e1534 100644
--- a/modules/ui/military-overview.js
+++ b/modules/ui/military-overview.js
@@ -10,8 +10,8 @@ function overviewMilitary() {
addLines();
$("#militaryOverview").dialog();
- if (modules.overviewMilitary) return;
- modules.overviewMilitary = true;
+ if (fmg.modules.overviewMilitary) return;
+ fmg.modules.overviewMilitary = true;
updateHeaders();
$("#militaryOverview").dialog({
@@ -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");
@@ -254,8 +274,8 @@ function overviewMilitary() {
}
});
- if (modules.overviewMilitaryCustomize) return;
- modules.overviewMilitaryCustomize = true;
+ if (fmg.modules.overviewMilitaryCustomize) return;
+ fmg.modules.overviewMilitaryCustomize = true;
tableBody.addEventListener("click", event => {
const el = event.target;
@@ -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 =>
`
`;
$("#alert").dialog({
@@ -194,7 +214,8 @@ function editNamesbase() {
function namesbaseAdd() {
const base = nameBases.length;
- const b = "This,is,an,example,of,name,base,showing,correct,format,It,should,have,at,least,one,hundred,names,separated,with,comma";
+ const b =
+ "This,is,an,example,of,name,base,showing,correct,format,It,should,have,at,least,one,hundred,names,separated,with,comma";
nameBases.push({name: "Base" + base, min: 5, max: 12, d: "", m: 0, b});
document.getElementById("namesbaseSelect").add(new Option("Base" + base, base));
document.getElementById("namesbaseSelect").value = base;
@@ -232,7 +253,7 @@ function editNamesbase() {
downloadFile(data, name);
}
- function namesbaseUpload(dataLoaded, override=true) {
+ function namesbaseUpload(dataLoaded, override = true) {
const data = dataLoaded.split("\r\n");
if (!data || !data[0]) {
tip("Cannot load a namesbase. Please check the data format", false, "error");
diff --git a/modules/ui/notes-editor.js b/modules/ui/notes-editor.js
index 4362fb25..6fa266bc 100644
--- a/modules/ui/notes-editor.js
+++ b/modules/ui/notes-editor.js
@@ -69,12 +69,12 @@ function editNotes(id, name) {
if (!window.tinymce) {
const url = "https://cdn.tiny.cloud/1/4i6a79ymt2y0cagke174jp3meoi28vyecrch12e5puyw3p9a/tinymce/5/tinymce.min.js";
try {
- await import(url);
+ await import(/* @vite-ignore */ url);
} catch (error) {
// error may be caused by failed request being cached, try again with random hash
try {
const hash = Math.random().toString(36).substring(2, 15);
- await import(`${url}#${hash}`);
+ await import(/* @vite-ignore */ `${url}#${hash}`);
} catch (error) {
console.error(error);
}
diff --git a/modules/ui/options.js b/modules/ui/options.js
index 0683a694..2a65efed 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -1,5 +1,4 @@
-// UI module to control the options (preferences)
-"use strict";
+import {stored, lock, locked, applyOption} from "./general";
$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"});
$("#exitCustomization").draggable({handle: "div"});
@@ -170,10 +169,7 @@ function changeMapSize() {
const maxWidth = Math.max(+mapWidthInput.value, graphWidth);
const maxHeight = Math.max(+mapHeightInput.value, graphHeight);
- zoom.translateExtent([
- [0, 0],
- [maxWidth, maxHeight]
- ]);
+ Zoom.translateExtent([0, 0, maxWidth, maxHeight]);
landmass.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
oceanPattern.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
oceanLayers.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
@@ -186,7 +182,7 @@ function changeMapSize() {
}
// just apply canvas size that was already set
-function applyMapSize() {
+export function applyMapSize() {
const zoomMin = +zoomExtentMin.value;
const zoomMax = +zoomExtentMax.value;
graphWidth = +mapWidthInput.value;
@@ -194,13 +190,10 @@ function applyMapSize() {
svgWidth = Math.min(graphWidth, window.innerWidth);
svgHeight = Math.min(graphHeight, window.innerHeight);
svg.attr("width", svgWidth).attr("height", svgHeight);
- zoom
- .translateExtent([
- [0, 0],
- [graphWidth, graphHeight]
- ])
- .scaleExtent([zoomMin, zoomMax])
- .scaleTo(svg, zoomMin);
+
+ Zoom.translateExtent([0, 0, graphWidth, graphHeight]);
+ Zoom.scaleExtent([zoomMin, zoomMax]);
+ Zoom.scaleTo(svg, zoomMin);
}
function toggleFullscreen() {
@@ -217,17 +210,13 @@ function toggleFullscreen() {
}
function toggleTranslateExtent(el) {
- const on = (el.dataset.on = +!+el.dataset.on);
- if (on)
- zoom.translateExtent([
- [-graphWidth / 2, -graphHeight / 2],
- [graphWidth * 1.5, graphHeight * 1.5]
- ]);
- else
- zoom.translateExtent([
- [0, 0],
- [graphWidth, graphHeight]
- ]);
+ const on = !Number(el.dataset.on);
+ const extent = on
+ ? [-graphWidth / 2, -graphHeight / 2, graphWidth * 1.5, graphHeight * 1.5]
+ : [0, 0, graphWidth, graphHeight];
+ Zoom.translateExtent(extent);
+
+ el.dataset.on = Number(on);
}
// add voice options
@@ -294,7 +283,8 @@ function restoreSeed(id) {
function restoreDefaultZoomExtent() {
zoomExtentMin.value = 1;
zoomExtentMax.value = 20;
- zoom.scaleExtent([1, 20]).scaleTo(svg, 1);
+ Zoom.scaleExtent([1, 20]);
+ Zoom.scaleTo(svg, 1);
}
function copyMapURL() {
@@ -461,15 +451,16 @@ function changeDialogsTheme(themeColor, transparency) {
}
function changeZoomExtent(value) {
- const min = Math.max(+zoomExtentMin.value, 0.01);
- const max = Math.min(+zoomExtentMax.value, 200);
- zoom.scaleExtent([min, max]);
+ const min = Math.max(+byId("zoomExtentMin").value, 0.01);
+ const max = Math.min(+byId("zoomExtentMax").value, 200);
+ Zoom.scaleExtent([min, max]);
+
const scale = minmax(+value, 0.01, 200);
- zoom.scaleTo(svg, scale);
+ Zoom.scaleTo(svg, scale);
}
// restore options stored in localStorage
-function applyStoredOptions() {
+export function applyStoredOptions() {
if (!stored("mapWidth") || !stored("mapHeight")) {
mapWidthInput.value = window.innerWidth;
mapHeightInput.value = window.innerHeight;
@@ -530,7 +521,7 @@ function applyStoredOptions() {
}
// randomize options if randomization is allowed (not locked or options='default')
-function randomizeOptions() {
+export function randomizeOptions() {
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
// 'Options' settings
@@ -595,7 +586,7 @@ function randomizeCultureSet() {
}
function setRendering(value) {
- viewbox.attr("shape-rendering", value);
+ fmg.viewbox?.attr("shape-rendering", value);
}
// generate current year and era name
@@ -652,7 +643,7 @@ document.getElementById("sticked").addEventListener("click", function (event) {
else if (id === "saveButton") showSavePane();
else if (id === "exportButton") showExportPane();
else if (id === "loadButton") showLoadPane();
- else if (id === "zoomReset") resetZoom(1000);
+ else if (id === "zoomReset") Zoom.reset(1000);
});
function regeneratePrompt(options) {
@@ -975,8 +966,8 @@ function toggle3dOptions() {
updateValues();
- if (modules.options3d) return;
- modules.options3d = true;
+ if (fmg.modules.options3d) return;
+ fmg.modules.options3d = true;
document.getElementById("options3dUpdate").addEventListener("click", ThreeD.update);
document.getElementById("options3dSave").addEventListener("click", ThreeD.saveScreenshot);
diff --git a/modules/ui/provinces-editor.js b/modules/ui/provinces-editor.js
index 3cac626f..6a55f032 100644
--- a/modules/ui/provinces-editor.js
+++ b/modules/ui/provinces-editor.js
@@ -11,8 +11,8 @@ function editProvinces() {
const body = document.getElementById("provincesBodySection");
refreshProvincesEditor();
- if (modules.editProvinces) return;
- modules.editProvinces = true;
+ if (fmg.modules.editProvinces) return;
+ fmg.modules.editProvinces = true;
$("#provincesEditor").dialog({
title: "Provinces Editor",
@@ -123,7 +123,9 @@ function editProvinces() {
const rural = p.rural * populationRate;
const urban = p.urban * populationRate * urbanization;
const population = rn(rural + urban);
- const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}`;
+ const populationTip = `Total population: ${si(population)}; Rural population: ${si(
+ rural
+ )}; Urban population: ${si(urban)}`;
totalPopulation += population;
const stateName = pack.states[p.state].name;
@@ -144,9 +146,15 @@ function editProvinces() {
>
-
-
-
+
+
+