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 = `
Undiscovered dungeon. See One page dungeon
`; + notes.push({id, name, legend}); + } - const dungeonSeed = `${seed}${cell}`; - const name = "Dungeon"; - const legend = `
Undiscovered dungeon. See One page dungeon
`; - notes.push({id, name, legend}); - quantity--; - } + function listLakeMonsters({features}) { + return features.filter(feature => feature.type === "lake" && feature.group === "freshwater" && !occupied[feature.firstCell]).map(feature => feature.firstCell); } - function addLakeMonsters(type, icon, multiplier) { - const {features} = pack; + function addLakeMonster(id, cell) { + const lake = pack.features[pack.cells.f[cell]]; - const lakes = features.filter(feature => feature.type === "lake" && feature.group === "freshwater" && !occupied[feature.firstCell]); - let quantity = getQuantity(lakes, 2, 10, multiplier); - if (!quantity) return; + // Check that the feature is a lake in case the user clicked on a wrong + // square + if (lake.type !== "lake") return; - while (quantity) { - const [lake] = extractAnyElement(lakes); - const cell = lake.firstCell; - const id = addMarker({cell, icon, type, dy: 48}); - const name = `${lake.name} Monster`; - const length = gauss(10, 5, 5, 100); - const legend = `Rumors say a relic monster of ${length} ${heightUnit.value} long inhabits ${lake.name} Lake. Truth or lie, folks are afraid to fish in the lake`; - notes.push({id, name, legend}); - quantity--; - } + const name = `${lake.name} Monster`; + const length = gauss(10, 5, 5, 100); + const legend = `Rumors say a relic monster of ${length} ${heightUnit.value} long inhabits ${lake.name} Lake. Truth or lie, folks are afraid to fish in the lake`; + notes.push({id, name, legend}); } - function addSeaMonsters(type, icon, multiplier) { - const {cells, features} = pack; + function listSeaMonsters({cells, features}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i] && features[cells.f[i]].type === "ocean"); + } - const sea = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i] && features[cells.f[i]].type === "ocean")); - let quantity = getQuantity(sea, 50, 700, multiplier); - if (!quantity) return; + function addSeaMonster(id, cell) { + const name = `${Names.getCultureShort(0)} Monster`; + const length = gauss(25, 10, 10, 100); + const legend = `Old sailors tell stories of a gigantic sea monster inhabiting these dangerous waters. Rumors say it can be ${length} ${heightUnit.value} long`; + notes.push({id, name, legend}); + } - while (quantity) { - const [cell] = extractAnyElement(sea); - const id = addMarker({cell, icon, type}); - const name = `${Names.getCultureShort(0)} Monster`; - const length = gauss(25, 10, 10, 100); - const legend = `Old sailors tell stories of a gigantic sea monster inhabiting these dangerous waters. Rumors say it can be ${length} ${heightUnit.value} long`; - notes.push({id, name, legend}); - quantity--; - } + function listHillMonsters({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] >= 50 && cells.pop[i]); } - function addHillMonsters(type, icon, multiplier) { + function addHillMonster(id, cell) { const {cells} = pack; - const hills = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 50 && cells.pop[i])); - let quantity = getQuantity(hills, 30, 600, multiplier); - if (!quantity) return; - const adjectives = [ "great", "big", @@ -619,99 +580,76 @@ window.Markers = (function () { "attacks unsuspecting victims" ]; - while (quantity) { - const [cell] = extractAnyElement(hills); - const id = addMarker({cell, icon, type, dy: 54, px: 13}); - const monster = ra(species); - const toponym = Names.getCulture(cells.culture[cell]); - const name = `${toponym} ${monster}`; - const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`; - notes.push({id, name, legend}); - quantity--; - } + const monster = ra(species); + const toponym = Names.getCulture(cells.culture[cell]); + const name = `${toponym} ${monster}`; + const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`; + notes.push({id, name, legend}); } - function addSacredMountains(type, icon, multiplier) { + // Sacred mountains spawn on lonely mountains + function listSacredMountains({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] >= 70 && cells.c[i].some(c => cells.culture[c]) && cells.c[i].every(c => cells.h[c] < 60)); + } + + function addSacredMountain(id, cell) { const {cells, cultures} = pack; - let lonelyMountains = Array.from( - cells.i.filter(i => !occupied[i] && cells.h[i] >= 70 && cells.c[i].some(c => cells.culture[c]) && cells.c[i].every(c => cells.h[c] < 60)) - ); - let quantity = getQuantity(lonelyMountains, 1, 5, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(lonelyMountains); - const id = addMarker({cell, icon, type, dy: 48}); - const culture = cells.c[cell].map(c => cells.culture[c]).find(c => c); - const name = `${Names.getCulture(culture)} Mountain`; - const height = getFriendlyHeight(cells.p[cell]); - const legend = `A sacred mountain of ${cultures[culture].name} culture. Height: ${height}`; - notes.push({id, name, legend}); - quantity--; - } + const culture = cells.c[cell].map(c => cells.culture[c]).find(c => c); + const name = `${Names.getCulture(culture)} Mountain`; + const height = getFriendlyHeight(cells.p[cell]); + const legend = `A sacred mountain of ${cultures[culture].name} culture. Height: ${height}`; + notes.push({id, name, legend}); } - function addSacredForests(type, icon, multiplier) { + // Sacred forests spawn on temperate forests + function listSacredForests({cells}) { + return cells.i.filter(i => !occupied[i] && cells.culture[i] && [6, 8].includes(cells.biome[i])); + } + + function addSacredForest(id, cell) { const {cells, cultures} = pack; - let temperateForests = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && [6, 8].includes(cells.biome[i]))); - let quantity = getQuantity(temperateForests, 30, 1000, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(temperateForests); - const id = addMarker({cell, icon, type}); - const culture = cells.culture[cell]; - const name = `${Names.getCulture(culture)} Forest`; - const legend = `A sacred forest of ${cultures[culture].name} culture`; - notes.push({id, name, legend}); - quantity--; - } + const culture = cells.culture[cell]; + const name = `${Names.getCulture(culture)} Forest`; + const legend = `A sacred forest of ${cultures[culture].name} culture`; + notes.push({id, name, legend}); + } + + // Sacred pineries spawn on boreal forests + function listSacredPineries({cells}) { + return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 9); } - function addSacredPineries(type, icon, multiplier) { + function addSacredPinery(id, cell) { const {cells, cultures} = pack; - let borealForests = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 9)); - let quantity = getQuantity(borealForests, 30, 800, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(borealForests); - const id = addMarker({cell, icon, type, px: 13}); - const culture = cells.culture[cell]; - const name = `${Names.getCulture(culture)} Pinery`; - const legend = `A sacred pinery of ${cultures[culture].name} culture`; - notes.push({id, name, legend}); - quantity--; - } + const culture = cells.culture[cell]; + const name = `${Names.getCulture(culture)} Pinery`; + const legend = `A sacred pinery of ${cultures[culture].name} culture`; + notes.push({id, name, legend}); + } + + // Sacred palm groves spawn on oasises + function listSacredPalmGroves({cells}) { + return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 1 && cells.pop[i] > 1 && cells.road[i]); } - function addSacredPalmGroves(type, icon, multiplier) { + function addSacredPalmGrove(id, cell) { const {cells, cultures} = pack; - let oasises = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 1 && cells.pop[i] > 1 && cells.road[i])); - let quantity = getQuantity(oasises, 1, 100, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(oasises); - const id = addMarker({cell, icon, type, px: 13}); - const culture = cells.culture[cell]; - const name = `${Names.getCulture(culture)} Palm Grove`; - const legend = `A sacred palm grove of ${cultures[culture].name} culture`; - notes.push({id, name, legend}); - quantity--; - } + const culture = cells.culture[cell]; + const name = `${Names.getCulture(culture)} Palm Grove`; + const legend = `A sacred palm grove of ${cultures[culture].name} culture`; + notes.push({id, name, legend}); } - function addBrigands(type, icon, multiplier) { - const {cells} = pack; + function listBrigands({cells}) { + return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.road[i] > 4); + } - let roads = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.road[i] > 4)); - let quantity = getQuantity(roads, 50, 100, multiplier); - if (!quantity) return; + function addBrigands(id, cell) { + const {cells} = pack; const animals = [ "Apes", @@ -747,53 +685,43 @@ window.Markers = (function () { ]; const types = {brigands: 4, bandits: 3, robbers: 1, highwaymen: 1}; - while (quantity) { - const [cell] = extractAnyElement(roads); - const id = addMarker({cell, icon, type, px: 13}); - const culture = cells.culture[cell]; - const biome = cells.biome[cell]; - const height = cells.p[cell]; - const locality = - height >= 70 - ? "highlander" - : [1, 2].includes(biome) - ? "desert" - : [3, 4].includes(biome) - ? "mounted" - : [5, 6, 7, 8, 9].includes(biome) - ? "forest" - : biome === 12 - ? "swamp" - : "angry"; - const name = `${Names.getCulture(culture)} ${ra(animals)}`; - const legend = `A gang of ${locality} ${rw(types)}`; - notes.push({id, name, legend}); - quantity--; - } + const culture = cells.culture[cell]; + const biome = cells.biome[cell]; + const height = cells.p[cell]; + const locality = + height >= 70 + ? "highlander" + : [1, 2].includes(biome) + ? "desert" + : [3, 4].includes(biome) + ? "mounted" + : [5, 6, 7, 8, 9].includes(biome) + ? "forest" + : biome === 12 + ? "swamp" + : "angry"; + const name = `${Names.getCulture(culture)} ${ra(animals)}`; + const legend = `A gang of ${locality} ${rw(types)}`; + notes.push({id, name, legend}); } - function addPirates(type, icon, multiplier) { - const {cells} = pack; + // Pirates spawn on sea routes + function listPirates({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i]); + } - let searoutes = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i])); - let quantity = getQuantity(searoutes, 40, 300, multiplier); - if (!quantity) return; - - while (quantity) { - const [cell] = extractAnyElement(searoutes); - const id = addMarker({cell, icon, type, dx: 51}); - const name = `Pirates`; - const legend = `Pirate ships have been spotted in these waters`; - notes.push({id, name, legend}); - quantity--; - } + function addPirates(id, cell) { + const name = `Pirates`; + const legend = `Pirate ships have been spotted in these waters`; + notes.push({id, name, legend}); + } + + function listStatues({cells}) { + return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.h[i] < 40); } - function addStatues(type, icon, multiplier) { + function addStatue(id, cell) { const {cells} = pack; - let statues = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.h[i] < 40)); - let quantity = getQuantity(statues, 80, 1200, multiplier); - if (!quantity) return; const variants = ["Statue", "Obelisk", "Monument", "Column", "Monolith", "Pillar", "Megalith", "Stele", "Runestone", "Sculpture", "Effigy", "Idol"]; const scripts = { @@ -804,31 +732,25 @@ window.Markers = (function () { mongolian: "᠀᠐᠑᠒ᠠᠡᠦᠧᠨᠩᠪᠭᠮᠯᠰᠱᠲᠳᠵᠻᠼᠽᠾᠿᡀᡁᡆᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡙᡜᡝᡞᡟᡠᡡᡭᡮᡯᡰᡱᡲᡳᡴᢀᢁᢂᢋᢏᢐᢑᢒᢓᢛᢜᢞᢟᢠᢡᢢᢤᢥᢦ" }; - while (quantity) { - const [cell] = extractAnyElement(statues); - const id = addMarker({cell, icon, type}); - const culture = cells.culture[cell]; - - const variant = ra(variants); - const name = `${Names.getCulture(culture)} ${variant}`; - const script = scripts[ra(Object.keys(scripts))]; - const inscription = Array(rand(40, 100)) - .fill(null) - .map(() => ra(script)) - .join(""); - const legend = `An ancient ${variant.toLowerCase()}. It has an inscription, but no one can translate it: + const culture = cells.culture[cell]; + + const variant = ra(variants); + const name = `${Names.getCulture(culture)} ${variant}`; + const script = scripts[ra(Object.keys(scripts))]; + const inscription = Array(rand(40, 100)) + .fill(null) + .map(() => ra(script)) + .join(""); + const legend = `An ancient ${variant.toLowerCase()}. It has an inscription, but no one can translate it:
${inscription}
`; - notes.push({id, name, legend}); - quantity--; - } + notes.push({id, name, legend}); } - function addRuines(type, icon, multiplier) { - const {cells} = pack; - let ruins = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && cells.h[i] < 60)); - let quantity = getQuantity(ruins, 80, 1200, multiplier); - if (!quantity) return; + function listRuins({cells}) { + return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && cells.h[i] < 60); + } + function addRuins(id, cell) { const types = [ "City", "Town", @@ -845,37 +767,30 @@ window.Markers = (function () { "Castle" ]; - while (quantity) { - const [cell] = extractAnyElement(ruins); - const id = addMarker({cell, icon, type}); - - const ruinType = ra(types); - const name = `Ruined ${ruinType}`; - const legend = `Ruins of an ancient ${ruinType.toLowerCase()}. Untold riches may lie within.`; - notes.push({id, name, legend}); - quantity--; - } + const ruinType = ra(types); + const name = `Ruined ${ruinType}`; + const legend = `Ruins of an ancient ${ruinType.toLowerCase()}. Untold riches may lie within.`; + notes.push({id, name, legend}); } - function addPortals(type, icon, multiplier) { - const {burgs} = pack; - let portals = burgs + function listPortals({burgs}) { + return burgs .slice(1, Math.ceil(burgs.length / 10) + 1) .filter(({cell}) => !occupied[cell]) - .map(burg => [burg.name, burg.cell]); - let quantity = getQuantity(portals, 16, 8, multiplier); - if (!quantity) return; - - while (quantity) { - const [portal] = extractAnyElement(portals); - const [burgName, cell] = portal; - const id = addMarker({cell, icon, type, px: 14}); - const name = `${burgName} Portal`; - const legend = `An element of the magic portal system connecting major cities. Portals installed centuries ago, but still work fine`; - notes.push({id, name, legend}); - quantity--; - } + .map(burg => burg.cell); + } + + function addPortal(id, cell) { + const {cells, burgs} = pack; + + // Portals can only be added to burgs + if (cells.burg[cell]) return; + const burgName = burgs[cells.burg[cell]].name; + + const name = `${burgName} Portal`; + const legend = `An element of the magic portal system connecting major cities. Portals installed centuries ago, but still work fine`; + notes.push({id, name, legend}); } - return {generate, regenerate, getConfig, setConfig}; + return {add, generate, regenerate, getConfig, setConfig}; })(); diff --git a/modules/ui/tools.js b/modules/ui/tools.js index aeed31ff2..aec92311a 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -706,14 +706,16 @@ function addMarkerOnClick() { const point = d3.mouse(this); const x = rn(point[0], 2); const y = rn(point[1], 2); - const i = markers.length ? last(markers).i + 1 : 0; + // Find the current cell + const cell = findCell(point[0], point[1]); + + // Find the currently selected marker to use as a base const isMarkerSelected = markers.length && elSelected?.node()?.parentElement?.id === "markers"; const selectedMarker = isMarkerSelected ? markers.find(marker => marker.i === +elSelected.attr("id").slice(6)) : null; const baseMarker = selectedMarker || {icon: "❓"}; - const marker = {...baseMarker, i, x, y}; + const marker = Markers.add({...baseMarker, x, y, cell}); - markers.push(marker); const markersElement = document.getElementById("markers"); const rescale = +markersElement.getAttribute("rescale"); markersElement.insertAdjacentHTML("beforeend", drawMarker(marker, rescale));