mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 12:01:23 +01:00
Got the religion generator working, but not the tree. There are cycles being generated
This commit is contained in:
parent
520551b14c
commit
3d2b32535d
2 changed files with 120 additions and 11 deletions
|
|
@ -212,7 +212,10 @@ function religionsEditorAddLines() {
|
||||||
<div data-tip="Religion area" class="religionArea hide" style="width: 5em">${si(area) + unit}</div>
|
<div data-tip="Religion area" class="religionArea hide" style="width: 5em">${si(area) + unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="religionPopulation hide pointer">${si(population)}</div>
|
<div data-tip="${populationTip}" class="religionPopulation hide pointer">${si(population)}</div>
|
||||||
<span data-tip="Lock religion" class="icon-lock${r.lock ? '' : '-open'} hide"></span>
|
<span
|
||||||
|
data-tip="Lock religion, will regenerate the origin folk and organized religion if they are not also locked"
|
||||||
|
class="icon-lock${r.lock ? '' : '-open'} hide"
|
||||||
|
></span>
|
||||||
<span data-tip="Remove religion" class="icon-trash-empty hide"></span>
|
<span data-tip="Remove religion" class="icon-trash-empty hide"></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -347,11 +347,51 @@ window.Religions = (function () {
|
||||||
const cells = pack.cells,
|
const cells = pack.cells,
|
||||||
states = pack.states,
|
states = pack.states,
|
||||||
cultures = pack.cultures;
|
cultures = pack.cultures;
|
||||||
const religions = (pack.religions = []);
|
|
||||||
|
// Keep a map of the previous religions per cell for referencing when we restore locked religions
|
||||||
|
const prevReligions = {};
|
||||||
|
if (cells.religion) {
|
||||||
|
cells.religion.forEach((rId, index) => {
|
||||||
|
prevReligions[index] = rId;
|
||||||
|
})
|
||||||
|
}
|
||||||
cells.religion = new Uint16Array(cells.culture); // cell religion; initially based on culture
|
cells.religion = new Uint16Array(cells.culture); // cell religion; initially based on culture
|
||||||
|
|
||||||
|
const religionsToRestore = [];
|
||||||
|
const restoredCells = [];
|
||||||
|
const restoredHeresyCells = [];
|
||||||
|
// Restore locked religions to their existing cells
|
||||||
|
if (pack.religions) {
|
||||||
|
pack.religions.forEach( (religion) => {
|
||||||
|
// Keep any locked religions, we will reassign it to a folk or organized religion later
|
||||||
|
if (!religion.lock) return;
|
||||||
|
|
||||||
|
// Add all religions we restore after the base cults to keep the index correct
|
||||||
|
const id = pack.cultures.length + religionsToRestore.length;
|
||||||
|
religionsToRestore.push(religion);
|
||||||
|
|
||||||
|
Object.entries(prevReligions).forEach(([index, rId]) => {
|
||||||
|
if (rId !== religion.i) return;
|
||||||
|
|
||||||
|
if (religion.type === "Heresy") {
|
||||||
|
restoredHeresyCells.push(parseInt(index));
|
||||||
|
} else {
|
||||||
|
restoredCells.push(parseInt(index));
|
||||||
|
cells.religion[index] = id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
religion.i = id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const religions = (pack.religions = []);
|
||||||
|
|
||||||
// add folk religions
|
// add folk religions
|
||||||
pack.cultures.forEach(c => {
|
pack.cultures.forEach(c => {
|
||||||
|
// If a restored religion exists for this culture, ignore
|
||||||
|
if (religionsToRestore.find(r => r.type === "Folk" && r.culture === c.i) !== undefined) return;
|
||||||
|
|
||||||
|
// We already preadded the "no religion" religion
|
||||||
if (!c.i) return religions.push({i: 0, name: "No religion"});
|
if (!c.i) return religions.push({i: 0, name: "No religion"});
|
||||||
|
|
||||||
if (c.removed) {
|
if (c.removed) {
|
||||||
|
|
@ -370,6 +410,7 @@ window.Religions = (function () {
|
||||||
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, origins: [0]});
|
religions.push({i: c.i, name, color, culture: c.i, type: "Folk", form, deity, center: c.center, origins: [0]});
|
||||||
});
|
});
|
||||||
|
religions.push(...religionsToRestore);
|
||||||
|
|
||||||
if (religionsInput.value == 0 || pack.cultures.length < 2)
|
if (religionsInput.value == 0 || pack.cultures.length < 2)
|
||||||
return 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)));
|
||||||
|
|
@ -379,6 +420,7 @@ window.Religions = (function () {
|
||||||
burgs.length > +religionsInput.value
|
burgs.length > +religionsInput.value
|
||||||
? burgs.sort((a, b) => b.population - a.population).map(b => b.cell)
|
? burgs.sort((a, b) => b.population - a.population).map(b => b.cell)
|
||||||
: cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
|
: cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
|
||||||
|
const available = [...sorted].filter(cellI => !restoredCells.includes(cellI));
|
||||||
const religionsTree = d3.quadtree();
|
const religionsTree = d3.quadtree();
|
||||||
const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns
|
const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns
|
||||||
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
||||||
|
|
@ -391,9 +433,27 @@ window.Religions = (function () {
|
||||||
return religions.length ? religions.slice(0, max) : [0];
|
return religions.length ? religions.slice(0, max) : [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the origins of any organized religion that was locked
|
||||||
|
pack.religions.forEach(religion => {
|
||||||
|
// Ignore if the religion is not locked or not organized
|
||||||
|
if (religion.type !== "Organized" || !religion.lock) return;
|
||||||
|
// Ignore if the religion already has a valid origin
|
||||||
|
if (pack.religions.find(r => religion.origins.includes(r.i) && r.lock) !== undefined) return;
|
||||||
|
|
||||||
|
// Select a random folk religion for the religion
|
||||||
|
const culture = cells.culture[religion.center];
|
||||||
|
const [x, y] = cells.p[religion.center];
|
||||||
|
const isFolkBased = religion.expansion === "culture" || P(0.5);
|
||||||
|
const folk = isFolkBased && religions.find(r => r.culture === culture && r.type === "Folk");
|
||||||
|
if (folk && religion.expansion === "culture" && folk.name.slice(0, 3) !== "Old") folk.name = "Old " + folk.name;
|
||||||
|
|
||||||
|
religion.origins = folk ? [folk.i] : getReligionsInRadius({x, y, r: 150 / count, max: 2});
|
||||||
|
religionsTree.add([x, y]);
|
||||||
|
});
|
||||||
|
|
||||||
// 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 = available[biased(0, available.length - 1, 5)]; // religion center
|
||||||
const form = rw(forms.Organized);
|
const form = rw(forms.Organized);
|
||||||
const state = cells.state[center];
|
const state = cells.state[center];
|
||||||
const culture = cells.culture[center];
|
const culture = cells.culture[center];
|
||||||
|
|
@ -439,10 +499,22 @@ window.Religions = (function () {
|
||||||
religionsTree.add([x, y]);
|
religionsTree.add([x, y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the origins of any cult that was locked
|
||||||
|
pack.religions.forEach(religion => {
|
||||||
|
// Ignore if the religion is not locked or not organized
|
||||||
|
if (religion.type !== "Cult" || !religion.lock) return;
|
||||||
|
// Ignore if the religion already has a valid origin
|
||||||
|
if (pack.religions.find(r => religion.origins.includes(r.i) && r.lock) !== undefined) return;
|
||||||
|
|
||||||
|
const [x, y] = cells.p[religion.center];
|
||||||
|
religion.origins = getReligionsInRadius({x, y, r: 300 / count, max: rand(0, 4)});
|
||||||
|
religionsTree.add([x, y]);
|
||||||
|
});
|
||||||
|
|
||||||
// generate cults
|
// generate cults
|
||||||
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 = available[biased(0, available.length - 1, 1)]; // religion center
|
||||||
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
||||||
center = cells.c[center].find(c => cells.burg[c]);
|
center = cells.c[center].find(c => cells.burg[c]);
|
||||||
const [x, y] = cells.p[center];
|
const [x, y] = cells.p[center];
|
||||||
|
|
@ -473,7 +545,29 @@ window.Religions = (function () {
|
||||||
religionsTree.add([x, y]);
|
religionsTree.add([x, y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
expandReligions();
|
expandReligions(restoredCells);
|
||||||
|
|
||||||
|
// Restore the origins of any heresy that was locked
|
||||||
|
pack.religions.forEach(religion => {
|
||||||
|
// Ignore if the religion is not locked or not organized
|
||||||
|
if (religion.type !== "Heresy" || !religion.lock) return;
|
||||||
|
|
||||||
|
const originReligion = [cells.religion[religion.center]];
|
||||||
|
// Restore the cells now that all other religions have been processed
|
||||||
|
Object.entries(prevReligions).forEach(([index, rId]) => {
|
||||||
|
if (rId !== religion.i) return;
|
||||||
|
|
||||||
|
cells.religion[index] = religion.i;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ignore if the religion already has a valid origin
|
||||||
|
if (pack.religions.find(r => religion.origins.includes(r.i) && r.lock) !== undefined) return;
|
||||||
|
|
||||||
|
const [x, y] = cells.p[religion.center];
|
||||||
|
// Use the religion from the expanded cells, we'll restore the heresies later
|
||||||
|
religion.origins = [originReligion];
|
||||||
|
religionsTree.add([x, y]);
|
||||||
|
});
|
||||||
|
|
||||||
// generate heresies
|
// generate heresies
|
||||||
religions
|
religions
|
||||||
|
|
@ -512,7 +606,7 @@ window.Religions = (function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
expandHeresies();
|
expandHeresies(restoredHeresyCells);
|
||||||
checkCenters();
|
checkCenters();
|
||||||
|
|
||||||
TIME && console.timeEnd("generateReligions");
|
TIME && console.timeEnd("generateReligions");
|
||||||
|
|
@ -567,14 +661,14 @@ window.Religions = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
// growth algorithm to assign cells to religions
|
// growth algorithm to assign cells to religions
|
||||||
const expandReligions = function () {
|
const expandReligions = function (restoredCells) {
|
||||||
const cells = pack.cells,
|
const cells = pack.cells,
|
||||||
religions = pack.religions;
|
religions = pack.religions;
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
||||||
const cost = [];
|
const cost = [];
|
||||||
|
|
||||||
religions
|
religions
|
||||||
.filter(r => r.type === "Organized" || r.type === "Cult")
|
.filter(r => !r.lock && (r.type === "Organized" || r.type === "Cult"))
|
||||||
.forEach(r => {
|
.forEach(r => {
|
||||||
cells.religion[r.center] = r.i;
|
cells.religion[r.center] = r.i;
|
||||||
queue.queue({e: r.center, p: 0, r: r.i, s: cells.state[r.center], c: r.culture});
|
queue.queue({e: r.center, p: 0, r: r.i, s: cells.state[r.center], c: r.culture});
|
||||||
|
|
@ -583,6 +677,11 @@ window.Religions = (function () {
|
||||||
|
|
||||||
const neutral = (cells.i.length / 5000) * 200 * gauss(1, 0.3, 0.2, 2, 2) * neutralInput.value; // limit cost for organized religions growth
|
const neutral = (cells.i.length / 5000) * 200 * gauss(1, 0.3, 0.2, 2, 2) * neutralInput.value; // limit cost for organized religions growth
|
||||||
const popCost = d3.max(cells.pop) / 3; // enougth population to spered religion without penalty
|
const popCost = d3.max(cells.pop) / 3; // enougth population to spered religion without penalty
|
||||||
|
const cellsCost = {};
|
||||||
|
restoredCells.forEach(index => {
|
||||||
|
// Make the cost of cells with an existing culture so high, they won't get picked
|
||||||
|
cellsCost[index] = neutral;
|
||||||
|
});
|
||||||
|
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const next = queue.dequeue(),
|
const next = queue.dequeue(),
|
||||||
|
|
@ -605,6 +704,7 @@ window.Religions = (function () {
|
||||||
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 =
|
const totalCost =
|
||||||
p +
|
p +
|
||||||
|
(cellsCost[e] || 0) +
|
||||||
(cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
|
(cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
|
||||||
if (totalCost > neutral) return;
|
if (totalCost > neutral) return;
|
||||||
|
|
||||||
|
|
@ -618,14 +718,14 @@ window.Religions = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
// growth algorithm to assign cells to heresies
|
// growth algorithm to assign cells to heresies
|
||||||
const expandHeresies = function () {
|
const expandHeresies = function (restoredCells) {
|
||||||
const cells = pack.cells,
|
const cells = pack.cells,
|
||||||
religions = pack.religions;
|
religions = pack.religions;
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
||||||
const cost = [];
|
const cost = [];
|
||||||
|
|
||||||
religions
|
religions
|
||||||
.filter(r => r.type === "Heresy")
|
.filter(r => !r.lock && r.type === "Heresy")
|
||||||
.forEach(r => {
|
.forEach(r => {
|
||||||
const b = cells.religion[r.center]; // "base" religion id
|
const b = cells.religion[r.center]; // "base" religion id
|
||||||
cells.religion[r.center] = r.i; // heresy id
|
cells.religion[r.center] = r.i; // heresy id
|
||||||
|
|
@ -634,6 +734,11 @@ window.Religions = (function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
const neutral = (cells.i.length / 5000) * 500 * neutralInput.value; // limit cost for heresies growth
|
const neutral = (cells.i.length / 5000) * 500 * neutralInput.value; // limit cost for heresies growth
|
||||||
|
const cellsCost = {};
|
||||||
|
restoredCells.forEach(index => {
|
||||||
|
// Make the cost of cells with an existing culture so high, they won't get picked
|
||||||
|
cellsCost[index] = neutral;
|
||||||
|
});
|
||||||
|
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const next = queue.dequeue(),
|
const next = queue.dequeue(),
|
||||||
|
|
@ -648,7 +753,8 @@ window.Religions = (function () {
|
||||||
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 =
|
const totalCost =
|
||||||
p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
|
p + (cellsCost[e] || 0) +
|
||||||
|
(religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
|
||||||
|
|
||||||
if (totalCost > neutral) return;
|
if (totalCost > neutral) return;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue