From 8dd4d486f017bcb60c240303c15bb8f6a44d0e0f Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 26 Aug 2024 03:06:52 +0200 Subject: [PATCH] feat: style - store emblem size mod in style (v1.99.10) --- index.html | 1 + main.js | 438 +------------------------------ modules/dynamic/auto-update.js | 2 +- modules/submap.js | 2 +- modules/ui/heightmap-editor.js | 2 +- modules/ui/tools.js | 2 +- modules/zones-generator.js | 465 +++++++++++++++++++++++++++++++++ versioning.js | 2 +- 8 files changed, 472 insertions(+), 442 deletions(-) create mode 100644 modules/zones-generator.js diff --git a/index.html b/index.html index 6b1ad48d..c9ef341e 100644 --- a/index.html +++ b/index.html @@ -8003,6 +8003,7 @@ + diff --git a/main.js b/main.js index 12f75328..83a7ef50 100644 --- a/main.js +++ b/main.js @@ -662,7 +662,7 @@ async function generate(options) { Military.generate(); Markers.generate(); - addZones(); + Zones.generate(); drawScaleBar(scaleBar, scale); Names.getMapName(); @@ -1484,442 +1484,6 @@ function rankCells() { TIME && console.timeEnd("rankCells"); } -// generate zones -function addZones(number = 1) { - TIME && console.time("addZones"); - const {cells, states, burgs} = pack; - const used = new Uint8Array(cells.i.length); // to store used cells - const zonesData = []; - - for (let i = 0; i < rn(Math.random() * 1.8 * number); i++) addInvasion(); // invasion of enemy lands - for (let i = 0; i < rn(Math.random() * 1.6 * number); i++) addRebels(); // rebels along a state border - for (let i = 0; i < rn(Math.random() * 1.6 * number); i++) addProselytism(); // proselitism of organized religion - for (let i = 0; i < rn(Math.random() * 1.6 * number); i++) addCrusade(); // crusade on heresy lands - for (let i = 0; i < rn(Math.random() * 1.8 * number); i++) addDisease(); // disease starting in a random city - for (let i = 0; i < rn(Math.random() * 1.4 * number); i++) addDisaster(); // disaster starting in a random city - for (let i = 0; i < rn(Math.random() * 1.4 * number); i++) addEruption(); // volcanic eruption aroung volcano - for (let i = 0; i < rn(Math.random() * 1.0 * number); i++) addAvalanche(); // avalanche impacting highland road - for (let i = 0; i < rn(Math.random() * 1.4 * number); i++) addFault(); // fault line in elevated areas - for (let i = 0; i < rn(Math.random() * 1.4 * number); i++) addFlood(); // flood on river banks - for (let i = 0; i < rn(Math.random() * 1.2 * number); i++) addTsunami(); // tsunami starting near coast - - drawZones(); - - function addInvasion() { - const atWar = states.filter(s => s.diplomacy && s.diplomacy.some(d => d === "Enemy")); - if (!atWar.length) return; - - const invader = ra(atWar); - const target = invader.diplomacy.findIndex(d => d === "Enemy"); - - const cell = ra( - cells.i.filter(i => cells.state[i] === target && cells.c[i].some(c => cells.state[c] === invader.i)) - ); - if (!cell) return; - - const cellsArray = [], - queue = [cell], - power = rand(5, 30); - - while (queue.length) { - const q = P(0.4) ? queue.shift() : queue.pop(); - cellsArray.push(q); - if (cellsArray.length > power) break; - - cells.c[q].forEach(e => { - if (used[e]) return; - if (cells.state[e] !== target) return; - used[e] = 1; - queue.push(e); - }); - } - - const invasion = rw({ - Invasion: 4, - Occupation: 3, - Raid: 2, - Conquest: 2, - Subjugation: 1, - Foray: 1, - Skirmishes: 1, - Incursion: 2, - Pillaging: 1, - Intervention: 1 - }); - const name = getAdjective(invader.name) + " " + invasion; - zonesData.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"}); - } - - function addRebels() { - const state = ra(states.filter(s => s.i && !s.removed && s.neighbors.some(n => n))); - if (!state) return; - - const neib = ra(state.neighbors.filter(n => n && !states[n].removed)); - if (!neib) 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); - - while (queue.length) { - const q = queue.shift(); - cellsArray.push(q); - if (cellsArray.length > power) break; - - cells.c[q].forEach(e => { - if (used[e]) return; - if (cells.state[e] !== state.i) return; - used[e] = 1; - if (e % 4 !== 0 && !cells.c[e].some(c => cells.state[c] === neib)) return; - queue.push(e); - }); - } - - const rebels = rw({ - Rebels: 5, - Insurgents: 2, - Mutineers: 1, - Rioters: 1, - Separatists: 1, - Secessionists: 1, - Insurrection: 2, - Rebellion: 1, - Conspiracy: 2 - }); - const name = getAdjective(states[neib].name) + " " + rebels; - zonesData.push({name, type: "Rebels", cells: cellsArray, fill: "url(#hatch3)"}); - } - - function addProselytism() { - const organized = ra(pack.religions.filter(r => r.type === "Organized")); - if (!organized) return; - - 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) - ) - ); - if (!cell) return; - const target = cells.religion[cell]; - const cellsArray = [], - queue = [cell], - power = rand(10, 30); - - while (queue.length) { - const q = queue.shift(); - cellsArray.push(q); - if (cellsArray.length > power) break; - - cells.c[q].forEach(e => { - if (used[e]) return; - if (cells.religion[e] !== target) return; - if (cells.h[e] < 20) return; - used[e] = 1; - //if (e%2 !== 0 && !cells.c[e].some(c => cells.state[c] === neib)) return; - queue.push(e); - }); - } - - const name = getAdjective(organized.name.split(" ")[0]) + " Proselytism"; - zonesData.push({name, type: "Proselytism", cells: cellsArray, fill: "url(#hatch6)"}); - } - - function addCrusade() { - const heresy = ra(pack.religions.filter(r => r.type === "Heresy")); - if (!heresy) return; - - const cellsArray = cells.i.filter(i => !used[i] && cells.religion[i] === heresy.i); - if (!cellsArray.length) return; - cellsArray.forEach(i => (used[i] = 1)); - - const name = getAdjective(heresy.name.split(" ")[0]) + " Crusade"; - zonesData.push({name, type: "Crusade", cells: cellsArray, fill: "url(#hatch6)"}); - } - - function addDisease() { - const burg = ra(burgs.filter(b => !used[b.cell] && b.i && !b.removed)); // random burg - if (!burg) return; - - const cellsArray = []; - const cost = []; - const power = rand(20, 37); - - const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); - queue.queue({e: burg.cell, p: 0}); - - while (queue.length) { - const next = queue.dequeue(); - if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e); - used[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 > power) return; - - if (!cost[nextCellId] || p < cost[nextCellId]) { - cost[nextCellId] = p; - queue.queue({e: nextCellId, p}); - } - }); - } - - const adjective = () => - ra(["Great", "Silent", "Severe", "Blind", "Unknown", "Loud", "Deadly", "Burning", "Bloody", "Brutal", "Fatal"]); - const animal = () => - ra([ - "Ape", - "Bear", - "Boar", - "Cat", - "Cow", - "Dog", - "Pig", - "Fox", - "Bird", - "Horse", - "Rat", - "Raven", - "Sheep", - "Spider", - "Wolf" - ]); - const color = () => - ra([ - "Golden", - "White", - "Black", - "Red", - "Pink", - "Purple", - "Blue", - "Green", - "Yellow", - "Amber", - "Orange", - "Brown", - "Grey" - ]); - - const type = rw({ - Fever: 5, - Pestilence: 2, - Flu: 2, - Pox: 2, - Smallpox: 2, - Plague: 4, - Cholera: 2, - Dropsy: 1, - Leprosy: 2 - }); - const name = rw({[color()]: 4, [animal()]: 2, [adjective()]: 1}) + " " + type; - zonesData.push({name, type: "Disease", cells: cellsArray, fill: "url(#hatch12)"}); - } - - function addDisaster() { - const burg = ra(burgs.filter(b => !used[b.cell] && b.i && !b.removed)); // random burg - if (!burg) return; - - const cellsArray = [], - cost = [], - power = rand(5, 25); - const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); - queue.queue({e: burg.cell, p: 0}); - - while (queue.length) { - const next = queue.dequeue(); - if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e); - used[next.e] = 1; - - cells.c[next.e].forEach(function (e) { - const c = rand(1, 10); - const p = next.p + c; - if (p > power) return; - - if (!cost[e] || p < cost[e]) { - cost[e] = p; - queue.queue({e, p}); - } - }); - } - - const type = rw({Famine: 5, Dearth: 1, Drought: 3, Earthquake: 3, Tornadoes: 1, Wildfires: 1}); - const name = getAdjective(burg.name) + " " + type; - zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"}); - } - - function addEruption() { - const volcano = byId("markers").querySelector("use[data-id='#marker_volcano']"); - if (!volcano) return; - - const x = +volcano.dataset.x, - y = +volcano.dataset.y, - cell = findCell(x, y); - const id = volcano.id; - const note = notes.filter(n => n.id === id); - - if (note[0]) note[0].legend = note[0].legend.replace("Active volcano", "Erupting volcano"); - const name = note[0] ? note[0].name.replace(" Volcano", "") + " Eruption" : "Volcano Eruption"; - - const cellsArray = [], - queue = [cell], - power = rand(10, 30); - - while (queue.length) { - const q = P(0.5) ? queue.shift() : queue.pop(); - cellsArray.push(q); - if (cellsArray.length > power) break; - cells.c[q].forEach(e => { - if (used[e] || cells.h[e] < 20) return; - used[e] = 1; - queue.push(e); - }); - } - - zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch7)"}); - } - - function addAvalanche() { - const routes = cells.i.filter(i => !used[i] && Routes.isConnected(i) && cells.h[i] >= 70); - if (!routes.length) return; - - const cell = +ra(routes); - const cellsArray = [], - queue = [cell], - power = rand(3, 15); - - while (queue.length) { - const q = P(0.3) ? queue.shift() : queue.pop(); - cellsArray.push(q); - if (cellsArray.length > power) break; - cells.c[q].forEach(e => { - if (used[e] || cells.h[e] < 65) return; - used[e] = 1; - queue.push(e); - }); - } - - const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); - const name = proper + " Avalanche"; - zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"}); - } - - function addFault() { - const elevated = cells.i.filter(i => !used[i] && cells.h[i] > 50 && cells.h[i] < 70); - if (!elevated.length) return; - - const cell = ra(elevated); - const cellsArray = [], - queue = [cell], - power = rand(3, 15); - - while (queue.length) { - const q = queue.pop(); - if (cells.h[q] >= 20) cellsArray.push(q); - if (cellsArray.length > power) break; - cells.c[q].forEach(e => { - if (used[e] || cells.r[e]) return; - used[e] = 1; - queue.push(e); - }); - } - - const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); - const name = proper + " Fault"; - zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch2)"}); - } - - function addFlood() { - const fl = cells.fl.filter(fl => fl), - meanFlux = d3.mean(fl), - maxFlux = d3.max(fl), - flux = (maxFlux - meanFlux) / 2 + meanFlux; - const rivers = cells.i.filter( - i => !used[i] && cells.h[i] < 50 && cells.r[i] && cells.fl[i] > flux && cells.burg[i] - ); - if (!rivers.length) return; - - const cell = +ra(rivers), - river = cells.r[cell]; - const cellsArray = [], - queue = [cell], - power = rand(5, 30); - - while (queue.length) { - const q = queue.pop(); - cellsArray.push(q); - if (cellsArray.length > power) break; - - cells.c[q].forEach(e => { - if (used[e] || cells.h[e] < 20 || cells.r[e] !== river || cells.h[e] > 50 || cells.fl[e] < meanFlux) return; - used[e] = 1; - queue.push(e); - }); - } - - const name = getAdjective(burgs[cells.burg[cell]].name) + " Flood"; - zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"}); - } - - function addTsunami() { - const coastal = cells.i.filter(i => !used[i] && cells.t[i] === -1 && pack.features[cells.f[i]].type !== "lake"); - if (!coastal.length) return; - - const cell = +ra(coastal); - const cellsArray = [], - queue = [cell], - power = rand(10, 30); - - while (queue.length) { - const q = queue.shift(); - if (cells.t[q] === 1) cellsArray.push(q); - if (cellsArray.length > power) break; - - cells.c[q].forEach(e => { - if (used[e]) return; - if (cells.t[e] > 2) return; - if (pack.features[cells.f[e]].type === "lake") return; - used[e] = 1; - queue.push(e); - }); - } - - const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); - const name = proper + " Tsunami"; - zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"}); - } - - function drawZones() { - zones - .selectAll("g") - .data(zonesData) - .enter() - .append("g") - .attr("id", (d, i) => "zone" + i) - .attr("data-description", d => d.name) - .attr("data-type", d => d.type) - .attr("data-cells", d => d.cells.join(",")) - .attr("fill", d => d.fill) - .selectAll("polygon") - .data(d => d.cells) - .enter() - .append("polygon") - .attr("points", d => getPackPolygon(d)) - .attr("id", function (d) { - return this.parentNode.id + "_" + d; - }); - } - - TIME && console.timeEnd("addZones"); -} - // show map stats on generation complete function showStatistics() { const heightmap = byId("templateInput").value; diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js index 4bbb778d..2363a355 100644 --- a/modules/dynamic/auto-update.js +++ b/modules/dynamic/auto-update.js @@ -63,7 +63,7 @@ export function resolveVersionConflicts(version) { .attr("stroke-width", 0) .attr("stroke-dasharray", null) .attr("stroke-linecap", "butt"); - addZones(); + Zones.generate(); if (!markers.selectAll("*").size()) { Markers.generate(); turnButtonOn("toggleMarkers"); diff --git a/modules/submap.js b/modules/submap.js index 6a9593ca..8804db9d 100644 --- a/modules/submap.js +++ b/modules/submap.js @@ -310,7 +310,7 @@ window.Submap = (function () { stage("Redraw emblems."); drawEmblems(); stage("Regenerating Zones."); - addZones(); + Zones.generate(); Names.getMapName(); stage("Restoring Notes."); notes = parentMap.notes; diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index ac270299..a88c698c 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -261,7 +261,7 @@ function editHeightmap(options) { Military.generate(); Markers.generate(); - addZones(); + Zones.generate(); TIME && console.timeEnd("regenerateErasedData"); INFO && console.groupEnd("Edit Heightmap"); } diff --git a/modules/ui/tools.js b/modules/ui/tools.js index 9c75ac20..7d2a0e1f 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -546,7 +546,7 @@ function regenerateZones(event) { function addNumberOfZones(number) { zones.selectAll("g").remove(); // remove existing zones - addZones(number); + Zones.generate(number); if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click(); if (!layerIsOn("toggleZones")) toggleZones(); } diff --git a/modules/zones-generator.js b/modules/zones-generator.js new file mode 100644 index 00000000..6454e895 --- /dev/null +++ b/modules/zones-generator.js @@ -0,0 +1,465 @@ +"use strict"; + +window.Zones = (function () { + const config = { + invasion: {modifier: 1.8, generate: addInvasion}, // invasion of enemy lands + rebels: {modifier: 1.6, generate: addRebels}, // rebels along a state border + proselytism: {modifier: 1.6, generate: addProselytism}, // proselitism of organized religion + crusade: {modifier: 1.6, generate: addCrusade}, // crusade on heresy lands + disease: {modifier: 1.8, generate: addDisease}, // disease starting in a random city + disaster: {modifier: 1.4, generate: addDisaster}, // disaster starting in a random city + eruption: {modifier: 1.4, generate: addEruption}, // volcanic eruption aroung volcano + avalanche: {modifier: 1.0, generate: addAvalanche}, // avalanche impacting highland road + fault: {modifier: 1.4, generate: addFault}, // fault line in elevated areas + flood: {modifier: 1.4, generate: addFlood}, // flood on river banks + tsunami: {modifier: 1.2, 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 count = rn(Math.random() * type.modifier * globalModifier); + for (let i = 0; i < count; i++) { + type.generate(usedCells); + } + }); + + drawZones(); + + function drawZones() { + zones + .selectAll("g") + .data(pack.zones) + .enter() + .append("g") + .attr("id", d => "zone" + d.i) + .attr("data-description", d => d.name) + .attr("data-type", d => d.type) + .attr("data-cells", d => d.cells.join(",")) + .attr("fill", d => d.color) + .selectAll("polygon") + .data(d => d.cells) + .enter() + .append("polygon") + .attr("points", d => getPackPolygon(d)) + .attr("id", function (d) { + return this.parentNode.id + "_" + d; + }); + } + + 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 invader = ra(atWar); + const target = invader.diplomacy.findIndex(d => d === "Enemy"); + + const cells = pack.cells; + const cell = ra( + cells.i.filter(i => cells.state[i] === target && cells.c[i].some(c => cells.state[c] === invader.i)) + ); + if (!cell) return; + + const cellsArray = [], + queue = [cell], + power = rand(5, 30); + + while (queue.length) { + const q = P(0.4) ? queue.shift() : queue.pop(); + cellsArray.push(q); + if (cellsArray.length > power) break; + + cells.c[q].forEach(e => { + if (usedCells[e]) return; + if (cells.state[e] !== target) return; + usedCells[e] = 1; + queue.push(e); + }); + } + + const invasion = rw({ + Invasion: 4, + Occupation: 3, + Raid: 2, + Conquest: 2, + Subjugation: 1, + Foray: 1, + Skirmishes: 1, + Incursion: 2, + Pillaging: 1, + Intervention: 1 + }); + const name = getAdjective(invader.name) + " " + invasion; + pack.zones.push({name, type: "Invasion", cells: cellsArray, 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 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); + + while (queue.length) { + const q = queue.shift(); + cellsArray.push(q); + if (cellsArray.length > power) 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); + }); + } + + const rebels = rw({ + Rebels: 5, + Insurgents: 2, + Mutineers: 1, + Rioters: 1, + Separatists: 1, + Secessionists: 1, + Insurrection: 2, + Rebellion: 1, + Conspiracy: 2 + }); + + const name = getAdjective(states[neib].name) + " " + rebels; + pack.zones.push({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 = 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) + ) + ); + if (!cell) return; + const target = cells.religion[cell]; + const cellsArray = [], + queue = [cell], + power = rand(10, 30); + + while (queue.length) { + const q = queue.shift(); + cellsArray.push(q); + if (cellsArray.length > power) 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); + }); + } + + const name = getAdjective(organized.name.split(" ")[0]) + " Proselytism"; + pack.zones.push({name, type: "Proselytism", cells: cellsArray, color: "url(#hatch6)"}); + } + + function addCrusade(usedCells) { + const heresy = ra(pack.religions.filter(r => r.type === "Heresy")); + if (!heresy) return; + + const cells = pack.cells; + const cellsArray = cells.i.filter(i => !usedCells[i] && cells.religion[i] === heresy.i); + if (!cellsArray.length) return; + cellsArray.forEach(i => (usedCells[i] = 1)); + + const name = getAdjective(heresy.name.split(" ")[0]) + " Crusade"; + pack.zones.push({name, type: "Crusade", cells: cellsArray, color: "url(#hatch6)"}); + } + + function addDisease(usedCells) { + const burg = ra(pack.burgs.filter(b => !usedCells[b.cell] && b.i && !b.removed)); // random burg + if (!burg) return; + + const cells = pack.cells; + const cellsArray = []; + const cost = []; + const power = rand(20, 37); + + const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); + queue.queue({e: burg.cell, p: 0}); + + while (queue.length) { + const next = queue.dequeue(); + 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 > power) return; + + if (!cost[nextCellId] || p < cost[nextCellId]) { + cost[nextCellId] = p; + queue.queue({e: nextCellId, p}); + } + }); + } + + const adjective = () => + ra(["Great", "Silent", "Severe", "Blind", "Unknown", "Loud", "Deadly", "Burning", "Bloody", "Brutal", "Fatal"]); + const animal = () => + ra([ + "Ape", + "Bear", + "Boar", + "Cat", + "Cow", + "Dog", + "Pig", + "Fox", + "Bird", + "Horse", + "Rat", + "Raven", + "Sheep", + "Spider", + "Wolf" + ]); + const color = () => + ra([ + "Golden", + "White", + "Black", + "Red", + "Pink", + "Purple", + "Blue", + "Green", + "Yellow", + "Amber", + "Orange", + "Brown", + "Grey" + ]); + + const type = rw({ + Fever: 5, + Pestilence: 2, + Flu: 2, + Pox: 2, + Smallpox: 2, + Plague: 4, + Cholera: 2, + Dropsy: 1, + Leprosy: 2 + }); + const name = rw({[color()]: 4, [animal()]: 2, [adjective()]: 1}) + " " + type; + pack.zones.push({name, type: "Disease", cells: cellsArray, color: "url(#hatch12)"}); + } + + function addDisaster(usedCells) { + const burg = ra(pack.burgs.filter(b => !usedCells[b.cell] && b.i && !b.removed)); // random burg + if (!burg) return; + + const cells = pack.cells; + const cellsArray = [], + cost = [], + power = rand(5, 25); + const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); + queue.queue({e: burg.cell, p: 0}); + + while (queue.length) { + const next = queue.dequeue(); + 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 > power) return; + + if (!cost[e] || p < cost[e]) { + cost[e] = p; + queue.queue({e, p}); + } + }); + } + + const type = rw({Famine: 5, Dearth: 1, Drought: 3, Earthquake: 3, Tornadoes: 1, Wildfires: 1}); + const name = getAdjective(burg.name) + " " + type; + pack.zones.push({name, type: "Disaster", cells: cellsArray, color: "url(#hatch5)"}); + } + + function addEruption(usedCells) { + const volcano = byId("markers").querySelector("use[data-id='#marker_volcano']"); + if (!volcano) return; + + const cells = pack.cells; + const x = +volcano.dataset.x, + y = +volcano.dataset.y, + cell = findCell(x, y); + const id = volcano.id; + const note = notes.filter(n => n.id === id); + + if (note[0]) note[0].legend = note[0].legend.replace("Active volcano", "Erupting volcano"); + const name = note[0] ? note[0].name.replace(" Volcano", "") + " Eruption" : "Volcano Eruption"; + + const cellsArray = [], + queue = [cell], + power = rand(10, 30); + + while (queue.length) { + const q = P(0.5) ? queue.shift() : queue.pop(); + cellsArray.push(q); + if (cellsArray.length > power) break; + cells.c[q].forEach(e => { + if (usedCells[e] || cells.h[e] < 20) return; + usedCells[e] = 1; + queue.push(e); + }); + } + + pack.zones.push({name, type: "Disaster", cells: cellsArray, color: "url(#hatch7)"}); + } + + function addAvalanche(usedCells) { + const cells = pack.cells; + const routes = cells.i.filter(i => !usedCells[i] && Routes.isConnected(i) && cells.h[i] >= 70); + if (!routes.length) return; + + const cell = +ra(routes); + const cellsArray = [], + queue = [cell], + power = rand(3, 15); + + while (queue.length) { + const q = P(0.3) ? queue.shift() : queue.pop(); + cellsArray.push(q); + if (cellsArray.length > power) break; + cells.c[q].forEach(e => { + if (usedCells[e] || cells.h[e] < 65) return; + usedCells[e] = 1; + queue.push(e); + }); + } + + const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); + const name = proper + " Avalanche"; + pack.zones.push({name, type: "Disaster", cells: cellsArray, color: "url(#hatch5)"}); + } + + function addFault(usedCells) { + const cells = pack.cells; + const elevated = cells.i.filter(i => !usedCells[i] && cells.h[i] > 50 && cells.h[i] < 70); + if (!elevated.length) return; + + const cell = ra(elevated); + const cellsArray = [], + queue = [cell], + power = rand(3, 15); + + while (queue.length) { + const q = queue.pop(); + if (cells.h[q] >= 20) cellsArray.push(q); + if (cellsArray.length > power) break; + cells.c[q].forEach(e => { + if (usedCells[e] || cells.r[e]) return; + usedCells[e] = 1; + queue.push(e); + }); + } + + const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); + const name = proper + " Fault"; + pack.zones.push({name, type: "Disaster", cells: cellsArray, color: "url(#hatch2)"}); + } + + function addFlood(usedCells) { + const cells = pack.cells; + const fl = cells.fl.filter(fl => fl), + meanFlux = d3.mean(fl), + maxFlux = d3.max(fl), + flux = (maxFlux - meanFlux) / 2 + meanFlux; + const rivers = cells.i.filter( + i => !usedCells[i] && cells.h[i] < 50 && cells.r[i] && cells.fl[i] > flux && cells.burg[i] + ); + if (!rivers.length) return; + + const cell = +ra(rivers), + river = cells.r[cell]; + const cellsArray = [], + queue = [cell], + power = rand(5, 30); + + while (queue.length) { + const q = queue.pop(); + cellsArray.push(q); + if (cellsArray.length > power) break; + + cells.c[q].forEach(e => { + if (usedCells[e] || cells.h[e] < 20 || cells.r[e] !== river || cells.h[e] > 50 || cells.fl[e] < meanFlux) + return; + usedCells[e] = 1; + queue.push(e); + }); + } + + const name = getAdjective(burgs[cells.burg[cell]].name) + " Flood"; + pack.zones.push({name, type: "Disaster", cells: cellsArray, color: "url(#hatch13)"}); + } + + function addTsunami(usedCells) { + const cells = pack.cells; + const coastal = cells.i.filter( + i => !usedCells[i] && cells.t[i] === -1 && pack.features[cells.f[i]].type !== "lake" + ); + if (!coastal.length) return; + + const cell = +ra(coastal); + const cellsArray = [], + queue = [cell], + power = rand(10, 30); + + while (queue.length) { + const q = queue.shift(); + if (cells.t[q] === 1) cellsArray.push(q); + if (cellsArray.length > power) break; + + cells.c[q].forEach(e => { + if (usedCells[e]) return; + if (cells.t[e] > 2) return; + if (pack.features[cells.f[e]].type === "lake") return; + usedCells[e] = 1; + queue.push(e); + }); + } + + const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); + const name = proper + " Tsunami"; + pack.zones.push({name, type: "Disaster", cells: cellsArray, color: "url(#hatch13)"}); + } + + return {generate}; +})(); diff --git a/versioning.js b/versioning.js index b0b15315..91aa8060 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.99.10"; // generator version, update each time +const version = "1.100.00"; // generator version, update each time { document.title += " v" + version;