diff --git a/modules/markers-generator.js b/modules/markers-generator.js index 50acf34d2..5e3ea4655 100644 --- a/modules/markers-generator.js +++ b/modules/markers-generator.js @@ -9,27 +9,27 @@ window.Markers = (function () { const isFantasy = culturesSet.includes("Fantasy"); return [ - {type: "volcanoes", icon: "🌋", multiplier: 1, fn: addVolcanoes}, - {type: "hot-springs", icon: "♨️", multiplier: 1, fn: addHotSprings}, - {type: "mines", icon: "⛏️", multiplier: 1, fn: addMines}, - {type: "bridges", icon: "🌉", multiplier: 1, fn: addBridges}, - {type: "inns", icon: "🍻", multiplier: 1, fn: addInns}, - {type: "lighthouses", icon: "🚨", multiplier: 1, fn: addLighthouses}, - {type: "waterfalls", icon: "⟱", multiplier: 1, fn: addWaterfalls}, - {type: "battlefields", icon: "⚔️", multiplier: 1, fn: addBattlefields}, - {type: "dungeons", icon: "🗝️", multiplier: 1, fn: addDungeons}, - {type: "lake-monsters", icon: "🐉", multiplier: 1, fn: addLakeMonsters}, - {type: "sea-monsters", icon: "🦑", multiplier: 1, fn: addSeaMonsters}, - {type: "hill-monsters", icon: "👹", multiplier: 1, fn: addHillMonsters}, - {type: "sacred-mountains", icon: "🗻", multiplier: 1, fn: addSacredMountains}, - {type: "sacred-forests", icon: "🌳", multiplier: 1, fn: addSacredForests}, - {type: "sacred-pineries", icon: "🌲", multiplier: 1, fn: addSacredPineries}, - {type: "sacred-palm-groves", icon: "🌴", multiplier: 1, fn: addSacredPalmGroves}, - {type: "brigands", icon: "💰", multiplier: 1, fn: addBrigands}, - {type: "pirates", icon: "🏴☠️", multiplier: 1, fn: addPirates}, - {type: "statues", icon: "🗿", multiplier: 1, fn: addStatues}, - {type: "ruines", icon: "🏺", multiplier: 1, fn: addRuines}, - {type: "portals", icon: "🌀", multiplier: +isFantasy, fn: addPortals} + {type: "volcanoes", icon: "🌋", dx: 52, px: 13, min: 10, each: 500, multiplier: 1, fn: [listVolcanoes, addVolcano]}, + {type: "hot-springs", icon: "♨️", dy: 52, min: 30, each: 1200, multiplier: 1, fn: [listHotSprings, addHotSpring]}, + {type: "mines", icon: "⛏️", dx: 48, px: 13, min: 1, each: 15, multiplier: 1, fn: [listMines, addMine]}, + {type: "bridges", icon: "🌉", px: 14, min: 1, each: 5, multiplier: 1, fn: [listBridges, addBridge]}, + {type: "inns", icon: "🍻", px: 14, min: 1, each: 100, multiplier: 1, fn: [listInns, addInn]}, + {type: "lighthouses", icon: "🚨", px: 14, min: 1, each: 2, multiplier: 1, fn: [listLighthouses, addLighthouse]}, + {type: "waterfalls", icon: "⟱", dy: 54, px: 16, min: 1, each: 5, multiplier: 1, fn: [listWaterfalls, addWaterfall]}, + {type: "battlefields", icon: "⚔️", dy: 52, min: 50, each: 700, multiplier: 1, fn: [listBattlefields, addBattlefield]}, + {type: "dungeons", icon: "🗝️", dy: 51, px: 13, min: 30, each: 200, multiplier: 1, fn: [listDungeons, addDungeon]}, + {type: "lake-monsters", icon: "🐉", dy: 48, min: 2, each: 10, multiplier: 1, fn: [listLakeMonsters, addLakeMonster]}, + {type: "sea-monsters", icon: "🦑", min: 50, each: 700, multiplier: 1, fn: [listSeaMonsters, addSeaMonster]}, + {type: "hill-monsters", icon: "👹", dy: 54, px: 13, min: 30, each: 600, multiplier: 1, fn: [listHillMonsters, addHillMonster]}, + {type: "sacred-mountains", icon: "🗻", dy: 48, min: 1, each: 5, multiplier: 1, fn: [listSacredMountains, addSacredMountain]}, + {type: "sacred-forests", icon: "🌳", min: 30, each: 1000, multiplier: 1, fn: [listSacredForests, addSacredForest]}, + {type: "sacred-pineries", icon: "🌲", px: 13, min: 30, each: 800, multiplier: 1, fn: [listSacredPineries, addSacredPinery]}, + {type: "sacred-palm-groves", icon: "🌴", px: 13, min: 1, each: 100, multiplier: 1, fn: [listSacredPalmGroves, addSacredPalmGrove]}, + {type: "brigands", icon: "💰", px: 13, min: 50, each: 100, multiplier: 1, fn: [listBrigands, addBrigands]}, + {type: "pirates", icon: "🏴☠️", dx: 51, min: 40, each: 300, multiplier: 1, fn: [listPirates, addPirates]}, + {type: "statues", icon: "🗿", min: 80, each: 1200, multiplier: 1, fn: [listStatues, addStatue]}, + {type: "ruines", icon: "🏺", min: 80, each: 1200, multiplier: 1, fn: [listRuins, addRuins]}, + {type: "portals", icon: "🌀", px: 14, min: 16, each: 8, multiplier: +isFantasy, fn: [listPortals, addPortal]} ]; } @@ -61,12 +61,36 @@ window.Markers = (function () { generateTypes(); }; + const add = marker => { + const base = config.find(c => c.type === marker.type); + if (base) { + const {icon, type, dx, dy, px} = base; + marker = addMarker({icon, type, dx, dy, px}, marker); + base.fn[1]("marker" + marker.i, marker.cell); + return marker; + } + + const i = last(pack.markers)?.i + 1 || 0; + pack.markers.push({...marker, i}); + occupied[marker.cell] = true; + return {...marker, i}; + }; + function generateTypes() { TIME && console.time("addMarkers"); - config.forEach(({type, icon, multiplier, fn}) => { + config.forEach(({type, icon, dx, dy, px, min, each, multiplier, fn}) => { if (multiplier === 0) return; - fn(type, icon, multiplier); + + let candidates = Array.from(fn[0](pack)); + let quantity = getQuantity(candidates, min, each, multiplier); + + while (quantity && candidates.length) { + const [cell] = extractAnyElement(candidates); + const marker = addMarker({icon, type, dx, dy, px}, {cell}); + fn[1]("marker" + marker.i, cell); + quantity--; + } }); occupied = []; @@ -96,112 +120,84 @@ window.Markers = (function () { return cells.p[cell]; } - function addMarker({cell, type, icon, dx, dy, px}) { + function addMarker(base, marker) { const i = last(pack.markers)?.i + 1 || 0; - const [x, y] = getMarkerCoordinates(cell); - const marker = {i, icon, type, x, y, cell}; - if (dx) marker.dx = dx; - if (dy) marker.dy = dy; - if (px) marker.px = px; + const [x, y] = getMarkerCoordinates(marker.cell); + marker = {...base, x, y, ...marker, i}; pack.markers.push(marker); - occupied[cell] = true; - return "marker" + i; + occupied[marker.cell] = true; + return marker; } - function addVolcanoes(type, icon, multiplier) { + function listVolcanoes({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] >= 70); + } + + function addVolcano(id, cell) { const {cells} = pack; - let mountains = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 70).sort((a, b) => cells.h[b] - cells.h[a])); - let quantity = getQuantity(mountains, 10, 500, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(mountains); - const id = addMarker({cell, icon, type, dx: 52, px: 13}); - const proper = Names.getCulture(cells.culture[cell]); - const name = P(0.3) ? "Mount " + proper : Math.random() > 0.3 ? proper + " Volcano" : proper; - notes.push({id, name, legend: `Active volcano. Height: ${getFriendlyHeight(cells.p[cell])}`}); - quantity--; - } + const proper = Names.getCulture(cells.culture[cell]); + const name = P(0.3) ? "Mount " + proper : Math.random() > 0.3 ? proper + " Volcano" : proper; + notes.push({id, name, legend: `Active volcano. Height: ${getFriendlyHeight(cells.p[cell])}`}); } - function addHotSprings(type, icon, multiplier) { + function listHotSprings({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] > 50); + } + + function addHotSpring(id, cell) { const {cells} = pack; - let springs = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] > 50).sort((a, b) => cells.h[b] - cells.h[a])); - let quantity = getQuantity(springs, 30, 1200, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(springs); - const id = addMarker({cell, icon, type, dy: 52}); - const proper = Names.getCulture(cells.culture[cell]); - const temp = convertTemperature(gauss(35, 15, 20, 100)); - notes.push({id, name: proper + " Hot Springs", legend: `A hot springs area. Average temperature: ${temp}`}); - quantity--; - } + const proper = Names.getCulture(cells.culture[cell]); + const temp = convertTemperature(gauss(35, 15, 20, 100)); + notes.push({id, name: proper + " Hot Springs", legend: `A hot springs area. Average temperature: ${temp}`}); } - function addMines(type, icon, multiplier) { - const {cells} = pack; + function listMines({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] > 47 && cells.burg[i]); + } - let hillyBurgs = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] > 47 && cells.burg[i])); - let quantity = getQuantity(hillyBurgs, 1, 15, multiplier); - if (!quantity) return; + function addMine(id, cell) { + const {cells} = pack; const resources = {salt: 5, gold: 2, silver: 4, copper: 2, iron: 3, lead: 1, tin: 1}; - - while (quantity && hillyBurgs.length) { - const [cell] = extractAnyElement(hillyBurgs); - const id = addMarker({cell, icon, type, dx: 48, px: 13}); - const resource = rw(resources); - const burg = pack.burgs[cells.burg[cell]]; - const name = `${burg.name} — ${resource} mining town`; - const population = rn(burg.population * populationRate * urbanization); - const legend = `${burg.name} is a mining town of ${population} people just nearby the ${resource} mine`; - notes.push({id, name, legend}); - quantity--; - } + const resource = rw(resources); + const burg = pack.burgs[cells.burg[cell]]; + const name = `${burg.name} — ${resource} mining town`; + const population = rn(burg.population * populationRate * urbanization); + const legend = `${burg.name} is a mining town of ${population} people just nearby the ${resource} mine`; + notes.push({id, name, legend}); } - function addBridges(type, icon, multiplier) { - const {cells, burgs} = pack; - + function listBridges({cells, burgs}) { const meanFlux = d3.mean(cells.fl.filter(fl => fl)); - let bridges = Array.from( - cells.i.filter(i => !occupied[i] && cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux) - ); - let quantity = getQuantity(bridges, 1, 5, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(bridges); - const id = addMarker({cell, icon, type, px: 14}); - const burg = pack.burgs[cells.burg[cell]]; - const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); - const riverName = river ? `${river.name} ${river.type}` : "river"; - const name = river && P(0.2) ? river.name : burg.name; - const weightedAdjectives = { - stone: 10, - wooden: 1, - lengthy: 2, - formidable: 2, - rickety: 1, - beaten: 1, - weathered: 1 - }; - notes.push({id, name: `${name} Bridge`, legend: `A ${rw(weightedAdjectives)} bridge spans over the ${riverName} near ${burg.name}`}); - quantity--; - } + return cells.i.filter(i => !occupied[i] && cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux); } - function addInns(type, icon, multiplier) { + function addBridge(id, cell) { const {cells} = pack; - let taverns = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.road[i] > 4 && cells.pop[i] > 10)); - let quantity = getQuantity(taverns, 1, 100, multiplier); - if (!quantity) return; + const burg = pack.burgs[cells.burg[cell]]; + const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); + const riverName = river ? `${river.name} ${river.type}` : "river"; + const name = river && P(0.2) ? river.name : burg.name; + const weightedAdjectives = { + stone: 10, + wooden: 1, + lengthy: 2, + formidable: 2, + rickety: 1, + beaten: 1, + weathered: 1 + }; + notes.push({id, name: `${name} Bridge`, legend: `A ${rw(weightedAdjectives)} bridge spans over the ${riverName} near ${burg.name}`}); + } + + function listInns({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.road[i] > 4 && cells.pop[i] > 10); + } + function addInn(id, cell) { const colors = [ "Dark", "Light", @@ -430,45 +426,35 @@ window.Markers = (function () { "sap" ]; - while (quantity) { - const [cell] = extractAnyElement(taverns); - const id = addMarker({cell, icon, type, px: 14}); - const typeName = P(0.3) ? "inn" : "tavern"; - const isAnimalThemed = P(0.7); - const animal = ra(animals); - const name = isAnimalThemed ? (P(0.6) ? ra(colors) + " " + animal : ra(adjectives) + " " + animal) : ra(adjectives) + " " + capitalize(type); - const meal = isAnimalThemed && P(0.3) ? animal : ra(courses); - const course = `${ra(methods)} ${meal}`.toLowerCase(); - const drink = `${P(0.5) ? ra(types) : ra(colors)} ${ra(drinks)}`.toLowerCase(); - const legend = `A big and famous roadside ${typeName}. Delicious ${course} with ${drink} is served here`; - notes.push({id, name: "The " + name, legend}); - quantity--; - } + const typeName = P(0.3) ? "inn" : "tavern"; + const isAnimalThemed = P(0.7); + const animal = ra(animals); + const name = isAnimalThemed ? (P(0.6) ? ra(colors) + " " + animal : ra(adjectives) + " " + animal) : ra(adjectives) + " " + capitalize(typeName); + const meal = isAnimalThemed && P(0.3) ? animal : ra(courses); + const course = `${ra(methods)} ${meal}`.toLowerCase(); + const drink = `${P(0.5) ? ra(types) : ra(colors)} ${ra(drinks)}`.toLowerCase(); + const legend = `A big and famous roadside ${typeName}. Delicious ${course} with ${drink} is served here`; + notes.push({id, name: "The " + name, legend}); + } + + function listLighthouses({cells}) { + return cells.i.filter(i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.road[c])); } - function addLighthouses(type, icon, multiplier) { + function addLighthouse(id, cell) { const {cells} = pack; - const lighthouses = Array.from(cells.i.filter(i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.road[c]))); - let quantity = getQuantity(lighthouses, 1, 2, multiplier); - if (!quantity) return; + const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); + notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to serve as a beacon for ships in the open sea`}); + } - while (quantity) { - const [cell] = extractAnyElement(lighthouses); - const id = addMarker({cell, icon, type, px: 14}); - const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); - notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to serve as a beacon for ships in the open sea`}); - quantity--; - } + function listWaterfalls({cells}) { + return cells.i.filter(i => cells.r[i] && !occupied[i] && cells.h[i] >= 50 && cells.c[i].some(c => cells.h[c] < 40 && cells.r[c])); } - function addWaterfalls(type, icon, multiplier) { + function addWaterfall(id, cell) { const {cells} = pack; - const waterfalls = Array.from(cells.i.filter(i => cells.r[i] && !occupied[i] && cells.h[i] >= 50 && cells.c[i].some(c => cells.h[c] < 40 && cells.r[c]))); - const quantity = getQuantity(waterfalls, 1, 5, multiplier); - if (!quantity) return; - const descriptions = [ "A gorgeous waterfall flows here", "The rapids of an exceptionally beautiful waterfall", @@ -477,98 +463,73 @@ window.Markers = (function () { "A river drops down from a great height forming a wonderous waterfall", "A breathtaking waterfall cuts through the landscape" ]; - for (let i = 0; i < waterfalls.length && i < quantity; i++) { - const cell = waterfalls[i]; - const id = addMarker({cell, icon, type, dy: 54, px: 16}); - const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); - notes.push({id, name: getAdjective(proper) + " Waterfall" + name, legend: `${ra(descriptions)}`}); - } + + const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); + notes.push({id, name: getAdjective(proper) + " Waterfall" + name, legend: `${ra(descriptions)}`}); + } + + function listBattlefields({cells}) { + return cells.i.filter(i => !occupied[i] && cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25); } - function addBattlefields(type, icon, multiplier) { + function addBattlefield(id, cell) { const {cells, states} = pack; - let battlefields = Array.from(cells.i.filter(i => !occupied[i] && cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25)); - let quantity = getQuantity(battlefields, 50, 700, multiplier); - if (!quantity) return; - - while (quantity && battlefields.length) { - const [cell] = extractAnyElement(battlefields); - const id = addMarker({cell, icon, type, dy: 52}); - const state = states[cells.state[cell]]; - if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state); - const campaign = ra(state.campaigns); - const date = generateDate(campaign.start, campaign.end); - const name = Names.getCulture(cells.culture[cell]) + " Battlefield"; - const legend = `A historical battle of the ${campaign.name}. \r\nDate: ${date} ${options.era}`; - notes.push({id, name, legend}); - quantity--; - } + const state = states[cells.state[cell]]; + if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state); + const campaign = ra(state.campaigns); + const date = generateDate(campaign.start, campaign.end); + const name = Names.getCulture(cells.culture[cell]) + " Battlefield"; + const legend = `A historical battle of the ${campaign.name}. \r\nDate: ${date} ${options.era}`; + notes.push({id, name, legend}); } - function addDungeons(type, icon, multiplier) { - const {cells} = pack; - - let dungeons = Array.from(cells.i.filter(i => !occupied[i] && cells.pop[i] && cells.pop[i] < 3)); - let quantity = getQuantity(dungeons, 30, 200, multiplier); - if (!quantity) return; + function listDungeons({cells}) { + return cells.i.filter(i => !occupied[i] && cells.pop[i] && cells.pop[i] < 3); + } - while (quantity) { - const [cell] = extractAnyElement(dungeons); - const id = addMarker({cell, icon, type, dy: 51, px: 13}); + function addDungeon(id, cell) { + const dungeonSeed = `${seed}${cell}`; + const name = "Dungeon"; + const legend = `