Merge branch 'upstream' into dev-submaps

This commit is contained in:
GoteGuru 2022-03-30 19:42:51 +00:00
commit 9b618544e6
8 changed files with 379 additions and 397 deletions

View file

@ -50,6 +50,7 @@ iframe {
mask-mode: alpha;
mask-clip: no-clip;
fill-rule: evenodd;
user-select: none;
}
#canvas {

View file

@ -959,6 +959,7 @@
<option value="isthmus">Isthmus</option>
<option value="shattered">Shattered</option>
<option value="taklamakan">Taklamakan</option>
<option value="oldWorld">Old World</option>
</optgroup>
<optgroup label="Specific">
<option value="africa-centric">Africa Centric</option>
@ -2404,6 +2405,7 @@
<option value="isthmus">Isthmus</option>
<option value="shattered">Shattered</option>
<option value="taklamakan">Taklamakan</option>
<option value="oldWorld">Old World</option>
</select>
</div>
<div id="templateTools">

View file

@ -119,5 +119,56 @@ window.HeightmapTemplates = (function () {
Hill 3-4 60-85 20-80 0-5
Hill 3-4 60-85 20-80 95-100`;
return {volcano, highIsland, lowIsland, continents, archipelago, atoll, mediterranean, peninsula, peninsula, pangea, isthmus, shattered, taklamakan};
const oldWorld = `Hill 4-6 20-40 15-85 30-45
Hill 3-7 20-40 15-85 55-70
Strait 2-7 vertical 0 0
Pit 1-2 40-50 35-55 20-80
Strait 2-7 vertical 0 0
Range 2-3 20-25 15-35 20-30
Range 2-3 20-25 15-35 65-80
Range 2-3 20-25 45-85 20-45
Range 2-3 20-25 45-85 65-80
Multiply .9 80-100 0 0
Strait 2-7 vertical 0 0
Pit 2-3 40-50 45-65 20-80
Trough 1-2 40-50 15-45 20-45
Trough 1-3 40-50 15-45 45-80
Trough 1-2 40-50 45-85 20-45
Trough 1-2 40-50 45-85 45-80
Multiply 1.2 17-20 0 0
Strait 2-7 horizontal 0 0
Multiply 1.2 17-50 0 0
Range 1-2 20-25 15-45 45-65
Range 1-2 20-25 65-85 45-80
Multiply 1.1 50-80 0 0
Hill 1-2 20 15-45 20-80
Hill 1-2 20 65-85 20-80
Multiply 1.2 15-30 0 0
Strait 2-7 vertical 0 0
Trough 1-2 40-50 35-65 65-80
Range 1-2 20-25 15-35 20-45
Strait 2-7 vertical 0 0
Range 1-2 20-25 65-85 45-80
Multiply .9 70-100 0 0
Hill 1-2 20-25 15-45 65-80
Hill 1-2 20-25 65-85 20-45
Hill 1 20-25 15-45 45-65
Hill 1 20-25 65-85 45-65
Strait 2-7 vertical 0 0
Trough 1-2 20-50 15-45 45-65
Trough 1-2 20-50 65-85 45-65
Strait 2-7 horizontal 0 0
Multiply 0.8 70-100 0 0
Hill 1-2 20-25 35-45 45-65
Hill 1-2 20-25 65-70 45-65
Pit 2-3 40-50 45-65 30-70
Trough 1-2 40-50 15-85 65-80
Trough 1-2 40-50 15-85 10-35
Strait 2-5 vertical 0 0
Multiply 1.1 45-90 0 0
Strait 3-7 vertical 0 0
Trough 1-2 40-50 45-65 45-65`;
return {volcano, highIsland, lowIsland, continents, archipelago, atoll, mediterranean, peninsula, peninsula, pangea, isthmus, shattered, taklamakan, oldWorld};
})();

View file

@ -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, list: listVolcanoes, add: addVolcano},
{type: "hot-springs", icon: "♨️", dy: 52, min: 30, each: 1200, multiplier: 1, list: listHotSprings, add: addHotSpring},
{type: "mines", icon: "⛏️", dx: 48, px: 13, min: 1, each: 15, multiplier: 1, list: listMines, add: addMine},
{type: "bridges", icon: "🌉", px: 14, min: 1, each: 5, multiplier: 1, list: listBridges, add: addBridge},
{type: "inns", icon: "🍻", px: 14, min: 1, each: 100, multiplier: 1, list: listInns, add: addInn},
{type: "lighthouses", icon: "🚨", px: 14, min: 1, each: 2, multiplier: 1, list: listLighthouses, add: addLighthouse},
{type: "waterfalls", icon: "⟱", dy: 54, px: 16, min: 1, each: 5, multiplier: 1, list: listWaterfalls, add: addWaterfall},
{type: "battlefields", icon: "⚔️", dy: 52, min: 50, each: 700, multiplier: 1, list: listBattlefields, add: addBattlefield},
{type: "dungeons", icon: "🗝️", dy: 51, px: 13, min: 30, each: 200, multiplier: 1, list: listDungeons, add: addDungeon},
{type: "lake-monsters", icon: "🐉", dy: 48, min: 2, each: 10, multiplier: 1, list: listLakeMonsters, add: addLakeMonster},
{type: "sea-monsters", icon: "🦑", min: 50, each: 700, multiplier: 1, list: listSeaMonsters, add: addSeaMonster},
{type: "hill-monsters", icon: "👹", dy: 54, px: 13, min: 30, each: 600, multiplier: 1, list: listHillMonsters, add: addHillMonster},
{type: "sacred-mountains", icon: "🗻", dy: 48, min: 1, each: 5, multiplier: 1, list: listSacredMountains, add: addSacredMountain},
{type: "sacred-forests", icon: "🌳", min: 30, each: 1000, multiplier: 1, list: listSacredForests, add: addSacredForest},
{type: "sacred-pineries", icon: "🌲", px: 13, min: 30, each: 800, multiplier: 1, list: listSacredPineries, add: addSacredPinery},
{type: "sacred-palm-groves", icon: "🌴", px: 13, min: 1, each: 100, multiplier: 1, list: listSacredPalmGroves, add: addSacredPalmGrove},
{type: "brigands", icon: "💰", px: 13, min: 50, each: 100, multiplier: 1, list: listBrigands, add: addBrigands},
{type: "pirates", icon: "🏴‍☠️", dx: 51, min: 40, each: 300, multiplier: 1, list: listPirates, add: addPirates},
{type: "statues", icon: "🗿", min: 80, each: 1200, multiplier: 1, list: listStatues, add: addStatue},
{type: "ruines", icon: "🏺", min: 80, each: 1200, multiplier: 1, list: listRuins, add: addRuins},
{type: "portals", icon: "🌀", px: 14, min: 16, each: 8, multiplier: +isFantasy, list: listPortals, add: 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.add("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, list, add}) => {
if (multiplier === 0) return;
fn(type, icon, multiplier);
let candidates = Array.from(list(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});
add("marker" + marker.i, cell);
quantity--;
}
});
occupied = [];
@ -96,87 +120,65 @@ 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--;
}
}
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--;
}
}
function addMines(type, icon, multiplier) {
function listMines({cells}) {
return cells.i.filter(i => !occupied[i] && cells.h[i] > 47 && cells.burg[i]);
}
function addMine(id, cell) {
const {cells} = pack;
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;
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--;
}
}
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)
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
);
let quantity = getQuantity(bridges, 1, 5, multiplier);
if (!quantity) return;
}
function addBridge(id, cell) {
const {cells} = pack;
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";
@ -191,17 +193,13 @@ window.Markers = (function () {
weathered: 1
};
notes.push({id, name: `${name} Bridge`, legend: `A ${rw(weightedAdjectives)} bridge spans over the ${riverName} near ${burg.name}`});
quantity--;
}
}
function addInns(type, icon, multiplier) {
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;
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 +428,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 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});
quantity--;
}
}
function addLighthouses(type, icon, multiplier) {
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 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;
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 addWaterfalls(type, icon, multiplier) {
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 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,24 +465,18 @@ 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)}`});
}
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);
@ -502,73 +484,56 @@ window.Markers = (function () {
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--;
}
}
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;
while (quantity) {
const [cell] = extractAnyElement(dungeons);
const id = addMarker({cell, icon, type, dy: 51, px: 13});
function listDungeons({cells}) {
return cells.i.filter(i => !occupied[i] && cells.pop[i] && cells.pop[i] < 3);
}
function addDungeon(id, cell) {
const dungeonSeed = `${seed}${cell}`;
const name = "Dungeon";
const legend = `<div>Undiscovered dungeon. See <a href="https://watabou.github.io/one-page-dungeon/?seed=${dungeonSeed}" target="_blank">One page dungeon</a></div><iframe src="https://watabou.github.io/one-page-dungeon/?seed=${dungeonSeed}" sandbox="allow-scripts allow-same-origin"></iframe>`;
notes.push({id, name, legend});
quantity--;
}
}
function addLakeMonsters(type, icon, multiplier) {
const {features} = pack;
function listLakeMonsters({features}) {
return features
.filter(feature => feature.type === "lake" && feature.group === "freshwater" && !occupied[feature.firstCell])
.map(feature => feature.firstCell);
}
const lakes = features.filter(feature => feature.type === "lake" && feature.group === "freshwater" && !occupied[feature.firstCell]);
let quantity = getQuantity(lakes, 2, 10, multiplier);
if (!quantity) return;
function addLakeMonster(id, cell) {
const lake = pack.features[pack.cells.f[cell]];
// 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--;
}
}
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;
while (quantity) {
const [cell] = extractAnyElement(sea);
const id = addMarker({cell, icon, type});
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});
quantity--;
}
}
function addHillMonsters(type, icon, multiplier) {
function listHillMonsters({cells}) {
return cells.i.filter(i => !occupied[i] && cells.h[i] >= 50 && cells.pop[i]);
}
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,100 +584,77 @@ 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--;
}
}
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--;
}
}
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--;
}
}
function addSacredPineries(type, icon, multiplier) {
// Sacred pineries spawn on boreal forests
function listSacredPineries({cells}) {
return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 9);
}
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--;
}
}
function addSacredPalmGroves(type, icon, multiplier) {
// 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 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--;
}
}
function addBrigands(type, icon, multiplier) {
function listBrigands({cells}) {
return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.road[i] > 4);
}
function addBrigands(id, cell) {
const {cells} = pack;
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;
const animals = [
"Apes",
"Badgers",
@ -747,9 +689,6 @@ 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];
@ -768,32 +707,25 @@ window.Markers = (function () {
const name = `${Names.getCulture(culture)} ${ra(animals)}`;
const legend = `A gang of ${locality} ${rw(types)}`;
notes.push({id, name, legend});
quantity--;
}
}
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});
function addPirates(id, cell) {
const name = `Pirates`;
const legend = `Pirate ships have been spotted in these waters`;
notes.push({id, name, legend});
quantity--;
}
}
function addStatues(type, icon, multiplier) {
function listStatues({cells}) {
return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.h[i] < 40);
}
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,9 +736,6 @@ 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);
@ -819,16 +748,13 @@ window.Markers = (function () {
const legend = `An ancient ${variant.toLowerCase()}. It has an inscription, but no one can translate it:
<div style="font-size: 1.8em; line-break: anywhere;">${inscription}</div>`;
notes.push({id, name, legend});
quantity--;
}
}
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 +771,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--;
}
}
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}) => cell && !occupied[cell])
.map(burg => [burg.name, burg.cell]);
let quantity = getQuantity(portals, 16, 8, multiplier);
if (!quantity) return;
.filter(({cell}) => !occupied[cell])
.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;
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--;
}
}
return {generate, regenerate, getConfig, setConfig};
return {add, generate, regenerate, getConfig, setConfig};
})();

