mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
feat: multi-parental tree for religions
This commit is contained in:
parent
23ff50bd76
commit
ff1d3566c4
4 changed files with 346 additions and 220 deletions
|
|
@ -618,17 +618,15 @@ export function resolveVersionConflicts(version) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version < 1.86) {
|
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) {
|
for (const culture of pack.cultures) {
|
||||||
const origin = culture.origin;
|
culture.origins = [culture.origin];
|
||||||
delete culture.origin;
|
delete culture.origin;
|
||||||
culture.origins = [origin];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const religion of pack.religions) {
|
for (const religion of pack.religions) {
|
||||||
const origin = religion.origin;
|
religion.origins = [religion.origin];
|
||||||
delete religion.origin;
|
delete religion.origin;
|
||||||
religion.origins = [origin];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,15 +295,14 @@ function getShapeOptions(selectShape, selected) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cultureHighlightOn(event) {
|
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");
|
const $info = byId("cultureInfo");
|
||||||
if ($info) {
|
if ($info) {
|
||||||
d3.select("#hierarchy").select(`g[data-id='${culture}']`).classed("selected", 1);
|
d3.select("#hierarchy").select(`g[data-id='${cultureId}']`).classed("selected", 1);
|
||||||
const c = pack.cultures[culture];
|
const {name, type, rural, urban} = pack.cultures[cultureId];
|
||||||
const rural = c.rural * populationRate;
|
const population = rural * populationRate + urban * populationRate * urbanization;
|
||||||
const urban = c.urban * populationRate * urbanization;
|
const populationText = population > 0 ? si(rn(population)) + " people" : "Extinct";
|
||||||
const population = rural + urban > 0 ? si(rn(rural + urban)) + " people" : "Extinct";
|
$info.innerHTML = `${name} culture. ${type}. ${populationText}`;
|
||||||
$info.innerHTML = `${c.name} culture. ${c.type}. ${population}`;
|
|
||||||
tip("Drag to other node to add parent, click to edit");
|
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);
|
const animate = d3.transition().duration(2000).ease(d3.easeSinIn);
|
||||||
cults
|
cults
|
||||||
.select("#culture" + culture)
|
.select("#culture" + cultureId)
|
||||||
.raise()
|
.raise()
|
||||||
.transition(animate)
|
.transition(animate)
|
||||||
.attr("stroke-width", 2.5)
|
.attr("stroke-width", 2.5)
|
||||||
.attr("stroke", "#d0240f");
|
.attr("stroke", "#d0240f");
|
||||||
debug
|
debug
|
||||||
.select("#cultureCenter" + culture)
|
.select("#cultureCenter" + cultureId)
|
||||||
.raise()
|
.raise()
|
||||||
.transition(animate)
|
.transition(animate)
|
||||||
.attr("r", 8)
|
.attr("r", 8)
|
||||||
|
|
@ -326,23 +325,23 @@ function cultureHighlightOn(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cultureHighlightOff(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");
|
const $info = byId("cultureInfo");
|
||||||
if ($info) {
|
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 = "‍";
|
$info.innerHTML = "‍";
|
||||||
tip("");
|
tip("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!layerIsOn("toggleCultures")) return;
|
if (!layerIsOn("toggleCultures")) return;
|
||||||
cults
|
cults
|
||||||
.select("#culture" + culture)
|
.select("#culture" + cultureId)
|
||||||
.transition()
|
.transition()
|
||||||
.attr("stroke-width", null)
|
.attr("stroke-width", null)
|
||||||
.attr("stroke", null);
|
.attr("stroke", null);
|
||||||
debug
|
debug
|
||||||
.select("#cultureCenter" + culture)
|
.select("#cultureCenter" + cultureId)
|
||||||
.transition()
|
.transition()
|
||||||
.attr("r", 6)
|
.attr("r", 6)
|
||||||
.attr("stroke", null);
|
.attr("stroke", null);
|
||||||
|
|
@ -769,7 +768,6 @@ function showHierarchy() {
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
title: "Cultures tree",
|
title: "Cultures tree",
|
||||||
width: fitContent(),
|
width: fitContent(),
|
||||||
minWidth: "20vw",
|
|
||||||
resizable: false,
|
resizable: false,
|
||||||
position: {my: "left center", at: "left+10 center", of: "svg"},
|
position: {my: "left center", at: "left+10 center", of: "svg"},
|
||||||
buttons: null,
|
buttons: null,
|
||||||
|
|
@ -822,10 +820,10 @@ function showHierarchy() {
|
||||||
if (!selected.size()) return;
|
if (!selected.size()) return;
|
||||||
|
|
||||||
const cultureId = d.data.i;
|
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 (cultureId === newOrigin) return; // dragged to itself
|
||||||
if (d.data.origins.includes(newOrigin)) return; // already a child of the selected node
|
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];
|
const culture = pack.cultures[cultureId];
|
||||||
if (culture.origins[0] === 0) culture.origins = [];
|
if (culture.origins[0] === 0) culture.origins = [];
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,24 @@ window.Religions = (function () {
|
||||||
"Word",
|
"Word",
|
||||||
"World"
|
"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 = {
|
const forms = {
|
||||||
|
|
@ -311,7 +328,18 @@ window.Religions = (function () {
|
||||||
Cult: {Cult: 4, Sect: 4, Arcanum: 1, Coterie: 1, Order: 1, Worship: 1},
|
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},
|
"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 () {
|
const generate = function () {
|
||||||
|
|
@ -324,25 +352,27 @@ window.Religions = (function () {
|
||||||
|
|
||||||
// add folk religions
|
// add folk religions
|
||||||
pack.cultures.forEach(c => {
|
pack.cultures.forEach(c => {
|
||||||
if (!c.i) {
|
if (!c.i) return religions.push({i: 0, name: "No religion"});
|
||||||
religions.push({i: 0, name: "No religion"});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (c.removed) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const form = rw(forms.Folk);
|
const form = rw(forms.Folk);
|
||||||
const name = c.name + " " + rw(types[form]);
|
const name = c.name + " " + rw(types[form]);
|
||||||
const deity = form === "Animism" ? null : getDeityName(c.i);
|
const deity = form === "Animism" ? null : getDeityName(c.i);
|
||||||
const color = getMixedColor(c.color, 0.1, 0); // `url(#hatch${rand(8,13)})`;
|
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) {
|
if (religionsInput.value == 0 || pack.cultures.length < 2)
|
||||||
religions.filter(r => r.i).forEach(r => (r.code = abbreviate(r.name)));
|
return religions.filter(r => r.i).forEach(r => (r.code = abbreviate(r.name)));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||||
const sorted =
|
const sorted =
|
||||||
|
|
@ -354,6 +384,12 @@ window.Religions = (function () {
|
||||||
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
||||||
const count = +religionsInput.value - cultsCount + religions.length;
|
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
|
// generate organized religions
|
||||||
for (let i = 0; religions.length < count && i < 1000; i++) {
|
for (let i = 0; religions.length < count && i < 1000; i++) {
|
||||||
let center = sorted[biased(0, sorted.length - 1, 5)]; // religion center
|
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 === "state" && Math.random() > 0.5) center = states[state].center;
|
||||||
if (expansion === "culture" && Math.random() > 0.5) center = cultures[culture].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]);
|
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
||||||
const x = cells.p[center][0],
|
center = cells.c[center].find(c => cells.burg[c]);
|
||||||
y = cells.p[center][1];
|
const [x, y] = cells.p[center];
|
||||||
|
|
||||||
const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make the placement not uniform
|
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
|
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
|
||||||
|
|
||||||
// add "Old" to name of the folk religion on this culture
|
// 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;
|
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 expansionism = rand(3, 8);
|
||||||
const color = getMixedColor(religions[origin].color, 0.3, 0); // `url(#hatch${rand(0,5)})`;
|
const baseColor = religions[culture]?.color || states[state]?.color || getRandomColor();
|
||||||
religions.push({i: religions.length, name, color, culture, type: "Organized", form, deity, expansion, expansionism, center, origin});
|
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]);
|
religionsTree.add([x, y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -391,23 +441,34 @@ window.Religions = (function () {
|
||||||
for (let i = 0; religions.length < count + cultsCount && i < 1000; i++) {
|
for (let i = 0; religions.length < count + cultsCount && i < 1000; i++) {
|
||||||
const form = rw(forms.Cult);
|
const form = rw(forms.Cult);
|
||||||
let center = sorted[biased(0, sorted.length - 1, 1)]; // religion center
|
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]);
|
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
||||||
const x = cells.p[center][0],
|
center = cells.c[center].find(c => cells.burg[c]);
|
||||||
y = cells.p[center][1];
|
const [x, y] = cells.p[center];
|
||||||
|
|
||||||
const s = spacing * gauss(2, 0.3, 1, 3, 2); // randomize to make the placement not uniform
|
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
|
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
|
||||||
|
|
||||||
const culture = cells.culture[center];
|
const culture = cells.culture[center];
|
||||||
const folk = religions.find(r => r.culture === culture && r.type === "Folk");
|
const origins = getReligionsInRadius({x, y, r: 75, max: rand(0, 4)});
|
||||||
const origin = folk ? folk.i : 0;
|
|
||||||
const deity = getDeityName(culture);
|
const deity = getDeityName(culture);
|
||||||
const name = getCultName(form, center);
|
const name = getCultName(form, center);
|
||||||
const expansionism = gauss(1.1, 0.5, 0, 5);
|
const expansionism = gauss(1.1, 0.5, 0, 5);
|
||||||
const color = getMixedColor(cultures[culture].color, 0.5, 0); // "url(#hatch7)";
|
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]);
|
religionsTree.add([x, y]);
|
||||||
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "red");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expandReligions();
|
expandReligions();
|
||||||
|
|
@ -419,11 +480,13 @@ window.Religions = (function () {
|
||||||
if (r.expansionism < 3) return;
|
if (r.expansionism < 3) return;
|
||||||
const count = gauss(0, 1, 0, 3);
|
const count = gauss(0, 1, 0, 3);
|
||||||
for (let i = 0; i < count; i++) {
|
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 (!center) continue;
|
||||||
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
|
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
||||||
const x = cells.p[center][0],
|
center = cells.c[center].find(c => cells.burg[c]);
|
||||||
y = cells.p[center][1];
|
const [x, y] = cells.p[center];
|
||||||
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other
|
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other
|
||||||
|
|
||||||
const culture = cells.culture[center];
|
const culture = cells.culture[center];
|
||||||
|
|
@ -441,7 +504,7 @@ window.Religions = (function () {
|
||||||
expansion: "global",
|
expansion: "global",
|
||||||
expansionism,
|
expansionism,
|
||||||
center,
|
center,
|
||||||
origin: r.i
|
origins: [r.i]
|
||||||
});
|
});
|
||||||
religionsTree.add([x, y]);
|
religionsTree.add([x, y]);
|
||||||
}
|
}
|
||||||
|
|
@ -461,7 +524,8 @@ window.Religions = (function () {
|
||||||
const culture = cells.culture[center];
|
const culture = cells.culture[center];
|
||||||
const color = getMixedColor(religions[r].color, 0.3, 0);
|
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 form = rw(forms[type]);
|
||||||
const deity = type === "Heresy" ? religions[r].deity : form === "Non-theism" ? null : getDeityName(culture);
|
const deity = type === "Heresy" ? religions[r].deity : form === "Non-theism" ? null : getDeityName(culture);
|
||||||
|
|
||||||
|
|
@ -491,7 +555,7 @@ window.Religions = (function () {
|
||||||
area: 0,
|
area: 0,
|
||||||
rural: 0,
|
rural: 0,
|
||||||
urban: 0,
|
urban: 0,
|
||||||
origin: r,
|
origins: [r],
|
||||||
code
|
code
|
||||||
});
|
});
|
||||||
cells.religion[center] = i;
|
cells.religion[center] = i;
|
||||||
|
|
@ -534,7 +598,9 @@ window.Religions = (function () {
|
||||||
const populationCost = Math.max(rn(popCost - cells.pop[e]), 0);
|
const populationCost = Math.max(rn(popCost - cells.pop[e]), 0);
|
||||||
const heightCost = Math.max(cells.h[e], 20) - 20;
|
const heightCost = Math.max(cells.h[e], 20) - 20;
|
||||||
const waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
|
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 (totalCost > neutral) return;
|
||||||
|
|
||||||
if (!cost[e] || totalCost < cost[e]) {
|
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 biomeCost = cells.road[e] ? 0 : biomesData.cost[cells.biome[e]];
|
||||||
const heightCost = Math.max(cells.h[e], 20) - 20;
|
const heightCost = Math.max(cells.h[e], 20) - 20;
|
||||||
const waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
|
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;
|
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 + 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 === "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 === "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 + Being + of + Genitive")
|
||||||
if (a === "Adjective + Animal + of + Genitive") return ra(base.adjective) + " " + ra(base.animal) + " of " + ra(base.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) {
|
function getReligionName(form, deity, center) {
|
||||||
const cells = pack.cells;
|
const {cells, cultures, burgs, states} = pack;
|
||||||
const random = function () {
|
|
||||||
return Names.getCulture(cells.culture[center], null, null, "", 0);
|
const random = () => Names.getCulture(cells.culture[center], null, null, "", 0);
|
||||||
};
|
const type = () => rw(types[form]);
|
||||||
const type = function () {
|
const supreme = () => deity.split(/[ ,]+/)[0];
|
||||||
return rw(types[form]);
|
const culture = () => cultures[cells.culture[center]].name;
|
||||||
};
|
const place = adj => {
|
||||||
const supreme = function () {
|
const burgId = cells.burg[center];
|
||||||
return deity.split(/[ ,]+/)[0];
|
const stateId = cells.state[center];
|
||||||
};
|
|
||||||
const place = function (adj) {
|
const base = burgId ? burgs[burgId].name : states[stateId].name;
|
||||||
const base = cells.burg[center] ? pack.burgs[cells.burg[center]].name : pack.states[cells.state[center]].name;
|
|
||||||
let name = trimVowels(base.split(/[ ,]+/)[0]);
|
let name = trimVowels(base.split(/[ ,]+/)[0]);
|
||||||
return adj ? getAdjective(name) : name;
|
return adj ? getAdjective(name) : name;
|
||||||
};
|
};
|
||||||
const culture = function () {
|
|
||||||
return pack.cultures[cells.culture[center]].name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const m = rw(methods);
|
const m = rw(methods);
|
||||||
if (m === "Random + type") return [random() + " " + type(), "global"];
|
if (m === "Random + type") return [random() + " " + type(), "global"];
|
||||||
if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"];
|
if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"];
|
||||||
if (m === "Supreme + ism" && deity) return [trimVowels(supreme()) + "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 === "Place + ism") return [place() + "ism", "state"];
|
||||||
if (m === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
|
if (m === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
|
||||||
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
|
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ function editReligions() {
|
||||||
if (layerIsOn("toggleBiomes")) toggleBiomes();
|
if (layerIsOn("toggleBiomes")) toggleBiomes();
|
||||||
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
||||||
|
|
||||||
const body = document.getElementById("religionsBody");
|
const body = byId("religionsBody");
|
||||||
const animate = d3.transition().duration(1500).ease(d3.easeSinIn);
|
const animate = d3.transition().duration(1500).ease(d3.easeSinIn);
|
||||||
refreshReligionsEditor();
|
refreshReligionsEditor();
|
||||||
drawReligionCenters();
|
drawReligionCenters();
|
||||||
|
|
@ -25,17 +25,17 @@ function editReligions() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
document.getElementById("religionsEditorRefresh").addEventListener("click", refreshReligionsEditor);
|
byId("religionsEditorRefresh").on("click", refreshReligionsEditor);
|
||||||
document.getElementById("religionsEditStyle").addEventListener("click", () => editStyle("relig"));
|
byId("religionsEditStyle").on("click", () => editStyle("relig"));
|
||||||
document.getElementById("religionsLegend").addEventListener("click", toggleLegend);
|
byId("religionsLegend").on("click", toggleLegend);
|
||||||
document.getElementById("religionsPercentage").addEventListener("click", togglePercentageMode);
|
byId("religionsPercentage").on("click", togglePercentageMode);
|
||||||
document.getElementById("religionsHeirarchy").addEventListener("click", showHierarchy);
|
byId("religionsHeirarchy").on("click", showHierarchy);
|
||||||
document.getElementById("religionsExtinct").addEventListener("click", toggleExtinct);
|
byId("religionsExtinct").on("click", toggleExtinct);
|
||||||
document.getElementById("religionsManually").addEventListener("click", enterReligionsManualAssignent);
|
byId("religionsManually").on("click", enterReligionsManualAssignent);
|
||||||
document.getElementById("religionsManuallyApply").addEventListener("click", applyReligionsManualAssignent);
|
byId("religionsManuallyApply").on("click", applyReligionsManualAssignent);
|
||||||
document.getElementById("religionsManuallyCancel").addEventListener("click", () => exitReligionsManualAssignment());
|
byId("religionsManuallyCancel").on("click", () => exitReligionsManualAssignment());
|
||||||
document.getElementById("religionsAdd").addEventListener("click", enterAddReligionMode);
|
byId("religionsAdd").on("click", enterAddReligionMode);
|
||||||
document.getElementById("religionsExport").addEventListener("click", downloadReligionsData);
|
byId("religionsExport").on("click", downloadReligionsData);
|
||||||
|
|
||||||
function refreshReligionsEditor() {
|
function refreshReligionsEditor() {
|
||||||
religionsCollectStatistics();
|
religionsCollectStatistics();
|
||||||
|
|
@ -72,7 +72,9 @@ function editReligions() {
|
||||||
const urban = r.urban * populationRate * urbanization;
|
const urban = r.urban * populationRate * urbanization;
|
||||||
const population = rn(rural + urban);
|
const population = rn(rural + urban);
|
||||||
if (r.i && !r.cells && body.dataset.extinct !== "show") continue; // hide extinct religions
|
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;
|
totalArea += area;
|
||||||
totalPopulation += population;
|
totalPopulation += population;
|
||||||
|
|
||||||
|
|
@ -90,13 +92,19 @@ function editReligions() {
|
||||||
data-expansionism=${r.expansionism}
|
data-expansionism=${r.expansionism}
|
||||||
>
|
>
|
||||||
<fill-box fill="${r.color}"></fill-box>
|
<fill-box fill="${r.color}"></fill-box>
|
||||||
<input data-tip="Religion name. Click and type to change" class="religionName" value="${r.name}" autocorrect="off" spellcheck="false" />
|
<input data-tip="Religion name. Click and type to change" class="religionName" value="${
|
||||||
|
r.name
|
||||||
|
}" autocorrect="off" spellcheck="false" />
|
||||||
<select data-tip="Religion type" class="religionType">
|
<select data-tip="Religion type" class="religionType">
|
||||||
${getTypeOptions(r.type)}
|
${getTypeOptions(r.type)}
|
||||||
</select>
|
</select>
|
||||||
<input data-tip="Religion form" class="religionForm hide" value="${r.form}" autocorrect="off" spellcheck="false" />
|
<input data-tip="Religion form" class="religionForm hide" value="${
|
||||||
|
r.form
|
||||||
|
}" autocorrect="off" spellcheck="false" />
|
||||||
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw hide"></span>
|
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw hide"></span>
|
||||||
<input data-tip="Religion supreme deity" class="religionDeity hide" value="${r.deity ? r.deity : ""}" autocorrect="off" spellcheck="false" />
|
<input data-tip="Religion supreme deity" class="religionDeity hide" value="${
|
||||||
|
r.deity ? r.deity : ""
|
||||||
|
}" autocorrect="off" spellcheck="false" />
|
||||||
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||||
<div data-tip="Religion area" class="biomeArea hide">${si(area) + unit}</div>
|
<div data-tip="Religion area" class="biomeArea hide">${si(area) + unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
|
|
@ -118,7 +126,9 @@ function editReligions() {
|
||||||
data-expansionism=""
|
data-expansionism=""
|
||||||
>
|
>
|
||||||
<svg width="11" height="11" class="placeholder"></svg>
|
<svg width="11" height="11" class="placeholder"></svg>
|
||||||
<input data-tip="Religion name. Click and type to change" class="religionName italic" value="${r.name}" autocorrect="off" spellcheck="false" />
|
<input data-tip="Religion name. Click and type to change" class="religionName italic" value="${
|
||||||
|
r.name
|
||||||
|
}" autocorrect="off" spellcheck="false" />
|
||||||
<select data-tip="Religion type" class="religionType placeholder">
|
<select data-tip="Religion type" class="religionType placeholder">
|
||||||
${getTypeOptions(r.type)}
|
${getTypeOptions(r.type)}
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -146,17 +156,17 @@ function editReligions() {
|
||||||
religionsFooterPopulation.dataset.population = totalPopulation;
|
religionsFooterPopulation.dataset.population = totalPopulation;
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseenter", ev => religionHighlightOn(ev)));
|
body.querySelectorAll("div.religions").forEach(el => el.on("mouseenter", religionHighlightOn));
|
||||||
body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseleave", ev => religionHighlightOff(ev)));
|
body.querySelectorAll("div.religions").forEach(el => el.on("mouseleave", religionHighlightOff));
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectReligionOnLineClick));
|
body.querySelectorAll("div.states").forEach(el => el.on("click", selectReligionOnLineClick));
|
||||||
body.querySelectorAll("fill-box").forEach(el => el.addEventListener("click", religionChangeColor));
|
body.querySelectorAll("fill-box").forEach(el => el.on("click", religionChangeColor));
|
||||||
body.querySelectorAll("div > input.religionName").forEach(el => el.addEventListener("input", religionChangeName));
|
body.querySelectorAll("div > input.religionName").forEach(el => el.on("input", religionChangeName));
|
||||||
body.querySelectorAll("div > select.religionType").forEach(el => el.addEventListener("change", religionChangeType));
|
body.querySelectorAll("div > select.religionType").forEach(el => el.on("change", religionChangeType));
|
||||||
body.querySelectorAll("div > input.religionForm").forEach(el => el.addEventListener("input", religionChangeForm));
|
body.querySelectorAll("div > input.religionForm").forEach(el => el.on("input", religionChangeForm));
|
||||||
body.querySelectorAll("div > input.religionDeity").forEach(el => el.addEventListener("input", religionChangeDeity));
|
body.querySelectorAll("div > input.religionDeity").forEach(el => el.on("input", religionChangeDeity));
|
||||||
body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.addEventListener("click", regenerateDeity));
|
body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.on("click", regenerateDeity));
|
||||||
body.querySelectorAll("div > div.culturePopulation").forEach(el => el.addEventListener("click", changePopulation));
|
body.querySelectorAll("div > div.culturePopulation").forEach(el => el.on("click", changePopulation));
|
||||||
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", religionRemove));
|
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.on("click", religionRemove));
|
||||||
|
|
||||||
if (body.dataset.type === "percentage") {
|
if (body.dataset.type === "percentage") {
|
||||||
body.dataset.type = "absolute";
|
body.dataset.type = "absolute";
|
||||||
|
|
@ -174,35 +184,39 @@ function editReligions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function religionHighlightOn(event) {
|
function religionHighlightOn(event) {
|
||||||
const religion = +event.target.dataset.id;
|
const religionId = Number(event.id || event.target.dataset.id);
|
||||||
const info = document.getElementById("religionInfo");
|
const $info = byId("religionInfo");
|
||||||
if (info) {
|
if ($info) {
|
||||||
d3.select("#hierarchy")
|
d3.select("#hierarchy").select(`g[data-id='${religionId}']`).classed("selected", 1);
|
||||||
.select("g[data-id='" + religion + "'] > path")
|
const {name, type, form, rural, urban} = pack.religions[religionId];
|
||||||
.classed("selected", 1);
|
|
||||||
const r = pack.religions[religion];
|
const getTypeText = () => {
|
||||||
const type = r.name.includes(r.type) ? "" : r.type === "Folk" || r.type === "Organized" ? ". " + r.type + " religion" : ". " + r.type;
|
if (name.includes(type)) return "";
|
||||||
const form = r.form === r.type || r.name.includes(r.form) ? "" : ". " + r.form;
|
if (form.includes(type)) return "";
|
||||||
const rural = r.rural * populationRate;
|
if (type === "Folk" || type === "Organized") return `. ${type} religion`;
|
||||||
const urban = r.urban * populationRate * urbanization;
|
return `. ${type}`;
|
||||||
const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct";
|
};
|
||||||
info.innerHTML = /* html */ `${r.name}${type}${form}${population}`;
|
const formText = form === type ? "" : ". " + form;
|
||||||
tip("Drag to change parent, drag to itself to move to the top level. Hold CTRL and click to change abbreviation");
|
const population = rural * populationRate + urban * populationRate * urbanization;
|
||||||
|
const populationText = population > 0 ? si(rn(population)) + " people" : "Extinct";
|
||||||
|
|
||||||
|
$info.innerHTML = `${name}${getTypeText()}${formText}. ${populationText}`;
|
||||||
|
tip("Drag to other node to add parent, click to edit");
|
||||||
}
|
}
|
||||||
|
|
||||||
const el = body.querySelector(`div[data-id='${religion}']`);
|
const el = body.querySelector(`div[data-id='${religionId}']`);
|
||||||
if (el) el.classList.add("active");
|
if (el) el.classList.add("active");
|
||||||
|
|
||||||
if (!layerIsOn("toggleReligions")) return;
|
if (!layerIsOn("toggleReligions")) return;
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
relig
|
relig
|
||||||
.select("#religion" + religion)
|
.select("#religion" + religionId)
|
||||||
.raise()
|
.raise()
|
||||||
.transition(animate)
|
.transition(animate)
|
||||||
.attr("stroke-width", 2.5)
|
.attr("stroke-width", 2.5)
|
||||||
.attr("stroke", "#c13119");
|
.attr("stroke", "#c13119");
|
||||||
debug
|
debug
|
||||||
.select("#religionsCenter" + religion)
|
.select("#religionsCenter" + religionId)
|
||||||
.raise()
|
.raise()
|
||||||
.transition(animate)
|
.transition(animate)
|
||||||
.attr("r", 8)
|
.attr("r", 8)
|
||||||
|
|
@ -211,26 +225,24 @@ function editReligions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function religionHighlightOff(event) {
|
function religionHighlightOff(event) {
|
||||||
const religion = +event.target.dataset.id;
|
const religionId = Number(event.id || event.target.dataset.id);
|
||||||
const info = document.getElementById("religionInfo");
|
const $info = byId("religionInfo");
|
||||||
if (info) {
|
if ($info) {
|
||||||
d3.select("#hierarchy")
|
d3.select("#hierarchy").select(`g[data-id='${religionId}']`).classed("selected", 0);
|
||||||
.select("g[data-id='" + religion + "'] > path")
|
$info.innerHTML = "‍";
|
||||||
.classed("selected", 0);
|
|
||||||
info.innerHTML = "‍";
|
|
||||||
tip("");
|
tip("");
|
||||||
}
|
}
|
||||||
|
|
||||||
const el = body.querySelector(`div[data-id='${religion}']`);
|
const el = body.querySelector(`div[data-id='${religionId}']`);
|
||||||
if (el) el.classList.remove("active");
|
if (el) el.classList.remove("active");
|
||||||
|
|
||||||
relig
|
relig
|
||||||
.select("#religion" + religion)
|
.select("#religion" + religionId)
|
||||||
.transition()
|
.transition()
|
||||||
.attr("stroke-width", null)
|
.attr("stroke-width", null)
|
||||||
.attr("stroke", null);
|
.attr("stroke", null);
|
||||||
debug
|
debug
|
||||||
.select("#religionsCenter" + religion)
|
.select("#religionsCenter" + religionId)
|
||||||
.transition()
|
.transition()
|
||||||
.attr("r", 4)
|
.attr("r", 4)
|
||||||
.attr("stroke-width", 1.2)
|
.attr("stroke-width", 1.2)
|
||||||
|
|
@ -309,8 +321,12 @@ function editReligions() {
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
||||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
|
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${
|
||||||
<p>Total believers: ${l(total)} ⇒ <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
burgs.length ? "" : "disabled"
|
||||||
|
} />
|
||||||
|
<p>Total believers: ${l(total)} ⇒ <span id="totalPop">${l(
|
||||||
|
total
|
||||||
|
)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||||
|
|
||||||
const update = function () {
|
const update = function () {
|
||||||
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
||||||
|
|
@ -367,7 +383,7 @@ function editReligions() {
|
||||||
|
|
||||||
function religionRemove() {
|
function religionRemove() {
|
||||||
if (customization) return;
|
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? <br>This action cannot be reverted";
|
alertMessage.innerHTML = "Are you sure you want to remove the religion? <br>This action cannot be reverted";
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
|
|
@ -375,18 +391,21 @@ function editReligions() {
|
||||||
title: "Remove religion",
|
title: "Remove religion",
|
||||||
buttons: {
|
buttons: {
|
||||||
Remove: function () {
|
Remove: function () {
|
||||||
relig.select("#religion" + religion).remove();
|
relig.select("#religion" + religionId).remove();
|
||||||
relig.select("#religion-gap" + religion).remove();
|
relig.select("#religion-gap" + religionId).remove();
|
||||||
debug.select("#religionsCenter" + religion).remove();
|
debug.select("#religionsCenter" + religionId).remove();
|
||||||
|
|
||||||
pack.cells.religion.forEach((r, i) => {
|
pack.cells.religion.forEach((r, i) => {
|
||||||
if (r === religion) pack.cells.religion[i] = 0;
|
if (r === religionId) pack.cells.religion[i] = 0;
|
||||||
});
|
|
||||||
pack.religions[religion].removed = true;
|
|
||||||
const origin = pack.religions[religion].origin;
|
|
||||||
pack.religions.forEach(r => {
|
|
||||||
if (r.origin === religion) r.origin = origin;
|
|
||||||
});
|
});
|
||||||
|
pack.religions[religionId].removed = true;
|
||||||
|
|
||||||
|
pack.religions
|
||||||
|
.filter(r => r.i && !r.removed)
|
||||||
|
.forEach(r => {
|
||||||
|
r.origins = r.origins.filter(origin => origin !== religionId);
|
||||||
|
if (!r.origins.length) r.origins = [0];
|
||||||
|
});
|
||||||
|
|
||||||
refreshReligionsEditor();
|
refreshReligionsEditor();
|
||||||
$(this).dialog("close");
|
$(this).dialog("close");
|
||||||
|
|
@ -400,7 +419,12 @@ function editReligions() {
|
||||||
|
|
||||||
function drawReligionCenters() {
|
function drawReligionCenters() {
|
||||||
debug.select("#religionCenters").remove();
|
debug.select("#religionCenters").remove();
|
||||||
const religionCenters = debug.append("g").attr("id", "religionCenters").attr("stroke-width", 1.2).attr("stroke", "#444444").style("cursor", "move");
|
const religionCenters = debug
|
||||||
|
.append("g")
|
||||||
|
.attr("id", "religionCenters")
|
||||||
|
.attr("stroke-width", 1.2)
|
||||||
|
.attr("stroke", "#444444")
|
||||||
|
.style("cursor", "move");
|
||||||
|
|
||||||
const data = pack.religions.filter(r => r.i && r.center && r.cells && !r.removed);
|
const data = pack.religions.filter(r => r.i && r.center && r.cells && !r.removed);
|
||||||
religionCenters
|
religionCenters
|
||||||
|
|
@ -466,67 +490,90 @@ function editReligions() {
|
||||||
|
|
||||||
function showHierarchy() {
|
function showHierarchy() {
|
||||||
// build hierarchy tree
|
// build hierarchy tree
|
||||||
pack.religions[0].origin = null;
|
pack.religions[0].origins = [null];
|
||||||
const religions = pack.religions.filter(r => !r.removed);
|
const validReligions = pack.religions.filter(r => !r.removed);
|
||||||
if (religions.length < 3) {
|
if (validReligions.length < 3) return tip("Not enough religions to show hierarchy", false, "error");
|
||||||
tip("Not enough religions to show hierarchy", false, "error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const root = d3
|
const root = d3
|
||||||
.stratify()
|
.stratify()
|
||||||
.id(d => d.i)
|
.id(d => d.i)
|
||||||
.parentId(d => d.origin)(religions);
|
.parentId(d => d.origins[0])(validReligions);
|
||||||
const treeWidth = root.leaves().length;
|
const treeWidth = root.leaves().length;
|
||||||
const treeHeight = root.height;
|
const treeHeight = root.height;
|
||||||
const width = treeWidth * 40,
|
const width = Math.max(treeWidth * 40, 300);
|
||||||
height = treeHeight * 60;
|
const height = treeHeight * 60;
|
||||||
|
|
||||||
const margin = {top: 10, right: 10, bottom: -5, left: 10};
|
const margin = {top: 10, right: 10, bottom: -5, left: 10};
|
||||||
const w = width - margin.left - margin.right;
|
const w = width - margin.left - margin.right;
|
||||||
const h = height + 30 - margin.top - margin.bottom;
|
const h = height + 30 - margin.top - margin.bottom;
|
||||||
const treeLayout = d3.tree().size([w, h]);
|
const treeLayout = d3.tree().size([w, h]);
|
||||||
|
|
||||||
|
alertMessage.innerHTML = /* html */ `<div id="religionChartDetails" class='chartInfo'>
|
||||||
|
<div id='religionInfo' style="display: block">‍</div>
|
||||||
|
<div id='religionSelected' style="display: none">
|
||||||
|
<span><span id='religionSelectedName'></span>. </span>
|
||||||
|
<span data-name="Type religion short name (abbreviation)">Abbreviation: <input id='religionSelectedCode' type='text' maxlength='3' size='3' /></span>
|
||||||
|
<button data-tip='Clear origin, religion will be linked to top level' id='religionSelectedClear'>Clear</button>
|
||||||
|
<button data-tip='Close edit mode' id='religionSelectedClose'>Close</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
// prepare svg
|
// prepare svg
|
||||||
alertMessage.innerHTML = "<div id='religionInfo' class='chartInfo'>‍</div>";
|
|
||||||
const svg = d3
|
const svg = d3
|
||||||
.select("#alertMessage")
|
.select("#alertMessage")
|
||||||
.insert("svg", "#religionInfo")
|
.insert("svg", "#religionChartDetails")
|
||||||
.attr("id", "hierarchy")
|
.attr("id", "hierarchy")
|
||||||
.attr("width", width)
|
.attr("width", width)
|
||||||
.attr("height", height)
|
.attr("height", height)
|
||||||
.style("text-anchor", "middle");
|
.style("text-anchor", "middle");
|
||||||
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
|
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
|
||||||
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
|
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
|
||||||
|
const primaryLinks = links.append("g");
|
||||||
|
const secondaryLinks = links.append("g").attr("stroke-dasharray", 1);
|
||||||
const nodes = graph.append("g");
|
const nodes = graph.append("g");
|
||||||
|
|
||||||
|
// render helper functions
|
||||||
|
const getLinkPath = d => {
|
||||||
|
const {
|
||||||
|
source: {x: sx, y: sy},
|
||||||
|
target: {x: tx, y: ty}
|
||||||
|
} = d;
|
||||||
|
return `M${sx},${sy} C${sx},${(sy * 3 + ty) / 4} ${tx},${(sy * 2 + ty) / 3} ${tx},${ty}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSecondaryLinks = root => {
|
||||||
|
const nodes = root.descendants();
|
||||||
|
const links = [];
|
||||||
|
|
||||||
|
for (const node of nodes) {
|
||||||
|
const origins = node.data.origins;
|
||||||
|
if (node.depth < 2) continue;
|
||||||
|
|
||||||
|
for (let i = 1; i < origins.length; i++) {
|
||||||
|
const source = nodes.find(n => n.data.i === origins[i]);
|
||||||
|
if (source) links.push({source, target: node});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodePathMap = {
|
||||||
|
undefined: "M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0", // small circle
|
||||||
|
Folk: "M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0", // circle
|
||||||
|
Organized: "M-11,-11h22v22h-22Z", // square
|
||||||
|
Cult: "M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z", // hexagon
|
||||||
|
Heresy: "M0,-14L14,0L0,14L-14,0Z" // diamond
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNodePath = d => nodePathMap[d.data.type];
|
||||||
|
|
||||||
renderTree();
|
renderTree();
|
||||||
function renderTree() {
|
function renderTree() {
|
||||||
treeLayout(root);
|
treeLayout(root);
|
||||||
links
|
|
||||||
.selectAll("path")
|
primaryLinks.selectAll("path").data(root.links()).enter().append("path").attr("d", getLinkPath);
|
||||||
.data(root.links())
|
secondaryLinks.selectAll("path").data(getSecondaryLinks(root)).enter().append("path").attr("d", getLinkPath);
|
||||||
.enter()
|
|
||||||
.append("path")
|
|
||||||
.attr("d", d => {
|
|
||||||
return (
|
|
||||||
"M" +
|
|
||||||
d.source.x +
|
|
||||||
"," +
|
|
||||||
d.source.y +
|
|
||||||
"C" +
|
|
||||||
d.source.x +
|
|
||||||
"," +
|
|
||||||
(d.source.y * 3 + d.target.y) / 4 +
|
|
||||||
" " +
|
|
||||||
d.target.x +
|
|
||||||
"," +
|
|
||||||
(d.source.y * 2 + d.target.y) / 3 +
|
|
||||||
" " +
|
|
||||||
d.target.x +
|
|
||||||
"," +
|
|
||||||
d.target.y
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const node = nodes
|
const node = nodes
|
||||||
.selectAll("g")
|
.selectAll("g")
|
||||||
|
|
@ -536,30 +583,21 @@ function editReligions() {
|
||||||
.attr("data-id", d => d.data.i)
|
.attr("data-id", d => d.data.i)
|
||||||
.attr("stroke", "#333333")
|
.attr("stroke", "#333333")
|
||||||
.attr("transform", d => `translate(${d.x}, ${d.y})`)
|
.attr("transform", d => `translate(${d.x}, ${d.y})`)
|
||||||
.on("mouseenter", () => religionHighlightOn(event))
|
.on("mouseenter", religionHighlightOn)
|
||||||
.on("mouseleave", () => religionHighlightOff(event))
|
.on("mouseleave", religionHighlightOff)
|
||||||
.call(d3.drag().on("start", d => dragToReorigin(d)));
|
.on("click", religionSelect)
|
||||||
|
.call(d3.drag().on("start", dragToReorigin));
|
||||||
|
|
||||||
node
|
node
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("d", d => {
|
.attr("d", getNodePath)
|
||||||
if (d.data.type === "Folk") return "M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0";
|
.attr("fill", d => d.data.color || "#ffffff")
|
||||||
// circle
|
|
||||||
else if (d.data.type === "Heresy") return "M0,-14L14,0L0,14L-14,0Z";
|
|
||||||
// diamond
|
|
||||||
else if (d.data.type === "Cult") return "M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z";
|
|
||||||
// hex
|
|
||||||
else if (!d.data.i) return "M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0";
|
|
||||||
// small circle
|
|
||||||
else return "M-11,-11h22v22h-22Z"; // square
|
|
||||||
})
|
|
||||||
.attr("fill", d => (d.data.i ? d.data.color : "#ffffff"))
|
|
||||||
.attr("stroke-dasharray", d => (d.data.cells ? "null" : "1"));
|
.attr("stroke-dasharray", d => (d.data.cells ? "null" : "1"));
|
||||||
|
|
||||||
node
|
node
|
||||||
.append("text")
|
.append("text")
|
||||||
.attr("dy", ".35em")
|
.attr("dy", ".35em")
|
||||||
.text(d => (d.data.i ? d.data.code : ""));
|
.text(d => d.data.code || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
|
|
@ -573,12 +611,38 @@ function editReligions() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function dragToReorigin(d) {
|
function religionSelect(d) {
|
||||||
if (isCtrlClick(d3.event.sourceEvent)) {
|
d3.event.stopPropagation();
|
||||||
changeCode(d);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
nodes.selectAll("g").style("outline", "none");
|
||||||
|
this.style.outline = "1px solid #c13119";
|
||||||
|
byId("religionSelected").style.display = "block";
|
||||||
|
byId("religionInfo").style.display = "none";
|
||||||
|
|
||||||
|
const religion = d.data;
|
||||||
|
byId("religionSelectedName").innerText = religion.name;
|
||||||
|
byId("religionSelectedCode").value = religion.code;
|
||||||
|
|
||||||
|
byId("religionSelectedCode").onchange = function () {
|
||||||
|
if (this.value.length > 3) return tip("Abbreviation must be 3 characters or less", false, "error", 3000);
|
||||||
|
if (!this.value.length) return tip("Abbreviation cannot be empty", false, "error", 3000);
|
||||||
|
nodes.select(`g[data-id="${d.id}"] > text`).text(this.value);
|
||||||
|
religion.code = this.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
byId("religionSelectedClear").onclick = () => {
|
||||||
|
religion.origins = [0];
|
||||||
|
showHierarchy();
|
||||||
|
};
|
||||||
|
|
||||||
|
byId("religionSelectedClose").onclick = () => {
|
||||||
|
this.style.outline = "none";
|
||||||
|
byId("religionSelected").style.display = "none";
|
||||||
|
byId("religionInfo").style.display = "block";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragToReorigin(d) {
|
||||||
const originLine = graph.append("path").attr("class", "dragLine").attr("d", `M${d.x},${d.y}L${d.x},${d.y}`);
|
const originLine = graph.append("path").attr("class", "dragLine").attr("d", `M${d.x},${d.y}L${d.x},${d.y}`);
|
||||||
|
|
||||||
d3.event.on("drag", () => {
|
d3.event.on("drag", () => {
|
||||||
|
|
@ -587,26 +651,20 @@ function editReligions() {
|
||||||
|
|
||||||
d3.event.on("end", () => {
|
d3.event.on("end", () => {
|
||||||
originLine.remove();
|
originLine.remove();
|
||||||
const selected = graph.select("path.selected");
|
const selected = graph.select("g.selected");
|
||||||
if (!selected.size()) return;
|
if (!selected.size()) return;
|
||||||
const religion = d.data.i;
|
|
||||||
const oldOrigin = d.data.origin;
|
|
||||||
let newOrigin = selected.datum().data.i;
|
|
||||||
if (newOrigin == oldOrigin) return; // already a child of the selected node
|
|
||||||
if (newOrigin == religion) newOrigin = 0; // move to top
|
|
||||||
if (newOrigin && d.descendants().some(node => node.id == newOrigin)) return; // cannot be a child of its own child
|
|
||||||
pack.religions[religion].origin = d.data.origin = newOrigin; // change data
|
|
||||||
showHierarchy(); // update hierarchy
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeCode(d) {
|
const religionId = d.data.i;
|
||||||
prompt(`Please provide an abbreviation for ${d.data.name}`, {default: d.data.code}, v => {
|
const newOrigin = selected.datum().data.i;
|
||||||
pack.religions[d.data.i].code = v;
|
if (religionId === newOrigin) return; // dragged to itself
|
||||||
nodes
|
if (d.data.origins.includes(newOrigin)) return; // already a child of the selected node
|
||||||
.select("g[data-id='" + d.data.i + "']")
|
if (d.descendants().some(node => node.data.i === newOrigin)) return; // cannot be a child of its own child
|
||||||
.select("text")
|
|
||||||
.text(v);
|
const religion = pack.religions[religionId];
|
||||||
|
if (religion.origins[0] === 0) religion.origins = [];
|
||||||
|
religion.origins.push(newOrigin);
|
||||||
|
|
||||||
|
showHierarchy();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -621,7 +679,7 @@ function editReligions() {
|
||||||
customization = 7;
|
customization = 7;
|
||||||
relig.append("g").attr("id", "temp");
|
relig.append("g").attr("id", "temp");
|
||||||
document.querySelectorAll("#religionsBottom > button").forEach(el => (el.style.display = "none"));
|
document.querySelectorAll("#religionsBottom > button").forEach(el => (el.style.display = "none"));
|
||||||
document.getElementById("religionsManuallyButtons").style.display = "inline-block";
|
byId("religionsManuallyButtons").style.display = "inline-block";
|
||||||
debug.select("#religionCenters").style("display", "none");
|
debug.select("#religionCenters").style("display", "none");
|
||||||
|
|
||||||
religionsEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
|
religionsEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
|
||||||
|
|
@ -685,7 +743,13 @@ function editReligions() {
|
||||||
|
|
||||||
// change of append new element
|
// change of append new element
|
||||||
if (exists.size()) exists.attr("data-religion", r).attr("fill", color);
|
if (exists.size()) exists.attr("data-religion", r).attr("fill", color);
|
||||||
else temp.append("polygon").attr("data-cell", i).attr("data-religion", r).attr("points", getPackPolygon(i)).attr("fill", color);
|
else
|
||||||
|
temp
|
||||||
|
.append("polygon")
|
||||||
|
.attr("data-cell", i)
|
||||||
|
.attr("data-religion", r)
|
||||||
|
.attr("points", getPackPolygon(i))
|
||||||
|
.attr("fill", color);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -717,7 +781,7 @@ function editReligions() {
|
||||||
relig.select("#temp").remove();
|
relig.select("#temp").remove();
|
||||||
removeCircle();
|
removeCircle();
|
||||||
document.querySelectorAll("#religionsBottom > button").forEach(el => (el.style.display = "inline-block"));
|
document.querySelectorAll("#religionsBottom > button").forEach(el => (el.style.display = "inline-block"));
|
||||||
document.getElementById("religionsManuallyButtons").style.display = "none";
|
byId("religionsManuallyButtons").style.display = "none";
|
||||||
|
|
||||||
religionsEditor.querySelectorAll(".hide").forEach(el => el.classList.remove("hidden"));
|
religionsEditor.querySelectorAll(".hide").forEach(el => el.classList.remove("hidden"));
|
||||||
religionsFooter.style.display = "block";
|
religionsFooter.style.display = "block";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue