diff --git a/heightmaps/import-rules.txt b/heightmaps/import-rules.txt
index 9478b35c..69499114 100644
--- a/heightmaps/import-rules.txt
+++ b/heightmaps/import-rules.txt
@@ -1,8 +1,8 @@
To get heightmap with correct height scale:
-1. Open tangrams.github.io
+1. Open https://tangrams.github.io/heightmapper
2. Toggle off auto-exposure
3. Set max elevation to 2000
4. Set min elevation to -500
5. Find region you like
6. Render image
-7. Optionally rescale image to a smaller size (e.g. 500x300px) as high resolution is not used
\ No newline at end of file
+7. Optionally rescale image to a smaller size (e.g. 500x300px) as high resolution is not used
diff --git a/index.html b/index.html
index cb428e3a..ce7f00e2 100644
--- a/index.html
+++ b/index.html
@@ -7863,14 +7863,14 @@
-
+
-
+
diff --git a/main.js b/main.js
index d3b290b8..5f9086f6 100644
--- a/main.js
+++ b/main.js
@@ -191,7 +191,6 @@ let populationRate = +document.getElementById("populationRateInput").value;
let distanceScale = +document.getElementById("distanceScaleInput").value;
let urbanization = +document.getElementById("urbanizationInput").value;
let urbanDensity = +document.getElementById("urbanDensityInput").value;
-let statesNeutral = 1; // statesEditor growth parameter
applyStoredOptions();
diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js
index e9069e83..e360b45a 100644
--- a/modules/burgs-and-states.js
+++ b/modules/burgs-and-states.js
@@ -359,7 +359,7 @@ window.BurgsAndStates = (function () {
TIME && console.timeEnd("drawBurgs");
};
- // growth algorithm to assign cells to states like we did for cultures
+ // expand cultures across the map (Dijkstra-like algorithm)
const expandStates = function () {
TIME && console.time("expandStates");
const {cells, states, cultures, burgs} = pack;
@@ -367,18 +367,28 @@ window.BurgsAndStates = (function () {
cells.state = cells.state || new Uint16Array(cells.i.length);
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
const cost = [];
- const neutral = (cells.i.length / 5000) * 2500 * neutralInput.value * statesNeutral; // limit cost for state growth
- states
- .filter(s => s.i && !s.removed)
- .forEach(s => {
- const capitalCell = burgs[s.capital].cell;
- cells.state[capitalCell] = s.i;
- const cultureCenter = cultures[s.culture].center;
- const b = cells.biome[cultureCenter]; // state native biome
- queue.queue({e: s.center, p: 0, s: s.i, b});
- cost[s.center] = 1;
- });
+ const globalNeutralRate = byId("neutralInput")?.valueAsNumber || 1;
+ const statesNeutralRate = byId("statesNeutral")?.valueAsNumber || 1;
+ const neutral = (cells.i.length / 2) * globalNeutralRate * statesNeutralRate; // limit cost for state growth
+
+ // remove state from all cells except of locked
+ for (const cellId of cells.i) {
+ const state = states[cells.state[cellId]];
+ if (state.lock) continue;
+ cells.state[cellId] = 0;
+ }
+
+ for (const state of states) {
+ if (!state.i || state.removed) continue;
+
+ const capitalCell = burgs[state.capital].cell;
+ cells.state[capitalCell] = state.i;
+ const cultureCenter = cultures[state.culture].center;
+ const b = cells.biome[cultureCenter]; // state native biome
+ queue.queue({e: state.center, p: 0, s: state.i, b});
+ cost[state.center] = 1;
+ }
while (queue.length) {
const next = queue.dequeue();
diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js
index db8fb06a..cb8473ca 100644
--- a/modules/cultures-generator.js
+++ b/modules/cultures-generator.js
@@ -116,10 +116,10 @@ window.Cultures = (function () {
cultures.forEach(c => (c.base = c.base % nameBases.length));
- function selectCultures(c) {
- let def = getDefault(c);
+ function selectCultures(culturesNumber) {
+ let def = getDefault(culturesNumber);
const cultures = [];
-
+
pack.cultures?.forEach(function (culture) {
if (culture.lock) cultures.push(culture);
});
@@ -153,8 +153,8 @@ window.Cultures = (function () {
}
}
} else {
- if (c === def.length) return def;
- if (def.every(d => d.odd === 1)) return def.splice(0, c);
+ if (culturesNumber === def.length) return def;
+ if (def.every(d => d.odd === 1)) return def.splice(0, culturesNumber);
if (pack.religions?.length > 2) {
pack.religions.forEach(r => {
if (r.type === "Folk" && !r.lock) r.removed = true;
@@ -162,7 +162,7 @@ window.Cultures = (function () {
}
}
- for (let culture, rnd, i = 0; cultures.length < c && def.length > 0;) {
+ for (let culture, rnd, i = 0; cultures.length < culturesNumber && def.length > 0;) {
do {
rnd = rand(def.length - 1);
culture = def[rnd];
@@ -542,28 +542,37 @@ window.Cultures = (function () {
// expand cultures across the map (Dijkstra-like algorithm)
const expand = function () {
TIME && console.time("expandCultures");
- cells = pack.cells;
+ const {cells, cultures} = pack;
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
- pack.cultures.forEach(function (c) {
- if (!c.i || c.removed || c.lock) return;
- queue.queue({e: c.center, p: 0, c: c.i});
- });
-
- const neutral = (cells.i.length / 5000) * 3000 * neutralInput.value; // limit cost for culture growth
const cost = [];
+
+ const neutralRate = byId("neutralRate")?.valueAsNumber || 1;
+ const neutral = cells.i.length * 0.6 * neutralRate; // limit cost for culture growth
+
+ // remove culture from all cells except of locked
+ for (const cellId of cells.i) {
+ const culture = cultures[cells.culture[cellId]];
+ if (culture.lock) continue;
+ cells.culture[cellId] = 0;
+ }
+
+ for (const culture of cultures) {
+ if (!culture.i || culture.removed) continue;
+ queue.queue({e: culture.center, p: 0, c: culture.i});
+ }
+
while (queue.length) {
- const next = queue.dequeue(),
- n = next.e,
- p = next.p,
- c = next.c;
- const type = pack.cultures[c].type;
- cells.c[n].forEach(e => {
- if (pack.cultures[cells.culture[e]]?.lock) return;
+ const {e, p, c} = queue.dequeue();
+ const {type} = pack.cultures[c];
+
+ cells.c[e].forEach(e => {
+ const culture = cells.culture[e];
+ if (culture?.lock) return; // do not overwrite cell of locked culture
const biome = cells.biome[e];
const biomeCost = getBiomeCost(c, biome, type);
- const biomeChangeCost = biome === cells.biome[n] ? 0 : 20; // penalty on biome change
+ const biomeChangeCost = biome === cells.biome[e] ? 0 : 20; // penalty on biome change
const heightCost = getHeightCost(e, cells.h[e], type);
const riverCost = getRiverCost(cells.r[e], e, type);
const typeCost = getTypeCost(cells.t[e], type);
diff --git a/modules/dynamic/editors/states-editor.js b/modules/dynamic/editors/states-editor.js
index 87592706..41241340 100644
--- a/modules/dynamic/editors/states-editor.js
+++ b/modules/dynamic/editors/states-editor.js
@@ -883,7 +883,6 @@ function changeStatesGrowthRate() {
const growthRate = +this.value;
byId("statesNeutral").value = growthRate;
byId("statesNeutralNumber").value = growthRate;
- statesNeutral = growthRate;
tip("Growth rate: " + growthRate);
recalculateStates(false);
}
diff --git a/modules/religions-generator.js b/modules/religions-generator.js
index 44426f59..010dec5e 100644
--- a/modules/religions-generator.js
+++ b/modules/religions-generator.js
@@ -351,9 +351,8 @@ window.Religions = (function () {
const codes = [];
// add folk religions
- pack.cultures.forEach(c => {
- const newId = c.i;
- if (!newId) return religions.push({i: 0, name: "No religion"});
+ cultures.forEach(c => {
+ if (!c.i) return religions.push({i: 0, name: "No religion"});
if (c.removed) {
religions.push({
@@ -365,6 +364,8 @@ window.Religions = (function () {
return;
}
+ const newId = c.i;
+
if (pack.religions) {
const lockedFolkReligion = pack.religions.find(
r => r.culture === c.i && !r.removed && r.lock && r.type === "Folk"
@@ -868,7 +869,6 @@ window.Religions = (function () {
r.culture = cells.culture[r.center];
}
});
- TIME && console.timeEnd("updateCulturesForReligions");
}
// get supreme deity name
diff --git a/modules/ui/editors.js b/modules/ui/editors.js
index 2fb4db07..def092fe 100644
--- a/modules/ui/editors.js
+++ b/modules/ui/editors.js
@@ -1176,7 +1176,7 @@ function refreshAllEditors() {
// dynamically loaded editors
async function editStates() {
if (customization) return;
- const Editor = await import("../dynamic/editors/states-editor.js?v=1.89.02");
+ const Editor = await import("../dynamic/editors/states-editor.js?v=1.89.05");
Editor.open();
}
diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js
index 07216f90..bd41cefe 100644
--- a/modules/ui/heightmap-editor.js
+++ b/modules/ui/heightmap-editor.js
@@ -204,6 +204,13 @@ function editHeightmap(options) {
INFO && console.group("Edit Heightmap");
TIME && console.time("regenerateErasedData");
+ // remove data
+ pack.cultures = [];
+ pack.burgs = [];
+ pack.states = [];
+ pack.provinces = [];
+ pack.religions = [];
+
const erosionAllowed = allowErosion.checked;
markFeatures();
markupGridOcean();
@@ -231,8 +238,10 @@ function editHeightmap(options) {
Lakes.defineGroup();
defineBiomes();
rankCells();
+
Cultures.generate();
Cultures.expand();
+
BurgsAndStates.generate();
Religions.generate();
BurgsAndStates.defineStateForms();