feat: zones - generation fixes

This commit is contained in:
Azgaar 2024-08-31 19:56:17 +02:00
parent fdf0d163a8
commit c17be58efe
4 changed files with 116 additions and 105 deletions

View file

@ -611,8 +611,7 @@ window.BurgsAndStates = (() => {
// generate Diplomatic Relationships // generate Diplomatic Relationships
const generateDiplomacy = () => { const generateDiplomacy = () => {
TIME && console.time("generateDiplomacy"); TIME && console.time("generateDiplomacy");
const cells = pack.cells, const {cells, states} = pack;
states = pack.states;
const chronicle = (states[0].diplomacy = []); const chronicle = (states[0].diplomacy = []);
const valid = states.filter(s => s.i && !states.removed); const valid = states.filter(s => s.i && !states.removed);
@ -696,21 +695,23 @@ window.BurgsAndStates = (() => {
const defender = ra( const defender = ra(
ad.map((r, d) => (r === "Rival" && !states[d].diplomacy.includes("Vassal") ? d : 0)).filter(d => d) ad.map((r, d) => (r === "Rival" && !states[d].diplomacy.includes("Vassal") ? d : 0)).filter(d => d)
); );
let ap = states[attacker].area * states[attacker].expansionism, let ap = states[attacker].area * states[attacker].expansionism;
dp = states[defender].area * states[defender].expansionism; let dp = states[defender].area * states[defender].expansionism;
if (ap < dp * gauss(1.6, 0.8, 0, 10, 2)) continue; // defender is too strong if (ap < dp * gauss(1.6, 0.8, 0, 10, 2)) continue; // defender is too strong
const an = states[attacker].name,
dn = states[defender].name; // names const an = states[attacker].name;
const attackers = [attacker], const dn = states[defender].name; // names
defenders = [defender]; // attackers and defenders array const attackers = [attacker];
const defenders = [defender]; // attackers and defenders array
const dd = states[defender].diplomacy; // defender relations; const dd = states[defender].diplomacy; // defender relations;
// start a war // start an ongoing war
const war = [`${an}-${trimVowels(dn)}ian War`, `${an} declared a war on its rival ${dn}`]; const name = `${an}-${trimVowels(dn)}ian War`;
const end = options.year; const start = options.year - gauss(2, 3, 0, 10);
const start = end - gauss(2, 2, 0, 5); const war = [name, `${an} declared a war on its rival ${dn}`];
states[attacker].campaigns.push({name: `${trimVowels(dn)}ian War`, start, end}); const campaign = {name, start, attacker, defender};
states[defender].campaigns.push({name: `${trimVowels(an)}ian War`, start, end}); states[attacker].campaigns.push(campaign);
states[defender].campaigns.push(campaign);
// attacker vassals join the war // attacker vassals join the war
ad.forEach((r, d) => { ad.forEach((r, d) => {
@ -790,7 +791,6 @@ window.BurgsAndStates = (() => {
} }
TIME && console.timeEnd("generateDiplomacy"); TIME && console.timeEnd("generateDiplomacy");
//console.table(states.map(s => s.diplomacy));
}; };
// select a forms for listed or all valid states // select a forms for listed or all valid states

View file

@ -503,7 +503,9 @@ window.Military = (function () {
: ""; : "";
const campaign = s.campaigns ? ra(s.campaigns) : null; const campaign = s.campaigns ? ra(s.campaigns) : null;
const year = campaign ? rand(campaign.start, campaign.end) : gauss(options.year - 100, 150, 1, options.year - 6); const year = campaign
? rand(campaign.start, campaign.end || options.year)
: gauss(options.year - 100, 150, 1, options.year - 6);
const conflict = campaign ? ` during the ${campaign.name}` : ""; const conflict = campaign ? ` during the ${campaign.name}` : "";
const legend = `Regiment was formed in ${year} ${options.era}${conflict}. ${station}${troops}`; const legend = `Regiment was formed in ${year} ${options.era}${conflict}. ${station}${troops}`;
notes.push({id: `regiment${s.i}-${r.i}`, name: `${r.icon} ${r.name}`, legend}); notes.push({id: `regiment${s.i}-${r.i}`, name: `${r.icon} ${r.name}`, legend});

View file

@ -233,6 +233,7 @@ function editZones() {
const zoneId = +body.querySelector("div.selected")?.dataset.id; const zoneId = +body.querySelector("div.selected")?.dataset.id;
const zone = pack.zones.find(z => z.i === zoneId); const zone = pack.zones.find(z => z.i === zoneId);
if (!zone) return;
if (eraseMode) { if (eraseMode) {
const data = zones const data = zones

View file

@ -2,14 +2,14 @@
window.Zones = (function () { window.Zones = (function () {
const config = { const config = {
invasion: {quantity: 1.8, generate: addInvasion}, // invasion of enemy lands invasion: {quantity: 2, generate: addInvasion}, // invasion of enemy lands
rebels: {quantity: 1.6, generate: addRebels}, // rebels along a state border rebels: {quantity: 1.5, generate: addRebels}, // rebels along a state border
proselytism: {quantity: 1.6, generate: addProselytism}, // proselitism of organized religion proselytism: {quantity: 1.6, generate: addProselytism}, // proselitism of organized religion
crusade: {quantity: 1.6, generate: addCrusade}, // crusade on heresy lands crusade: {quantity: 1.6, generate: addCrusade}, // crusade on heresy lands
disease: {quantity: 1.8, generate: addDisease}, // disease starting in a random city disease: {quantity: 1.8, generate: addDisease}, // disease starting in a random city
disaster: {quantity: 1.2, generate: addDisaster}, // disaster starting in a random city disaster: {quantity: 1.2, generate: addDisaster}, // disaster starting in a random city
eruption: {quantity: 1.4, generate: addEruption}, // volcanic eruption aroung volcano eruption: {quantity: 1.2, generate: addEruption}, // volcanic eruption aroung volcano
avalanche: {quantity: 1.0, generate: addAvalanche}, // avalanche impacting highland road avalanche: {quantity: 0.8, generate: addAvalanche}, // avalanche impacting highland road
fault: {quantity: 1.4, generate: addFault}, // fault line in elevated areas fault: {quantity: 1.4, generate: addFault}, // fault line in elevated areas
flood: {quantity: 1.4, generate: addFlood}, // flood on river banks flood: {quantity: 1.4, generate: addFlood}, // flood on river banks
tsunami: {quantity: 1.2, generate: addTsunami} // tsunami starting near coast tsunami: {quantity: 1.2, generate: addTsunami} // tsunami starting near coast
@ -21,152 +21,160 @@ window.Zones = (function () {
const usedCells = new Uint8Array(pack.cells.i.length); const usedCells = new Uint8Array(pack.cells.i.length);
pack.zones = []; pack.zones = [];
Object.values(config).forEach(type => { Object.entries(config).forEach(([name, type]) => {
const count = rn(Math.random() * type.quantity * globalModifier); const expectedNumber = type.quantity * globalModifier;
for (let i = 0; i < count; i++) { let number = gauss(expectedNumber, expectedNumber / 2, 0, 100);
type.generate(usedCells); console.log(name, number);
} while (number--) type.generate(usedCells);
}); });
TIME && console.timeEnd("generateZones"); TIME && console.timeEnd("generateZones");
}; };
function addInvasion(usedCells) { function addInvasion(usedCells) {
const atWar = pack.states.filter(s => s.diplomacy && s.diplomacy.some(d => d === "Enemy")); const {cells, states} = pack;
if (!atWar.length) return;
const invader = ra(atWar); const ongoingConflicts = states
const target = invader.diplomacy.find(d => d === "Enemy"); .filter(s => s.i && !s.removed && s.campaigns)
.map(s => s.campaigns)
.flat()
.filter(c => !c.end);
if (!ongoingConflicts.length) return;
const {defender, attacker} = ra(ongoingConflicts);
const cells = pack.cells; const borderCells = cells.i.filter(cellId => {
const cell = ra( if (usedCells[cellId]) return false;
cells.i.filter(i => cells.state[i] === target.i && cells.c[i].some(c => cells.state[c] === invader.i)) if (cells.state[cellId] !== defender) return false;
); return cells.c[cellId].some(c => cells.state[c] === attacker);
if (!cell) return; });
const cellsArray = [], const startCell = ra(borderCells);
queue = [cell], if (startCell === undefined) return;
power = rand(5, 30);
const invationCells = [];
const queue = [startCell];
const maxCells = rand(5, 30);
while (queue.length) { while (queue.length) {
const q = P(0.4) ? queue.shift() : queue.pop(); const cellId = P(0.4) ? queue.shift() : queue.pop();
cellsArray.push(q); invationCells.push(cellId);
if (cellsArray.length > power) break; if (invationCells.length >= maxCells) break;
cells.c[q].forEach(e => { cells.c[cellId].forEach(neibCellId => {
if (usedCells[e]) return; if (usedCells[neibCellId]) return;
if (cells.state[e] !== target.i) return; if (cells.state[neibCellId] !== defender) return;
usedCells[e] = 1; usedCells[neibCellId] = 1;
queue.push(e); queue.push(neibCellId);
}); });
} }
const invasion = rw({ const subtype = rw({
Invasion: 4, Invasion: 5,
Occupation: 3, Occupation: 4,
Raid: 2, Conquest: 3,
Conquest: 2, Incursion: 2,
Intervention: 2,
Subjugation: 1, Subjugation: 1,
Foray: 1, Foray: 1,
Skirmishes: 1, Skirmishes: 1,
Incursion: 2,
Pillaging: 1, Pillaging: 1,
Intervention: 1 Raid: 1
}); });
const name = getAdjective(invader.name) + " " + invasion; const name = getAdjective(states[attacker].name) + " " + subtype;
pack.zones.push({i: pack.zones.length, name, type: "Invasion", cells: cellsArray, color: "url(#hatch1)"});
pack.zones.push({i: pack.zones.length, name, type: "Invasion", cells: invationCells, color: "url(#hatch1)"});
} }
function addRebels(usedCells) { function addRebels(usedCells) {
const {cells, states} = pack; const {cells, states} = pack;
const state = ra(states.filter(s => s.i && !s.removed && s.neighbors.some(Boolean))); const state = ra(states.filter(s => s.i && !s.removed && s.neighbors.some(Boolean)));
if (!state) return; if (!state) return;
const neib = ra(state.neighbors.filter(n => n && !states[n].removed)); const neibStateId = ra(state.neighbors.filter(n => n && !states[n].removed));
if (!neib) return; if (!neibStateId) return;
const cell = cells.i.find(
i => cells.state[i] === state.i && !state.removed && cells.c[i].some(c => cells.state[c] === neib)
);
const cellsArray = []; const cellsArray = [];
const queue = []; const queue = [];
if (cell) queue.push(cell); const borderCellId = cells.i.find(
i => cells.state[i] === state.i && cells.c[i].some(c => cells.state[c] === neibStateId)
const power = rand(10, 30); );
if (borderCellId) queue.push(borderCellId);
const maxCells = rand(10, 30);
while (queue.length) { while (queue.length) {
const q = queue.shift(); const cellId = queue.shift();
cellsArray.push(q); cellsArray.push(cellId);
if (cellsArray.length > power) break; if (cellsArray.length >= maxCells) break;
cells.c[q].forEach(e => { cells.c[cellId].forEach(neibCellId => {
if (usedCells[e]) return; if (usedCells[neibCellId]) return;
if (cells.state[e] !== state.i) return; if (cells.state[neibCellId] !== state.i) return;
usedCells[e] = 1; usedCells[neibCellId] = 1;
if (e % 4 !== 0 && !cells.c[e].some(c => cells.state[c] === neib)) return; if (neibCellId % 4 !== 0 && !cells.c[neibCellId].some(c => cells.state[c] === neibStateId)) return;
queue.push(e); queue.push(neibCellId);
}); });
} }
const rebels = rw({ const rebels = rw({
Rebels: 5, Rebels: 5,
Insurgents: 2, Insurrection: 2,
Mutineers: 1, Mutineers: 1,
Insurgents: 1,
Rioters: 1, Rioters: 1,
Separatists: 1, Separatists: 1,
Secessionists: 1, Secessionists: 1,
Insurrection: 2,
Rebellion: 1, Rebellion: 1,
Conspiracy: 2 Conspiracy: 1
}); });
const name = getAdjective(states[neib].name) + " " + rebels; const name = getAdjective(states[neibStateId].name) + " " + rebels;
pack.zones.push({i: pack.zones.length, name, type: "Rebels", cells: cellsArray, color: "url(#hatch3)"}); pack.zones.push({i: pack.zones.length, name, type: "Rebels", cells: cellsArray, color: "url(#hatch3)"});
} }
function addProselytism(usedCells) { function addProselytism(usedCells) {
const organized = ra(pack.religions.filter(r => r.type === "Organized")); const {cells, religions} = pack;
if (!organized) return;
const cells = pack.cells; const organizedReligions = religions.filter(r => r.i && !r.removed && r.type === "Organized");
const cell = ra( const religion = ra(organizedReligions);
cells.i.filter( if (!religion) return;
i =>
cells.religion[i] && const targetBorderCells = cells.i.filter(
cells.religion[i] !== organized.i && i => cells.religion[i] !== religion.i && cells.c[i].some(c => cells.religion[c] === religion.i)
cells.c[i].some(c => cells.religion[c] === organized.i)
)
); );
if (!cell) return; const startCell = ra(targetBorderCells);
const target = cells.religion[cell]; if (!startCell) return;
const cellsArray = [],
queue = [cell], const targetReligionId = cells.religion[startCell];
power = rand(10, 30); const proselytismCells = [];
const queue = [startCell];
const maxCells = rand(10, 30);
while (queue.length) { while (queue.length) {
const q = queue.shift(); const cellId = queue.shift();
cellsArray.push(q); proselytismCells.push(cellId);
if (cellsArray.length > power) break; if (proselytismCells.length >= maxCells) break;
cells.c[q].forEach(e => { cells.c[cellId].forEach(neibCellId => {
if (usedCells[e]) return; if (usedCells[neibCellId]) return;
if (cells.religion[e] !== target) return; if (cells.religion[neibCellId] !== targetReligionId) return;
if (cells.h[e] < 20) return; if (cells.h[neibCellId] < 20) return;
usedCells[e] = 1; usedCells[neibCellId] = 1;
//if (e%2 !== 0 && !cells.c[e].some(c => cells.state[c] === neib)) return; queue.push(neibCellId);
queue.push(e);
}); });
} }
const name = getAdjective(organized.name.split(" ")[0]) + " Proselytism"; const name = getAdjective(religion.name.split(" ")[0]) + " Proselytism";
pack.zones.push({i: pack.zones.length, name, type: "Proselytism", cells: cellsArray, color: "url(#hatch6)"}); pack.zones.push({i: pack.zones.length, name, type: "Proselytism", cells: proselytismCells, color: "url(#hatch6)"});
} }
function addCrusade(usedCells) { function addCrusade(usedCells) {
const heresy = ra(pack.religions.filter(r => r.type === "Heresy")); const {cells, religions} = pack;
if (!heresy) return;
const cells = pack.cells; const heresies = religions.filter(r => !r.removed && r.type === "Heresy");
if (!heresies.length) return;
const heresy = ra(heresies);
const crusadeCells = cells.i.filter(i => !usedCells[i] && cells.religion[i] === heresy.i); const crusadeCells = cells.i.filter(i => !usedCells[i] && cells.religion[i] === heresy.i);
if (!crusadeCells.length) return; if (!crusadeCells.length) return;
crusadeCells.forEach(i => (usedCells[i] = 1)); crusadeCells.forEach(i => (usedCells[i] = 1));