View file

@ -1481,6 +1481,12 @@ function drawRivers() {
const riverPaths = pack.rivers.map(({cells, points, i, widthFactor, sourceWidth}) => {
if (!cells || cells.length < 2) return;
if (points && points.length !== cells.length) {
console.error(`River ${i} has ${cells.length} cells, but only ${points.length} points defined. Resetting points data`);
points = undefined;
}
const meanderedPoints = addMeandering(cells, points);
const path = getRiverPath(meanderedPoints, widthFactor, sourceWidth);
return `<path id="river${i}" d="${path}"/>`;

View file

@ -86,8 +86,8 @@ function editNotes(id, name) {
selector: "#notesLegend",
height: "90%",
menubar: false,
plugins: `autolink lists link charmap print formatpainter casechange code fullscreen image link media table paste hr checklist wordcount`,
toolbar: `code | undo redo | bold italic strikethrough | forecolor backcolor | formatpainter removeformat | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media table | fontselect fontsizeselect | blockquote hr casechange checklist charmap | print fullscreen`,
plugins: `autolink lists link charmap print code fullscreen image link media table paste hr wordcount`,
toolbar: `code | undo redo | removeformat | bold italic strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media table | fontselect fontsizeselect | blockquote hr charmap | print fullscreen`,
media_alt_source: false,
media_poster: false,
setup: editor => {

View file

@ -105,7 +105,7 @@ function showSupporters() {
Jonathan Williams,ojacid .,Brian Wilson,A Patreon of the Ahts,Shubham Jakhotiya,www15o,Jan Bundesmann,Angelique Badger,Joshua Xiong,Moist mongol,
Frank Fewkes,jason baldrick,Game Master Pro,Andrew Kircher,Preston Mitchell,Chris Kohut,Emarandzeb,Trentin Bergeron,Damon Gallaty,Pleaseworkforonce,
Jordan,William Markus,Sidr Dim,Alexander Whittaker,The Next Level,Patrick Valverde,Markus Peham,Daniel Cooper,the Beagles of Neorbus,Marley Moule,
Maximilian Schielke,Johnathan Xavier Hutchinson,Ele,Rita`;
Maximilian Schielke,Johnathan Xavier Hutchinson,Ele,Rita,Randy Ross,John Wick,RedSpaz,cameron cannon,Ian Grau-Fay,Kyle Barrett,Charlotte Wiland`;
const array = supporters
.replace(/(?:\r\n|\r|\n)/g, "")
@ -579,17 +579,18 @@ function randomizeOptions() {
function randomizeHeightmapTemplate() {
const templates = {
volcano: 3,
highIsland: 22,
highIsland: 19,
lowIsland: 9,
continents: 19,
archipelago: 23,
continents: 16,
archipelago: 18,
mediterranean: 5,
peninsula: 3,
pangea: 5,
isthmus: 2,
atoll: 1,
shattered: 7,
taklamakan: 1
taklamakan: 1,
oldWorld: 11
};
document.getElementById("templateInput").value = rw(templates);
}

View file

@ -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));