mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-04 09:31:23 +01:00
refactor: migrate zones (#1300)
* refactor: migrate zones * refactor: remove duplicate markers property from PackedGraph interface
This commit is contained in:
parent
86fc62da03
commit
8ba29b2561
5 changed files with 672 additions and 456 deletions
|
|
@ -1,454 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
window.Zones = (function () {
|
|
||||||
const config = {
|
|
||||||
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.4, generate: addDisease}, // disease starting in a random city
|
|
||||||
disaster: {quantity: 1, generate: addDisaster}, // disaster starting in a random city
|
|
||||||
eruption: {quantity: 1, generate: addEruption}, // eruption aroung volcano
|
|
||||||
avalanche: {quantity: 0.8, generate: addAvalanche}, // avalanche impacting highland road
|
|
||||||
fault: {quantity: 1, generate: addFault}, // fault line in elevated areas
|
|
||||||
flood: {quantity: 1, generate: addFlood}, // flood on river banks
|
|
||||||
tsunami: {quantity: 1, generate: addTsunami} // tsunami starting near coast
|
|
||||||
};
|
|
||||||
|
|
||||||
const generate = function (globalModifier = 1) {
|
|
||||||
TIME && console.time("generateZones");
|
|
||||||
|
|
||||||
const usedCells = new Uint8Array(pack.cells.i.length);
|
|
||||||
pack.zones = [];
|
|
||||||
|
|
||||||
Object.values(config).forEach(type => {
|
|
||||||
const expectedNumber = type.quantity * globalModifier;
|
|
||||||
let number = gauss(expectedNumber, expectedNumber / 2, 0, 100);
|
|
||||||
while (number--) type.generate(usedCells);
|
|
||||||
});
|
|
||||||
|
|
||||||
TIME && console.timeEnd("generateZones");
|
|
||||||
};
|
|
||||||
|
|
||||||
function addInvasion(usedCells) {
|
|
||||||
const {cells, states} = pack;
|
|
||||||
|
|
||||||
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 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 startCell = ra(borderCells);
|
|
||||||
if (startCell === undefined) return;
|
|
||||||
|
|
||||||
const invasionCells = [];
|
|
||||||
const queue = [startCell];
|
|
||||||
const maxCells = rand(5, 30);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const cellId = P(0.4) ? queue.shift() : queue.pop();
|
|
||||||
invasionCells.push(cellId);
|
|
||||||
if (invasionCells.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (usedCells[neibCellId]) return;
|
|
||||||
if (cells.state[neibCellId] !== defender) return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const subtype = rw({
|
|
||||||
Invasion: 5,
|
|
||||||
Occupation: 4,
|
|
||||||
Conquest: 3,
|
|
||||||
Incursion: 2,
|
|
||||||
Intervention: 2,
|
|
||||||
Assault: 1,
|
|
||||||
Foray: 1,
|
|
||||||
Intrusion: 1,
|
|
||||||
Irruption: 1,
|
|
||||||
Offensive: 1,
|
|
||||||
Pillaging: 1,
|
|
||||||
Plunder: 1,
|
|
||||||
Raid: 1,
|
|
||||||
Skirmishes: 1
|
|
||||||
});
|
|
||||||
const name = getAdjective(states[attacker].name) + " " + subtype;
|
|
||||||
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Invasion", cells: invasionCells, 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 neibStateId = ra(state.neighbors.filter(n => n && !states[n].removed));
|
|
||||||
if (!neibStateId) return;
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const queue = [];
|
|
||||||
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 cellId = queue.shift();
|
|
||||||
cellsArray.push(cellId);
|
|
||||||
if (cellsArray.length >= maxCells) break;
|
|
||||||
|
|
||||||
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,
|
|
||||||
Insurrection: 2,
|
|
||||||
Mutineers: 1,
|
|
||||||
Insurgents: 1,
|
|
||||||
Rebellion: 1,
|
|
||||||
Renegades: 1,
|
|
||||||
Revolters: 1,
|
|
||||||
Revolutionaries: 1,
|
|
||||||
Rioters: 1,
|
|
||||||
Separatists: 1,
|
|
||||||
Secessionists: 1,
|
|
||||||
Conspiracy: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
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 {cells, religions} = pack;
|
|
||||||
|
|
||||||
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.h[i] < 20 &&
|
|
||||||
cells.pop[i] &&
|
|
||||||
cells.religion[i] !== religion.i &&
|
|
||||||
cells.c[i].some(c => cells.religion[c] === religion.i)
|
|
||||||
);
|
|
||||||
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 cellId = queue.shift();
|
|
||||||
proselytismCells.push(cellId);
|
|
||||||
if (proselytismCells.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (usedCells[neibCellId]) return;
|
|
||||||
if (cells.religion[neibCellId] !== targetReligionId) return;
|
|
||||||
if (cells.h[neibCellId] < 20 || !cells.pop[i]) return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {cells, religions} = pack;
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
const name = getAdjective(heresy.name.split(" ")[0]) + " Crusade";
|
|
||||||
pack.zones.push({
|
|
||||||
i: pack.zones.length,
|
|
||||||
name,
|
|
||||||
type: "Crusade",
|
|
||||||
cells: Array.from(crusadeCells),
|
|
||||||
color: "url(#hatch6)"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDisease(usedCells) {
|
|
||||||
const {cells, burgs} = pack;
|
|
||||||
|
|
||||||
const burg = ra(burgs.filter(b => !usedCells[b.cell] && b.i && !b.removed)); // random burg
|
|
||||||
if (!burg) return;
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const cost = [];
|
|
||||||
const maxCells = rand(20, 40);
|
|
||||||
|
|
||||||
const queue = new FlatQueue();
|
|
||||||
queue.push({e: burg.cell, p: 0}, 0);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const next = queue.pop();
|
|
||||||
if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e);
|
|
||||||
usedCells[next.e] = 1;
|
|
||||||
|
|
||||||
cells.c[next.e].forEach(nextCellId => {
|
|
||||||
const c = Routes.getRoute(next.e, nextCellId) ? 5 : 100;
|
|
||||||
const p = next.p + c;
|
|
||||||
if (p > maxCells) return;
|
|
||||||
|
|
||||||
if (!cost[nextCellId] || p < cost[nextCellId]) {
|
|
||||||
cost[nextCellId] = p;
|
|
||||||
queue.push({e: nextCellId, p}, p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const name = `${(() => {
|
|
||||||
const model = rw({color: 2, animal: 1, adjective: 1});
|
|
||||||
if (model === "color") return ra(["Amber", "Azure", "Black", "Blue", "Brown", "Crimson", "Emerald", "Golden", "Green", "Grey", "Orange", "Pink", "Purple", "Red", "Ruby", "Scarlet", "Silver", "Violet", "White", "Yellow"]);
|
|
||||||
if (model === "animal") return ra(["Ape", "Bear", "Bird", "Boar", "Cat", "Cow", "Deer", "Dog", "Fox", "Goat", "Horse", "Lion", "Pig", "Rat", "Raven", "Sheep", "Spider", "Tiger", "Viper", "Wolf", "Worm", "Wyrm"]);
|
|
||||||
if (model === "adjective") return ra(["Blind", "Bloody", "Brutal", "Burning", "Deadly", "Fatal", "Furious", "Great", "Grim", "Horrible", "Invisible", "Lethal", "Loud", "Mortal", "Savage", "Severe", "Silent", "Unknown", "Venomous", "Vicious"]);
|
|
||||||
})()} ${rw({Fever: 5, Plague: 3, Cough: 3, Flu: 2, Pox: 2, Cholera: 2, Typhoid: 2, Leprosy: 1, Smallpox: 1, Pestilence: 1, Consumption: 1, Malaria: 1, Dropsy: 1})}`;
|
|
||||||
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Disease", cells: cellsArray, color: "url(#hatch12)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDisaster(usedCells) {
|
|
||||||
const {cells, burgs} = pack;
|
|
||||||
|
|
||||||
const burg = ra(burgs.filter(b => !usedCells[b.cell] && b.i && !b.removed));
|
|
||||||
if (!burg) return;
|
|
||||||
usedCells[burg.cell] = 1;
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const cost = [];
|
|
||||||
const maxCells = rand(5, 25);
|
|
||||||
|
|
||||||
const queue = new FlatQueue();
|
|
||||||
queue.push({e: burg.cell, p: 0}, 0);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const next = queue.pop();
|
|
||||||
if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e);
|
|
||||||
usedCells[next.e] = 1;
|
|
||||||
|
|
||||||
cells.c[next.e].forEach(function (e) {
|
|
||||||
const c = rand(1, 10);
|
|
||||||
const p = next.p + c;
|
|
||||||
if (p > maxCells) return;
|
|
||||||
|
|
||||||
if (!cost[e] || p < cost[e]) {
|
|
||||||
cost[e] = p;
|
|
||||||
queue.push({e, p}, p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = rw({
|
|
||||||
Famine: 5,
|
|
||||||
Drought: 3,
|
|
||||||
Earthquake: 3,
|
|
||||||
Dearth: 1,
|
|
||||||
Tornadoes: 1,
|
|
||||||
Wildfires: 1,
|
|
||||||
Storms: 1,
|
|
||||||
Blight: 1
|
|
||||||
});
|
|
||||||
const name = getAdjective(burg.name) + " " + type;
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Disaster", cells: cellsArray, color: "url(#hatch5)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEruption(usedCells) {
|
|
||||||
const {cells, markers} = pack;
|
|
||||||
|
|
||||||
const volcanoe = markers.find(m => m.type === "volcanoes" && !usedCells[m.cell]);
|
|
||||||
if (!volcanoe) return;
|
|
||||||
usedCells[volcanoe.cell] = 1;
|
|
||||||
|
|
||||||
const note = notes.find(n => n.id === "marker" + volcanoe.i);
|
|
||||||
if (note) note.legend = note.legend.replace("Active volcano", "Erupting volcano");
|
|
||||||
const name = note ? note.name.replace(" Volcano", "") + " Eruption" : "Volcano Eruption";
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const queue = [volcanoe.cell];
|
|
||||||
const maxCells = rand(10, 30);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const cellId = P(0.5) ? queue.shift() : queue.pop();
|
|
||||||
cellsArray.push(cellId);
|
|
||||||
if (cellsArray.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (usedCells[neibCellId] || cells.h[neibCellId] < 20) return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Eruption", cells: cellsArray, color: "url(#hatch7)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addAvalanche(usedCells) {
|
|
||||||
const {cells} = pack;
|
|
||||||
|
|
||||||
const routeCells = cells.i.filter(i => !usedCells[i] && Routes.isConnected(i) && cells.h[i] >= 70);
|
|
||||||
if (!routeCells.length) return;
|
|
||||||
|
|
||||||
const startCell = ra(routeCells);
|
|
||||||
usedCells[startCell] = 1;
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const queue = [startCell];
|
|
||||||
const maxCells = rand(3, 15);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const cellId = P(0.3) ? queue.shift() : queue.pop();
|
|
||||||
cellsArray.push(cellId);
|
|
||||||
if (cellsArray.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (usedCells[neibCellId] || cells.h[neibCellId] < 65) return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = getAdjective(Names.getCultureShort(cells.culture[startCell])) + " Avalanche";
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Avalanche", cells: cellsArray, color: "url(#hatch5)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFault(usedCells) {
|
|
||||||
const cells = pack.cells;
|
|
||||||
|
|
||||||
const elevatedCells = cells.i.filter(i => !usedCells[i] && cells.h[i] > 50 && cells.h[i] < 70);
|
|
||||||
if (!elevatedCells.length) return;
|
|
||||||
|
|
||||||
const startCell = ra(elevatedCells);
|
|
||||||
usedCells[startCell] = 1;
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const queue = [startCell];
|
|
||||||
const maxCells = rand(3, 15);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const cellId = queue.pop();
|
|
||||||
if (cells.h[cellId] >= 20) cellsArray.push(cellId);
|
|
||||||
if (cellsArray.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (usedCells[neibCellId] || cells.r[neibCellId]) return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = getAdjective(Names.getCultureShort(cells.culture[startCell])) + " Fault";
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Fault", cells: cellsArray, color: "url(#hatch2)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFlood(usedCells) {
|
|
||||||
const cells = pack.cells;
|
|
||||||
|
|
||||||
const fl = cells.fl.filter(Boolean);
|
|
||||||
const meanFlux = d3.mean(fl);
|
|
||||||
const maxFlux = d3.max(fl);
|
|
||||||
const fluxThreshold = (maxFlux - meanFlux) / 2 + meanFlux;
|
|
||||||
|
|
||||||
const bigRiverCells = cells.i.filter(
|
|
||||||
i => !usedCells[i] && cells.h[i] < 50 && cells.r[i] && cells.fl[i] > fluxThreshold && cells.burg[i]
|
|
||||||
);
|
|
||||||
if (!bigRiverCells.length) return;
|
|
||||||
|
|
||||||
const startCell = ra(bigRiverCells);
|
|
||||||
usedCells[startCell] = 1;
|
|
||||||
|
|
||||||
const riverId = cells.r[startCell];
|
|
||||||
const cellsArray = [];
|
|
||||||
const queue = [startCell];
|
|
||||||
const maxCells = rand(5, 30);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const cellId = queue.pop();
|
|
||||||
cellsArray.push(cellId);
|
|
||||||
if (cellsArray.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (
|
|
||||||
usedCells[neibCellId] ||
|
|
||||||
cells.h[neibCellId] < 20 ||
|
|
||||||
cells.r[neibCellId] !== riverId ||
|
|
||||||
cells.h[neibCellId] > 50 ||
|
|
||||||
cells.fl[neibCellId] < meanFlux
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = getAdjective(pack.burgs[cells.burg[startCell]].name) + " Flood";
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Flood", cells: cellsArray, color: "url(#hatch13)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTsunami(usedCells) {
|
|
||||||
const {cells, features} = pack;
|
|
||||||
|
|
||||||
const coastalCells = cells.i.filter(
|
|
||||||
i => !usedCells[i] && cells.t[i] === -1 && features[cells.f[i]].type !== "lake"
|
|
||||||
);
|
|
||||||
if (!coastalCells.length) return;
|
|
||||||
|
|
||||||
const startCell = ra(coastalCells);
|
|
||||||
usedCells[startCell] = 1;
|
|
||||||
|
|
||||||
const cellsArray = [];
|
|
||||||
const queue = [startCell];
|
|
||||||
const maxCells = rand(10, 30);
|
|
||||||
|
|
||||||
while (queue.length) {
|
|
||||||
const cellId = queue.shift();
|
|
||||||
if (cells.t[cellId] === 1) cellsArray.push(cellId);
|
|
||||||
if (cellsArray.length >= maxCells) break;
|
|
||||||
|
|
||||||
cells.c[cellId].forEach(neibCellId => {
|
|
||||||
if (usedCells[neibCellId]) return;
|
|
||||||
if (cells.t[neibCellId] > 2) return;
|
|
||||||
if (pack.features[cells.f[neibCellId]].type === "lake") return;
|
|
||||||
usedCells[neibCellId] = 1;
|
|
||||||
queue.push(neibCellId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = getAdjective(Names.getCultureShort(cells.culture[startCell])) + " Tsunami";
|
|
||||||
pack.zones.push({i: pack.zones.length, name, type: "Tsunami", cells: cellsArray, color: "url(#hatch13)"});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {generate};
|
|
||||||
})();
|
|
||||||
|
|
@ -8497,7 +8497,6 @@
|
||||||
<script defer src="modules/ice.js?v=1.111.0"></script>
|
<script defer src="modules/ice.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/military-generator.js?v=1.107.0"></script>
|
<script defer src="modules/military-generator.js?v=1.107.0"></script>
|
||||||
<script defer src="modules/markers-generator.js?v=1.107.0"></script>
|
<script defer src="modules/markers-generator.js?v=1.107.0"></script>
|
||||||
<script defer src="modules/zones-generator.js?v=1.106.0"></script>
|
|
||||||
<script defer src="modules/coa-generator.js?v=1.99.00"></script>
|
<script defer src="modules/coa-generator.js?v=1.99.00"></script>
|
||||||
<script defer src="modules/resample.js?v=1.112.1"></script>
|
<script defer src="modules/resample.js?v=1.112.1"></script>
|
||||||
<script defer src="libs/alea.min.js?v1.105.0"></script>
|
<script defer src="libs/alea.min.js?v1.105.0"></script>
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,6 @@ import "./biomes";
|
||||||
import "./cultures-generator";
|
import "./cultures-generator";
|
||||||
import "./routes-generator";
|
import "./routes-generator";
|
||||||
import "./states-generator";
|
import "./states-generator";
|
||||||
|
import "./zones-generator";
|
||||||
import "./religions-generator";
|
import "./religions-generator";
|
||||||
import "./provinces-generator";
|
import "./provinces-generator";
|
||||||
|
|
|
||||||
668
src/modules/zones-generator.ts
Normal file
668
src/modules/zones-generator.ts
Normal file
|
|
@ -0,0 +1,668 @@
|
||||||
|
import { max, mean } from "d3";
|
||||||
|
import { gauss, getAdjective, P, ra, rand, rw } from "../utils";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var Zones: ZonesModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Zone {
|
||||||
|
i: number;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
cells: number[];
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZoneGenerator = (usedCells: Uint8Array) => void;
|
||||||
|
|
||||||
|
interface ZoneConfig {
|
||||||
|
quantity: number;
|
||||||
|
generate: ZoneGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZonesModule {
|
||||||
|
private config: Record<string, ZoneConfig>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.config = {
|
||||||
|
invasion: { quantity: 2, generate: (u) => this.addInvasion(u) },
|
||||||
|
rebels: { quantity: 1.5, generate: (u) => this.addRebels(u) },
|
||||||
|
proselytism: { quantity: 1.6, generate: (u) => this.addProselytism(u) },
|
||||||
|
crusade: { quantity: 1.6, generate: (u) => this.addCrusade(u) },
|
||||||
|
disease: { quantity: 1.4, generate: (u) => this.addDisease(u) },
|
||||||
|
disaster: { quantity: 1, generate: (u) => this.addDisaster(u) },
|
||||||
|
eruption: { quantity: 1, generate: (u) => this.addEruption(u) },
|
||||||
|
avalanche: { quantity: 0.8, generate: (u) => this.addAvalanche(u) },
|
||||||
|
fault: { quantity: 1, generate: (u) => this.addFault(u) },
|
||||||
|
flood: { quantity: 1, generate: (u) => this.addFlood(u) },
|
||||||
|
tsunami: { quantity: 1, generate: (u) => this.addTsunami(u) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generate(globalModifier = 1) {
|
||||||
|
TIME && console.time("generateZones");
|
||||||
|
|
||||||
|
const usedCells = new Uint8Array(pack.cells.i.length);
|
||||||
|
pack.zones = [];
|
||||||
|
|
||||||
|
Object.values(this.config).forEach((type) => {
|
||||||
|
const expectedNumber = type.quantity * globalModifier;
|
||||||
|
let number = gauss(expectedNumber, expectedNumber / 2, 0, 100);
|
||||||
|
while (number--) type.generate(usedCells);
|
||||||
|
});
|
||||||
|
|
||||||
|
TIME && console.timeEnd("generateZones");
|
||||||
|
}
|
||||||
|
|
||||||
|
private addInvasion(usedCells: Uint8Array) {
|
||||||
|
const { cells, states } = pack;
|
||||||
|
|
||||||
|
const ongoingConflicts = states
|
||||||
|
.filter((s) => s.i && !s.removed && s.campaigns)
|
||||||
|
.flatMap((s) => s.campaigns!)
|
||||||
|
.filter((c) => !c.end);
|
||||||
|
if (!ongoingConflicts.length) return;
|
||||||
|
const { defender, attacker } = ra(ongoingConflicts);
|
||||||
|
|
||||||
|
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 startCell = ra(borderCells);
|
||||||
|
if (startCell === undefined) return;
|
||||||
|
|
||||||
|
const invasionCells: number[] = [];
|
||||||
|
const queue = [startCell];
|
||||||
|
const maxCells = rand(5, 30);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = P(0.4) ? queue.shift()! : queue.pop()!;
|
||||||
|
invasionCells.push(cellId);
|
||||||
|
if (invasionCells.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (usedCells[neibCellId]) return;
|
||||||
|
if (cells.state[neibCellId] !== defender) return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtype = rw({
|
||||||
|
Invasion: 5,
|
||||||
|
Occupation: 4,
|
||||||
|
Conquest: 3,
|
||||||
|
Incursion: 2,
|
||||||
|
Intervention: 2,
|
||||||
|
Assault: 1,
|
||||||
|
Foray: 1,
|
||||||
|
Intrusion: 1,
|
||||||
|
Irruption: 1,
|
||||||
|
Offensive: 1,
|
||||||
|
Pillaging: 1,
|
||||||
|
Plunder: 1,
|
||||||
|
Raid: 1,
|
||||||
|
Skirmishes: 1,
|
||||||
|
});
|
||||||
|
const name = `${getAdjective(states[attacker].name)} ${subtype}`;
|
||||||
|
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Invasion",
|
||||||
|
cells: invasionCells,
|
||||||
|
color: "url(#hatch1)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addRebels(usedCells: Uint8Array) {
|
||||||
|
const { cells, states } = pack;
|
||||||
|
|
||||||
|
const state = ra(
|
||||||
|
states.filter((s) => s.i && !s.removed && s.neighbors?.some(Boolean)),
|
||||||
|
);
|
||||||
|
if (!state) return;
|
||||||
|
|
||||||
|
const neibStateId = ra(
|
||||||
|
state.neighbors!.filter((n: number) => n && !states[n].removed),
|
||||||
|
);
|
||||||
|
if (!neibStateId) return;
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const queue: number[] = [];
|
||||||
|
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 cellId = queue.shift()!;
|
||||||
|
cellsArray.push(cellId);
|
||||||
|
if (cellsArray.length >= maxCells) break;
|
||||||
|
|
||||||
|
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,
|
||||||
|
Insurrection: 2,
|
||||||
|
Mutineers: 1,
|
||||||
|
Insurgents: 1,
|
||||||
|
Rebellion: 1,
|
||||||
|
Renegades: 1,
|
||||||
|
Revolters: 1,
|
||||||
|
Revolutionaries: 1,
|
||||||
|
Rioters: 1,
|
||||||
|
Separatists: 1,
|
||||||
|
Secessionists: 1,
|
||||||
|
Conspiracy: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const name = `${getAdjective(states[neibStateId].name)} ${rebels}`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Rebels",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch3)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addProselytism(usedCells: Uint8Array) {
|
||||||
|
const { cells, religions } = pack;
|
||||||
|
|
||||||
|
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.h[i] >= 20 &&
|
||||||
|
cells.pop[i] &&
|
||||||
|
cells.religion[i] !== religion.i &&
|
||||||
|
cells.c[i].some((c) => cells.religion[c] === religion.i),
|
||||||
|
);
|
||||||
|
const startCell = ra(targetBorderCells);
|
||||||
|
if (!startCell) return;
|
||||||
|
|
||||||
|
const targetReligionId = cells.religion[startCell];
|
||||||
|
const proselytismCells: number[] = [];
|
||||||
|
const queue = [startCell];
|
||||||
|
const maxCells = rand(10, 30);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = queue.shift()!;
|
||||||
|
proselytismCells.push(cellId);
|
||||||
|
if (proselytismCells.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (usedCells[neibCellId]) return;
|
||||||
|
if (cells.religion[neibCellId] !== targetReligionId) return;
|
||||||
|
if (cells.h[neibCellId] < 20 || !cells.pop[neibCellId]) return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${getAdjective(religion.name.split(" ")[0])} Proselytism`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Proselytism",
|
||||||
|
cells: proselytismCells,
|
||||||
|
color: "url(#hatch6)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addCrusade(usedCells: Uint8Array) {
|
||||||
|
const { cells, religions } = pack;
|
||||||
|
|
||||||
|
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;
|
||||||
|
for (const i of crusadeCells) {
|
||||||
|
usedCells[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${getAdjective(heresy.name.split(" ")[0])} Crusade`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Crusade",
|
||||||
|
cells: Array.from(crusadeCells),
|
||||||
|
color: "url(#hatch6)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addDisease(usedCells: Uint8Array) {
|
||||||
|
const { cells, burgs } = pack;
|
||||||
|
|
||||||
|
const burg = ra(
|
||||||
|
burgs.filter((b) => !usedCells[b.cell] && b.i && !b.removed),
|
||||||
|
);
|
||||||
|
if (!burg) return;
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const cost: number[] = [];
|
||||||
|
const maxCells = rand(20, 40);
|
||||||
|
|
||||||
|
const queue = new FlatQueue();
|
||||||
|
queue.push({ e: burg.cell, p: 0 }, 0);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const next = queue.pop();
|
||||||
|
if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e);
|
||||||
|
usedCells[next.e] = 1;
|
||||||
|
|
||||||
|
cells.c[next.e].forEach((nextCellId) => {
|
||||||
|
const c = Routes.getRoute(next.e, nextCellId) ? 5 : 100;
|
||||||
|
const p = next.p + c;
|
||||||
|
if (p > maxCells) return;
|
||||||
|
|
||||||
|
if (!cost[nextCellId] || p < cost[nextCellId]) {
|
||||||
|
cost[nextCellId] = p;
|
||||||
|
queue.push({ e: nextCellId, p }, p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorName = this.getDiseaseName("color");
|
||||||
|
const animalName = this.getDiseaseName("animal");
|
||||||
|
const adjectiveName = this.getDiseaseName("adjective");
|
||||||
|
|
||||||
|
const model = rw({ color: 2, animal: 1, adjective: 1 });
|
||||||
|
const prefix =
|
||||||
|
model === "color"
|
||||||
|
? colorName
|
||||||
|
: model === "animal"
|
||||||
|
? animalName
|
||||||
|
: adjectiveName;
|
||||||
|
|
||||||
|
const disease = rw({
|
||||||
|
Fever: 5,
|
||||||
|
Plague: 3,
|
||||||
|
Cough: 3,
|
||||||
|
Flu: 2,
|
||||||
|
Pox: 2,
|
||||||
|
Cholera: 2,
|
||||||
|
Typhoid: 2,
|
||||||
|
Leprosy: 1,
|
||||||
|
Smallpox: 1,
|
||||||
|
Pestilence: 1,
|
||||||
|
Consumption: 1,
|
||||||
|
Malaria: 1,
|
||||||
|
Dropsy: 1,
|
||||||
|
});
|
||||||
|
const name = `${prefix} ${disease}`;
|
||||||
|
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Disease",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch12)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDiseaseName(model: "color" | "animal" | "adjective"): string {
|
||||||
|
if (model === "color")
|
||||||
|
return ra([
|
||||||
|
"Amber",
|
||||||
|
"Azure",
|
||||||
|
"Black",
|
||||||
|
"Blue",
|
||||||
|
"Brown",
|
||||||
|
"Crimson",
|
||||||
|
"Emerald",
|
||||||
|
"Golden",
|
||||||
|
"Green",
|
||||||
|
"Grey",
|
||||||
|
"Orange",
|
||||||
|
"Pink",
|
||||||
|
"Purple",
|
||||||
|
"Red",
|
||||||
|
"Ruby",
|
||||||
|
"Scarlet",
|
||||||
|
"Silver",
|
||||||
|
"Violet",
|
||||||
|
"White",
|
||||||
|
"Yellow",
|
||||||
|
]);
|
||||||
|
if (model === "animal")
|
||||||
|
return ra([
|
||||||
|
"Ape",
|
||||||
|
"Bear",
|
||||||
|
"Bird",
|
||||||
|
"Boar",
|
||||||
|
"Cat",
|
||||||
|
"Cow",
|
||||||
|
"Deer",
|
||||||
|
"Dog",
|
||||||
|
"Fox",
|
||||||
|
"Goat",
|
||||||
|
"Horse",
|
||||||
|
"Lion",
|
||||||
|
"Pig",
|
||||||
|
"Rat",
|
||||||
|
"Raven",
|
||||||
|
"Sheep",
|
||||||
|
"Spider",
|
||||||
|
"Tiger",
|
||||||
|
"Viper",
|
||||||
|
"Wolf",
|
||||||
|
"Worm",
|
||||||
|
"Wyrm",
|
||||||
|
]);
|
||||||
|
return ra([
|
||||||
|
"Blind",
|
||||||
|
"Bloody",
|
||||||
|
"Brutal",
|
||||||
|
"Burning",
|
||||||
|
"Deadly",
|
||||||
|
"Fatal",
|
||||||
|
"Furious",
|
||||||
|
"Great",
|
||||||
|
"Grim",
|
||||||
|
"Horrible",
|
||||||
|
"Invisible",
|
||||||
|
"Lethal",
|
||||||
|
"Loud",
|
||||||
|
"Mortal",
|
||||||
|
"Savage",
|
||||||
|
"Severe",
|
||||||
|
"Silent",
|
||||||
|
"Unknown",
|
||||||
|
"Venomous",
|
||||||
|
"Vicious",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addDisaster(usedCells: Uint8Array) {
|
||||||
|
const { cells, burgs } = pack;
|
||||||
|
|
||||||
|
const burg = ra(
|
||||||
|
burgs.filter((b) => !usedCells[b.cell] && b.i && !b.removed),
|
||||||
|
);
|
||||||
|
if (!burg) return;
|
||||||
|
usedCells[burg.cell] = 1;
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const cost: number[] = [];
|
||||||
|
const maxCells = rand(5, 25);
|
||||||
|
|
||||||
|
const queue = new FlatQueue();
|
||||||
|
queue.push({ e: burg.cell, p: 0 }, 0);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const next = queue.pop();
|
||||||
|
if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e);
|
||||||
|
usedCells[next.e] = 1;
|
||||||
|
|
||||||
|
cells.c[next.e].forEach((e) => {
|
||||||
|
const c = rand(1, 10);
|
||||||
|
const p = next.p + c;
|
||||||
|
if (p > maxCells) return;
|
||||||
|
|
||||||
|
if (!cost[e] || p < cost[e]) {
|
||||||
|
cost[e] = p;
|
||||||
|
queue.push({ e, p }, p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = rw({
|
||||||
|
Famine: 5,
|
||||||
|
Drought: 3,
|
||||||
|
Earthquake: 3,
|
||||||
|
Dearth: 1,
|
||||||
|
Tornadoes: 1,
|
||||||
|
Wildfires: 1,
|
||||||
|
Storms: 1,
|
||||||
|
Blight: 1,
|
||||||
|
});
|
||||||
|
const name = `${getAdjective(burg.name!)} ${type}`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Disaster",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch5)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addEruption(usedCells: Uint8Array) {
|
||||||
|
const { cells, markers } = pack;
|
||||||
|
|
||||||
|
const volcanoe = markers.find(
|
||||||
|
(m) => m.type === "volcanoes" && !usedCells[m.cell],
|
||||||
|
);
|
||||||
|
if (!volcanoe) return;
|
||||||
|
usedCells[volcanoe.cell] = 1;
|
||||||
|
|
||||||
|
const note = notes.find((n) => n.id === `marker${volcanoe.i}`);
|
||||||
|
if (note)
|
||||||
|
note.legend = note.legend.replace("Active volcano", "Erupting volcano");
|
||||||
|
const name = note
|
||||||
|
? `${note.name.replace(" Volcano", "")} Eruption`
|
||||||
|
: "Volcano Eruption";
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const queue = [volcanoe.cell];
|
||||||
|
const maxCells = rand(10, 30);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = P(0.5) ? queue.shift()! : queue.pop()!;
|
||||||
|
cellsArray.push(cellId);
|
||||||
|
if (cellsArray.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (usedCells[neibCellId] || cells.h[neibCellId] < 20) return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Eruption",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch7)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addAvalanche(usedCells: Uint8Array) {
|
||||||
|
const { cells } = pack;
|
||||||
|
|
||||||
|
const routeCells = cells.i.filter(
|
||||||
|
(i) => !usedCells[i] && Routes.isConnected(i) && cells.h[i] >= 70,
|
||||||
|
);
|
||||||
|
if (!routeCells.length) return;
|
||||||
|
|
||||||
|
const startCell = ra(routeCells);
|
||||||
|
usedCells[startCell] = 1;
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const queue = [startCell];
|
||||||
|
const maxCells = rand(3, 15);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = P(0.3) ? queue.shift()! : queue.pop()!;
|
||||||
|
cellsArray.push(cellId);
|
||||||
|
if (cellsArray.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (usedCells[neibCellId] || cells.h[neibCellId] < 65) return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${getAdjective(Names.getCultureShort(cells.culture[startCell]))} Avalanche`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Avalanche",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch5)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addFault(usedCells: Uint8Array) {
|
||||||
|
const cells = pack.cells;
|
||||||
|
|
||||||
|
const elevatedCells = cells.i.filter(
|
||||||
|
(i) => !usedCells[i] && cells.h[i] > 50 && cells.h[i] < 70,
|
||||||
|
);
|
||||||
|
if (!elevatedCells.length) return;
|
||||||
|
|
||||||
|
const startCell = ra(elevatedCells);
|
||||||
|
usedCells[startCell] = 1;
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const queue = [startCell];
|
||||||
|
const maxCells = rand(3, 15);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = queue.pop()!;
|
||||||
|
if (cells.h[cellId] >= 20) cellsArray.push(cellId);
|
||||||
|
if (cellsArray.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (usedCells[neibCellId] || cells.r[neibCellId]) return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${getAdjective(Names.getCultureShort(cells.culture[startCell]))} Fault`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Fault",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch2)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addFlood(usedCells: Uint8Array) {
|
||||||
|
const cells = pack.cells;
|
||||||
|
|
||||||
|
const fl = cells.fl.filter(Boolean);
|
||||||
|
const meanFlux = mean(fl) ?? 0;
|
||||||
|
const maxFlux = max(fl) ?? 0;
|
||||||
|
const fluxThreshold = (maxFlux - meanFlux) / 2 + meanFlux;
|
||||||
|
|
||||||
|
const bigRiverCells = cells.i.filter(
|
||||||
|
(i) =>
|
||||||
|
!usedCells[i] &&
|
||||||
|
cells.h[i] < 50 &&
|
||||||
|
cells.r[i] &&
|
||||||
|
cells.fl[i] > fluxThreshold &&
|
||||||
|
cells.burg[i],
|
||||||
|
);
|
||||||
|
if (!bigRiverCells.length) return;
|
||||||
|
|
||||||
|
const startCell = ra(bigRiverCells);
|
||||||
|
usedCells[startCell] = 1;
|
||||||
|
|
||||||
|
const riverId = cells.r[startCell];
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const queue = [startCell];
|
||||||
|
const maxCells = rand(5, 30);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = queue.pop()!;
|
||||||
|
cellsArray.push(cellId);
|
||||||
|
if (cellsArray.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (
|
||||||
|
usedCells[neibCellId] ||
|
||||||
|
cells.h[neibCellId] < 20 ||
|
||||||
|
cells.r[neibCellId] !== riverId ||
|
||||||
|
cells.h[neibCellId] > 50 ||
|
||||||
|
cells.fl[neibCellId] < meanFlux
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${getAdjective(pack.burgs[cells.burg[startCell]].name!)} Flood`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Flood",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch13)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addTsunami(usedCells: Uint8Array) {
|
||||||
|
const { cells, features } = pack;
|
||||||
|
|
||||||
|
const coastalCells = cells.i.filter(
|
||||||
|
(i) =>
|
||||||
|
!usedCells[i] &&
|
||||||
|
cells.t[i] === -1 &&
|
||||||
|
features[cells.f[i]].type !== "lake",
|
||||||
|
);
|
||||||
|
if (!coastalCells.length) return;
|
||||||
|
|
||||||
|
const startCell = ra(coastalCells);
|
||||||
|
usedCells[startCell] = 1;
|
||||||
|
|
||||||
|
const cellsArray: number[] = [];
|
||||||
|
const queue = [startCell];
|
||||||
|
const maxCells = rand(10, 30);
|
||||||
|
|
||||||
|
while (queue.length) {
|
||||||
|
const cellId = queue.shift()!;
|
||||||
|
if (cells.t[cellId] === 1) cellsArray.push(cellId);
|
||||||
|
if (cellsArray.length >= maxCells) break;
|
||||||
|
|
||||||
|
cells.c[cellId].forEach((neibCellId) => {
|
||||||
|
if (usedCells[neibCellId]) return;
|
||||||
|
if (cells.t[neibCellId] > 2) return;
|
||||||
|
if (pack.features[cells.f[neibCellId]].type === "lake") return;
|
||||||
|
usedCells[neibCellId] = 1;
|
||||||
|
queue.push(neibCellId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = `${getAdjective(Names.getCultureShort(cells.culture[startCell]))} Tsunami`;
|
||||||
|
pack.zones.push({
|
||||||
|
i: pack.zones.length,
|
||||||
|
name,
|
||||||
|
type: "Tsunami",
|
||||||
|
cells: cellsArray,
|
||||||
|
color: "url(#hatch13)",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Zones = new ZonesModule();
|
||||||
|
|
@ -5,6 +5,7 @@ import type { Province } from "../modules/provinces-generator";
|
||||||
import type { River } from "../modules/river-generator";
|
import type { River } from "../modules/river-generator";
|
||||||
import type { Route } from "../modules/routes-generator";
|
import type { Route } from "../modules/routes-generator";
|
||||||
import type { State } from "../modules/states-generator";
|
import type { State } from "../modules/states-generator";
|
||||||
|
import type { Zone } from "../modules/zones-generator";
|
||||||
|
|
||||||
type TypedArray =
|
type TypedArray =
|
||||||
| Uint8Array
|
| Uint8Array
|
||||||
|
|
@ -58,7 +59,8 @@ export interface PackedGraph {
|
||||||
cultures: Culture[];
|
cultures: Culture[];
|
||||||
routes: Route[];
|
routes: Route[];
|
||||||
religions: any[];
|
religions: any[];
|
||||||
ice: any[];
|
zones: Zone[];
|
||||||
markers: any[];
|
markers: any[];
|
||||||
|
ice: any[];
|
||||||
provinces: Province[];
|
provinces: Province[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue