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

View file

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

View file

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