diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js
index da5f0250..fdb758de 100644
--- a/modules/dynamic/auto-update.js
+++ b/modules/dynamic/auto-update.js
@@ -618,17 +618,15 @@ export function resolveVersionConflicts(version) {
}
if (version < 1.86) {
- // v1.86.0 added support of multi-origin culture and religion hierarchy trees
+ // v1.86.0 added multi-origin culture and religion hierarchy trees
for (const culture of pack.cultures) {
- const origin = culture.origin;
+ culture.origins = [culture.origin];
delete culture.origin;
- culture.origins = [origin];
}
for (const religion of pack.religions) {
- const origin = religion.origin;
+ religion.origins = [religion.origin];
delete religion.origin;
- religion.origins = [origin];
}
}
}
diff --git a/modules/dynamic/editors/cultures-editor.js b/modules/dynamic/editors/cultures-editor.js
index 154fe9fe..212c2130 100644
--- a/modules/dynamic/editors/cultures-editor.js
+++ b/modules/dynamic/editors/cultures-editor.js
@@ -295,15 +295,14 @@ function getShapeOptions(selectShape, selected) {
}
function cultureHighlightOn(event) {
- const culture = Number(event.id || event.target.dataset.id);
+ const cultureId = Number(event.id || event.target.dataset.id);
const $info = byId("cultureInfo");
if ($info) {
- d3.select("#hierarchy").select(`g[data-id='${culture}']`).classed("selected", 1);
- const c = pack.cultures[culture];
- const rural = c.rural * populationRate;
- const urban = c.urban * populationRate * urbanization;
- const population = rural + urban > 0 ? si(rn(rural + urban)) + " people" : "Extinct";
- $info.innerHTML = `${c.name} culture. ${c.type}. ${population}`;
+ d3.select("#hierarchy").select(`g[data-id='${cultureId}']`).classed("selected", 1);
+ const {name, type, rural, urban} = pack.cultures[cultureId];
+ const population = rural * populationRate + urban * populationRate * urbanization;
+ const populationText = population > 0 ? si(rn(population)) + " people" : "Extinct";
+ $info.innerHTML = `${name} culture. ${type}. ${populationText}`;
tip("Drag to other node to add parent, click to edit");
}
@@ -312,13 +311,13 @@ function cultureHighlightOn(event) {
const animate = d3.transition().duration(2000).ease(d3.easeSinIn);
cults
- .select("#culture" + culture)
+ .select("#culture" + cultureId)
.raise()
.transition(animate)
.attr("stroke-width", 2.5)
.attr("stroke", "#d0240f");
debug
- .select("#cultureCenter" + culture)
+ .select("#cultureCenter" + cultureId)
.raise()
.transition(animate)
.attr("r", 8)
@@ -326,23 +325,23 @@ function cultureHighlightOn(event) {
}
function cultureHighlightOff(event) {
- const culture = Number(event.id || event.target.dataset.id);
+ const cultureId = Number(event.id || event.target.dataset.id);
const $info = byId("cultureInfo");
if ($info) {
- d3.select("#hierarchy").select(`g[data-id='${culture}']`).classed("selected", 0);
+ d3.select("#hierarchy").select(`g[data-id='${cultureId}']`).classed("selected", 0);
$info.innerHTML = "";
tip("");
}
if (!layerIsOn("toggleCultures")) return;
cults
- .select("#culture" + culture)
+ .select("#culture" + cultureId)
.transition()
.attr("stroke-width", null)
.attr("stroke", null);
debug
- .select("#cultureCenter" + culture)
+ .select("#cultureCenter" + cultureId)
.transition()
.attr("r", 6)
.attr("stroke", null);
@@ -769,7 +768,6 @@ function showHierarchy() {
$("#alert").dialog({
title: "Cultures tree",
width: fitContent(),
- minWidth: "20vw",
resizable: false,
position: {my: "left center", at: "left+10 center", of: "svg"},
buttons: null,
@@ -822,10 +820,10 @@ function showHierarchy() {
if (!selected.size()) return;
const cultureId = d.data.i;
- const newOrigin = Number(selected.datum().data.i);
+ const newOrigin = selected.datum().data.i;
if (cultureId === newOrigin) return; // dragged to itself
if (d.data.origins.includes(newOrigin)) return; // already a child of the selected node
- if (newOrigin && d.descendants().some(node => node.id === newOrigin)) return; // cannot be a child of its own child
+ if (d.descendants().some(node => node.data.i === newOrigin)) return; // cannot be a child of its own child
const culture = pack.cultures[cultureId];
if (culture.origins[0] === 0) culture.origins = [];
diff --git a/modules/religions-generator.js b/modules/religions-generator.js
index abe168ef..bc820384 100644
--- a/modules/religions-generator.js
+++ b/modules/religions-generator.js
@@ -277,7 +277,24 @@ window.Religions = (function () {
"Word",
"World"
],
- color: ["Amber", "Black", "Blue", "Bright", "Brown", "Dark", "Golden", "Green", "Grey", "Light", "Orange", "Pink", "Purple", "Red", "White", "Yellow"]
+ color: [
+ "Amber",
+ "Black",
+ "Blue",
+ "Bright",
+ "Brown",
+ "Dark",
+ "Golden",
+ "Green",
+ "Grey",
+ "Light",
+ "Orange",
+ "Pink",
+ "Purple",
+ "Red",
+ "White",
+ "Yellow"
+ ]
};
const forms = {
@@ -311,7 +328,18 @@ window.Religions = (function () {
Cult: {Cult: 4, Sect: 4, Arcanum: 1, Coterie: 1, Order: 1, Worship: 1},
"Dark Cult": {Cult: 2, Sect: 2, Blasphemy: 1, Circle: 1, Coven: 1, Idols: 1, Occultism: 1},
- Heresy: {Heresy: 3, Sect: 2, Apostates: 1, Brotherhood: 1, Circle: 1, Dissent: 1, Dissenters: 1, Iconoclasm: 1, Schism: 1, Society: 1}
+ Heresy: {
+ Heresy: 3,
+ Sect: 2,
+ Apostates: 1,
+ Brotherhood: 1,
+ Circle: 1,
+ Dissent: 1,
+ Dissenters: 1,
+ Iconoclasm: 1,
+ Schism: 1,
+ Society: 1
+ }
};
const generate = function () {
@@ -324,25 +352,27 @@ window.Religions = (function () {
// add folk religions
pack.cultures.forEach(c => {
- if (!c.i) {
- religions.push({i: 0, name: "No religion"});
- return;
- }
+ if (!c.i) return religions.push({i: 0, name: "No religion"});
+
if (c.removed) {
- religions.push({i: c.i, name: "Extinct religion for " + c.name, color: getMixedColor(c.color, 0.1, 0), removed: true});
+ religions.push({
+ i: c.i,
+ name: "Extinct religion for " + c.name,
+ color: getMixedColor(c.color, 0.1, 0),
+ removed: true
+ });
return;
}
+
const form = rw(forms.Folk);
const name = c.name + " " + rw(types[form]);
const deity = form === "Animism" ? null : getDeityName(c.i);
const color = getMixedColor(c.color, 0.1, 0); // `url(#hatch${rand(8,13)})`;
- religions.push({i: c.i, name, color, culture: c.i, type: "Folk", form, deity, center: c.center, origin: 0});
+ religions.push({i: c.i, name, color, culture: c.i, type: "Folk", form, deity, center: c.center, origins: [0]});
});
- if (religionsInput.value == 0 || pack.cultures.length < 2) {
- religions.filter(r => r.i).forEach(r => (r.code = abbreviate(r.name)));
- return;
- }
+ if (religionsInput.value == 0 || pack.cultures.length < 2)
+ return religions.filter(r => r.i).forEach(r => (r.code = abbreviate(r.name)));
const burgs = pack.burgs.filter(b => b.i && !b.removed);
const sorted =
@@ -354,6 +384,12 @@ window.Religions = (function () {
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
const count = +religionsInput.value - cultsCount + religions.length;
+ function getReligionsInRadius({x, y, r, max}) {
+ const cellsInRadius = findAll(x, y, r);
+ const religions = unique(cellsInRadius.map(i => cells.religion[i]).filter(r => r));
+ return religions.length ? religions.slice(0, max) : [0];
+ }
+
// generate organized religions
for (let i = 0; religions.length < count && i < 1000; i++) {
let center = sorted[biased(0, sorted.length - 1, 5)]; // religion center
@@ -369,21 +405,35 @@ window.Religions = (function () {
if (expansion === "state" && Math.random() > 0.5) center = states[state].center;
if (expansion === "culture" && Math.random() > 0.5) center = cultures[culture].center;
- if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
- const x = cells.p[center][0],
- y = cells.p[center][1];
+ if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
+ center = cells.c[center].find(c => cells.burg[c]);
+ const [x, y] = cells.p[center];
const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make the placement not uniform
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
// add "Old" to name of the folk religion on this culture
- const folk = religions.find(r => r.culture === culture && r.type === "Folk");
+ const isFolkBased = expansion === "culture" || P(0.5);
+ const folk = isFolkBased && religions.find(r => r.culture === culture && r.type === "Folk");
if (folk && expansion === "culture" && folk.name.slice(0, 3) !== "Old") folk.name = "Old " + folk.name;
- const origin = folk ? folk.i : 0;
+ const origins = folk ? [folk.i] : getReligionsInRadius({x, y, r: 30, max: 2});
const expansionism = rand(3, 8);
- const color = getMixedColor(religions[origin].color, 0.3, 0); // `url(#hatch${rand(0,5)})`;
- religions.push({i: religions.length, name, color, culture, type: "Organized", form, deity, expansion, expansionism, center, origin});
+ const baseColor = religions[culture]?.color || states[state]?.color || getRandomColor();
+ const color = getMixedColor(baseColor, 0.3, 0);
+ religions.push({
+ i: religions.length,
+ name,
+ color,
+ culture,
+ type: "Organized",
+ form,
+ deity,
+ expansion,
+ expansionism,
+ center,
+ origins
+ });
religionsTree.add([x, y]);
}
@@ -391,23 +441,34 @@ window.Religions = (function () {
for (let i = 0; religions.length < count + cultsCount && i < 1000; i++) {
const form = rw(forms.Cult);
let center = sorted[biased(0, sorted.length - 1, 1)]; // religion center
- if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
- const x = cells.p[center][0],
- y = cells.p[center][1];
+ if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
+ center = cells.c[center].find(c => cells.burg[c]);
+ const [x, y] = cells.p[center];
const s = spacing * gauss(2, 0.3, 1, 3, 2); // randomize to make the placement not uniform
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
const culture = cells.culture[center];
- const folk = religions.find(r => r.culture === culture && r.type === "Folk");
- const origin = folk ? folk.i : 0;
+ const origins = getReligionsInRadius({x, y, r: 75, max: rand(0, 4)});
+
const deity = getDeityName(culture);
const name = getCultName(form, center);
const expansionism = gauss(1.1, 0.5, 0, 5);
const color = getMixedColor(cultures[culture].color, 0.5, 0); // "url(#hatch7)";
- religions.push({i: religions.length, name, color, culture, type: "Cult", form, deity, expansion: "global", expansionism, center, origin});
+ religions.push({
+ i: religions.length,
+ name,
+ color,
+ culture,
+ type: "Cult",
+ form,
+ deity,
+ expansion: "global",
+ expansionism,
+ center,
+ origins
+ });
religionsTree.add([x, y]);
- //debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "red");
}
expandReligions();
@@ -419,11 +480,13 @@ window.Religions = (function () {
if (r.expansionism < 3) return;
const count = gauss(0, 1, 0, 3);
for (let i = 0; i < count; i++) {
- let center = ra(cells.i.filter(i => cells.religion[i] === r.i && cells.c[i].some(c => cells.religion[c] !== r.i)));
+ let center = ra(
+ cells.i.filter(i => cells.religion[i] === r.i && cells.c[i].some(c => cells.religion[c] !== r.i))
+ );
if (!center) continue;
- if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
- const x = cells.p[center][0],
- y = cells.p[center][1];
+ if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
+ center = cells.c[center].find(c => cells.burg[c]);
+ const [x, y] = cells.p[center];
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other
const culture = cells.culture[center];
@@ -441,7 +504,7 @@ window.Religions = (function () {
expansion: "global",
expansionism,
center,
- origin: r.i
+ origins: [r.i]
});
religionsTree.add([x, y]);
}
@@ -461,7 +524,8 @@ window.Religions = (function () {
const culture = cells.culture[center];
const color = getMixedColor(religions[r].color, 0.3, 0);
- const type = religions[r].type === "Organized" ? rw({Organized: 4, Cult: 1, Heresy: 2}) : rw({Organized: 5, Cult: 2});
+ const type =
+ religions[r].type === "Organized" ? rw({Organized: 4, Cult: 1, Heresy: 2}) : rw({Organized: 5, Cult: 2});
const form = rw(forms[type]);
const deity = type === "Heresy" ? religions[r].deity : form === "Non-theism" ? null : getDeityName(culture);
@@ -491,7 +555,7 @@ window.Religions = (function () {
area: 0,
rural: 0,
urban: 0,
- origin: r,
+ origins: [r],
code
});
cells.religion[center] = i;
@@ -534,7 +598,9 @@ window.Religions = (function () {
const populationCost = Math.max(rn(popCost - cells.pop[e]), 0);
const heightCost = Math.max(cells.h[e], 20) - 20;
const waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
- const totalCost = p + (cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
+ const totalCost =
+ p +
+ (cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
if (totalCost > neutral) return;
if (!cost[e] || totalCost < cost[e]) {
@@ -576,7 +642,8 @@ window.Religions = (function () {
const biomeCost = cells.road[e] ? 0 : biomesData.cost[cells.biome[e]];
const heightCost = Math.max(cells.h[e], 20) - 20;
const waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
- const totalCost = p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
+ const totalCost =
+ p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
if (totalCost > neutral) return;
@@ -641,35 +708,34 @@ window.Religions = (function () {
if (a === "Being + of + Genitive") return ra(base.being) + " of " + ra(base.genitive);
if (a === "Being + of the + Genitive") return ra(base.being) + " of the " + ra(base.theGenitive);
if (a === "Animal + of + Genitive") return ra(base.animal) + " of " + ra(base.genitive);
- if (a === "Adjective + Being + of + Genitive") return ra(base.adjective) + " " + ra(base.being) + " of " + ra(base.genitive);
- if (a === "Adjective + Animal + of + Genitive") return ra(base.adjective) + " " + ra(base.animal) + " of " + ra(base.genitive);
+ if (a === "Adjective + Being + of + Genitive")
+ return ra(base.adjective) + " " + ra(base.being) + " of " + ra(base.genitive);
+ if (a === "Adjective + Animal + of + Genitive")
+ return ra(base.adjective) + " " + ra(base.animal) + " of " + ra(base.genitive);
}
function getReligionName(form, deity, center) {
- const cells = pack.cells;
- const random = function () {
- return Names.getCulture(cells.culture[center], null, null, "", 0);
- };
- const type = function () {
- return rw(types[form]);
- };
- const supreme = function () {
- return deity.split(/[ ,]+/)[0];
- };
- const place = function (adj) {
- const base = cells.burg[center] ? pack.burgs[cells.burg[center]].name : pack.states[cells.state[center]].name;
+ const {cells, cultures, burgs, states} = pack;
+
+ const random = () => Names.getCulture(cells.culture[center], null, null, "", 0);
+ const type = () => rw(types[form]);
+ const supreme = () => deity.split(/[ ,]+/)[0];
+ const culture = () => cultures[cells.culture[center]].name;
+ const place = adj => {
+ const burgId = cells.burg[center];
+ const stateId = cells.state[center];
+
+ const base = burgId ? burgs[burgId].name : states[stateId].name;
let name = trimVowels(base.split(/[ ,]+/)[0]);
return adj ? getAdjective(name) : name;
};
- const culture = function () {
- return pack.cultures[cells.culture[center]].name;
- };
const m = rw(methods);
if (m === "Random + type") return [random() + " " + type(), "global"];
if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"];
if (m === "Supreme + ism" && deity) return [trimVowels(supreme()) + "ism", "global"];
- if (m === "Faith of + Supreme" && deity) return [ra(["Faith", "Way", "Path", "Word", "Witnesses"]) + " of " + supreme(), "global"];
+ if (m === "Faith of + Supreme" && deity)
+ return [ra(["Faith", "Way", "Path", "Word", "Witnesses"]) + " of " + supreme(), "global"];
if (m === "Place + ism") return [place() + "ism", "state"];
if (m === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
diff --git a/modules/ui/religions-editor.js b/modules/ui/religions-editor.js
index e4e9d174..8fbec3ac 100644
--- a/modules/ui/religions-editor.js
+++ b/modules/ui/religions-editor.js
@@ -8,7 +8,7 @@ function editReligions() {
if (layerIsOn("toggleBiomes")) toggleBiomes();
if (layerIsOn("toggleProvinces")) toggleProvinces();
- const body = document.getElementById("religionsBody");
+ const body = byId("religionsBody");
const animate = d3.transition().duration(1500).ease(d3.easeSinIn);
refreshReligionsEditor();
drawReligionCenters();
@@ -25,17 +25,17 @@ function editReligions() {
});
// add listeners
- document.getElementById("religionsEditorRefresh").addEventListener("click", refreshReligionsEditor);
- document.getElementById("religionsEditStyle").addEventListener("click", () => editStyle("relig"));
- document.getElementById("religionsLegend").addEventListener("click", toggleLegend);
- document.getElementById("religionsPercentage").addEventListener("click", togglePercentageMode);
- document.getElementById("religionsHeirarchy").addEventListener("click", showHierarchy);
- document.getElementById("religionsExtinct").addEventListener("click", toggleExtinct);
- document.getElementById("religionsManually").addEventListener("click", enterReligionsManualAssignent);
- document.getElementById("religionsManuallyApply").addEventListener("click", applyReligionsManualAssignent);
- document.getElementById("religionsManuallyCancel").addEventListener("click", () => exitReligionsManualAssignment());
- document.getElementById("religionsAdd").addEventListener("click", enterAddReligionMode);
- document.getElementById("religionsExport").addEventListener("click", downloadReligionsData);
+ byId("religionsEditorRefresh").on("click", refreshReligionsEditor);
+ byId("religionsEditStyle").on("click", () => editStyle("relig"));
+ byId("religionsLegend").on("click", toggleLegend);
+ byId("religionsPercentage").on("click", togglePercentageMode);
+ byId("religionsHeirarchy").on("click", showHierarchy);
+ byId("religionsExtinct").on("click", toggleExtinct);
+ byId("religionsManually").on("click", enterReligionsManualAssignent);
+ byId("religionsManuallyApply").on("click", applyReligionsManualAssignent);
+ byId("religionsManuallyCancel").on("click", () => exitReligionsManualAssignment());
+ byId("religionsAdd").on("click", enterAddReligionMode);
+ byId("religionsExport").on("click", downloadReligionsData);
function refreshReligionsEditor() {
religionsCollectStatistics();
@@ -72,7 +72,9 @@ function editReligions() {
const urban = r.urban * populationRate * urbanization;
const population = rn(rural + urban);
if (r.i && !r.cells && body.dataset.extinct !== "show") continue; // hide extinct religions
- const populationTip = `Believers: ${si(population)}; Rural areas: ${si(rural)}; Urban areas: ${si(urban)}. Click to change`;
+ const populationTip = `Believers: ${si(population)}; Rural areas: ${si(rural)}; Urban areas: ${si(
+ urban
+ )}. Click to change`;
totalArea += area;
totalPopulation += population;
@@ -90,13 +92,19 @@ function editReligions() {
data-expansionism=${r.expansionism}
>
Total believers: ${l(total)} ⇒ ${l(total)} (100%)
`; + +Total believers: ${l(total)} ⇒ ${l( + total + )} (100%)
`; const update = function () { const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber; @@ -367,7 +383,7 @@ function editReligions() { function religionRemove() { if (customization) return; - const religion = +this.parentNode.dataset.id; + const religionId = +this.parentNode.dataset.id; alertMessage.innerHTML = "Are you sure you want to remove the religion?