mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
feat: burg group editor - map preview
This commit is contained in:
parent
1125192b72
commit
6526c4c1f1
7 changed files with 192 additions and 194 deletions
31
index.html
31
index.html
|
|
@ -3467,12 +3467,6 @@
|
|||
<div style="display: flex; justify-content: space-between">
|
||||
<span>Burg preview:</span>
|
||||
<div style="display: flex; gap: 0.5em">
|
||||
<i
|
||||
id="burgLinkEdit"
|
||||
data-tip="Provide custom link to the burg map"
|
||||
class="icon-pencil pointer"
|
||||
style="margin-top: -0.1em"
|
||||
></i>
|
||||
<i id="burgLinkOpen" data-tip="Open burg map in a new tab" class="icon-link-ext pointer"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3502,7 +3496,7 @@
|
|||
</div>
|
||||
|
||||
<button id="burgEditEmblem" data-tip="Edit emblem" class="icon-shield-alt"></button>
|
||||
<button id="burgTogglePreview" data-tip="Toggle preview" class="icon-map"></button>
|
||||
<button id="burgSetPreviewLink" data-tip="Set custom burg map URL" class="icon-map-o"></button>
|
||||
<button id="burgLocate" data-tip="Zoom map and center view in the burg" class="icon-target"></button>
|
||||
<button id="burgRelocate" data-tip="Relocate burg. Click on map to move the burg" class="icon-map-pin"></button>
|
||||
<button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button>
|
||||
|
|
@ -5340,30 +5334,13 @@
|
|||
</div>
|
||||
|
||||
<div id="burgGroupsEditor" class="dialog stable" style="display: none">
|
||||
<form id="burgGroupsForm" class="table">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 1em" />
|
||||
<col style="width: 6em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 4em" />
|
||||
<col style="width: 1em" />
|
||||
<col style="width: 1em" />
|
||||
<col style="width: 1em" />
|
||||
<col style="width: 1em" />
|
||||
<col style="width: 1em" />
|
||||
<col style="width: 1em" />
|
||||
</colgroup>
|
||||
<form id="burgGroupsForm">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-tip="Rendering order: higher values are rendered on top">Order</th>
|
||||
<th data-tip="Type group name">Name</th>
|
||||
<th data-tip="Burg preview generator">Preview generator</th>
|
||||
<th data-tip="Set min population constraint" colspan="2">Population</th>
|
||||
<th data-tip="Set population percentile: 0-100, where 90 means the burg must have a population higher than 90% of all burgs">Percentile</th>
|
||||
<th data-tip="Select allowed biomes">Biomes</th>
|
||||
|
|
|
|||
|
|
@ -261,23 +261,21 @@ window.Burgs = (() => {
|
|||
}
|
||||
|
||||
const getDefaultGroups = () => [
|
||||
{name: "capitals", active: true, order: 9, features: {capital: true}, preview: "watabou-city-generator"},
|
||||
{name: "cities", active: true, order: 8, percentile: 90, min: 5, preview: "watabou-city-generator"},
|
||||
{name: "capitals", active: true, order: 9, features: {capital: true}, preview: "watabou-city"},
|
||||
{name: "cities", active: true, order: 8, percentile: 90, min: 5, preview: "watabou-city"},
|
||||
{
|
||||
name: "forts",
|
||||
active: true,
|
||||
features: {citadel: true, walls: false, plaza: false, port: false},
|
||||
order: 6,
|
||||
max: 1,
|
||||
preview: null
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
name: "monasteries",
|
||||
active: true,
|
||||
features: {temple: true, walls: false, plaza: false, port: false},
|
||||
order: 5,
|
||||
max: 0.8,
|
||||
preview: null
|
||||
max: 0.8
|
||||
},
|
||||
{
|
||||
name: "caravanserais",
|
||||
|
|
@ -285,8 +283,7 @@ window.Burgs = (() => {
|
|||
features: {port: false, plaza: true},
|
||||
order: 4,
|
||||
max: 0.8,
|
||||
biomes: [1, 2, 3],
|
||||
preview: null
|
||||
biomes: [1, 2, 3]
|
||||
},
|
||||
{
|
||||
name: "trading_posts",
|
||||
|
|
@ -295,7 +292,7 @@ window.Burgs = (() => {
|
|||
features: {plaza: true},
|
||||
max: 0.8,
|
||||
biomes: [5, 6, 7, 8, 9, 10, 11, 12],
|
||||
preview: null
|
||||
preview: "watabou-dwelling"
|
||||
},
|
||||
{
|
||||
name: "villages",
|
||||
|
|
@ -304,7 +301,7 @@ window.Burgs = (() => {
|
|||
min: 0.1,
|
||||
max: 2,
|
||||
features: {walls: false},
|
||||
preview: "watabou-village-generator"
|
||||
preview: "watabou-village"
|
||||
},
|
||||
{
|
||||
name: "hamlets",
|
||||
|
|
@ -312,9 +309,9 @@ window.Burgs = (() => {
|
|||
order: 1,
|
||||
features: {walls: false, plaza: false},
|
||||
max: 0.1,
|
||||
preview: "watabou-village-generator"
|
||||
preview: "watabou-village"
|
||||
},
|
||||
{name: "towns", active: true, order: 7, isDefault: true, preview: "watabou-city-generator"}
|
||||
{name: "towns", active: true, order: 7, isDefault: true, preview: "watabou-city"}
|
||||
];
|
||||
|
||||
function defineGroup(burg, populations) {
|
||||
|
|
@ -359,6 +356,146 @@ window.Burgs = (() => {
|
|||
}
|
||||
}
|
||||
|
||||
const previewGeneratorsMap = {
|
||||
"watabou-city": createWatabouCityLinks,
|
||||
"watabou-village": createWatabouVillageLinks,
|
||||
"watabou-dwelling": createWatabouDwellingLinks
|
||||
};
|
||||
|
||||
function getPreview(burg) {
|
||||
if (burg.link) return {link: burg.link, preview: burg.link};
|
||||
|
||||
const group = options.burgs.groups.find(g => g.name === burg.group);
|
||||
if (!group?.preview || !previewGeneratorsMap[group.preview]) return {link: null, preview: null};
|
||||
|
||||
return previewGeneratorsMap[group.preview](burg);
|
||||
}
|
||||
|
||||
function createWatabouCityLinks(burg) {
|
||||
const cells = pack.cells;
|
||||
const {i, name, population: burgPopulation, cell} = burg;
|
||||
const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0);
|
||||
|
||||
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
|
||||
const size = minmax(Math.ceil(sizeRaw), 6, 100);
|
||||
const population = rn(burgPopulation * populationRate * urbanization);
|
||||
|
||||
const river = cells.r[cell] ? 1 : 0;
|
||||
const coast = Number(burg.port > 0);
|
||||
const sea = (() => {
|
||||
if (!coast || !cells.haven[cell]) return null;
|
||||
|
||||
// calculate see direction: 0 = south, 0.5 = west, 1 = north, 1.5 = east
|
||||
const p1 = cells.p[cell];
|
||||
const p2 = cells.p[cells.haven[cell]];
|
||||
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
|
||||
if (deg < 0) deg += 360;
|
||||
return rn(normalize(deg, 0, 360) * 2, 2);
|
||||
})();
|
||||
|
||||
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||
const farms = +arableBiomes.includes(cells.biome[cell]);
|
||||
|
||||
const citadel = +burg.citadel;
|
||||
const urban_castle = +(citadel && each(2)(i));
|
||||
|
||||
const hub = Routes.isCrossroad(cell);
|
||||
const walls = +burg.walls;
|
||||
const plaza = +burg.plaza;
|
||||
const temple = +burg.temple;
|
||||
const shantytown = +burg.shanty;
|
||||
|
||||
const url = new URL("https://watabou.github.io/city-generator/");
|
||||
url.search = new URLSearchParams({
|
||||
name,
|
||||
population,
|
||||
size,
|
||||
seed: burgSeed,
|
||||
river,
|
||||
coast,
|
||||
farms,
|
||||
citadel,
|
||||
urban_castle,
|
||||
hub,
|
||||
plaza,
|
||||
temple,
|
||||
walls,
|
||||
shantytown,
|
||||
gates: -1
|
||||
});
|
||||
if (sea) url.searchParams.append("sea", sea);
|
||||
|
||||
const link = url.toString();
|
||||
return {link, preview: link + "&preview=1"};
|
||||
}
|
||||
|
||||
function createWatabouVillageLinks(burg) {
|
||||
const {cells, features} = pack;
|
||||
const {i, population, cell} = burg;
|
||||
|
||||
const burgSeed = seed + String(i).padStart(4, 0);
|
||||
const pop = rn(population * populationRate * urbanization);
|
||||
const tags = [];
|
||||
|
||||
if (cells.r[cell] && cells.haven[cell]) tags.push("estuary");
|
||||
else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district");
|
||||
else if (burg.port) tags.push("coast");
|
||||
else if (cells.conf[cell]) tags.push("confluence");
|
||||
else if (cells.r[cell]) tags.push("river");
|
||||
else if (pop < 200 && each(4)(cell)) tags.push("pond");
|
||||
|
||||
const connectivityRate = Routes.getConnectivityRate(cell);
|
||||
tags.push(connectivityRate > 1 ? "highway" : connectivityRate === 1 ? "dead end" : "isolated");
|
||||
|
||||
const biome = cells.biome[cell];
|
||||
const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||
if (!arableBiomes.includes(biome)) tags.push("uncultivated");
|
||||
else if (each(6)(cell)) tags.push("farmland");
|
||||
|
||||
const temp = grid.cells.temp[cells.g[cell]];
|
||||
if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards");
|
||||
|
||||
if (!burg.plaza) tags.push("no square");
|
||||
|
||||
if (pop < 100) tags.push("sparse");
|
||||
else if (pop > 300) tags.push("dense");
|
||||
|
||||
const width = (() => {
|
||||
if (pop > 1500) return 1600;
|
||||
if (pop > 1000) return 1400;
|
||||
if (pop > 500) return 1000;
|
||||
if (pop > 200) return 800;
|
||||
if (pop > 100) return 600;
|
||||
return 400;
|
||||
})();
|
||||
const height = rn(width / 2.05);
|
||||
|
||||
const url = new URL("https://watabou.github.io/village-generator/");
|
||||
url.search = new URLSearchParams({pop, name: "", seed: burgSeed, width, height, tags});
|
||||
|
||||
const link = url.toString();
|
||||
return {link, preview: link + "&preview=1"};
|
||||
}
|
||||
|
||||
function createWatabouDwellingLinks(burg) {
|
||||
const burgSeed = seed + String(burg.i).padStart(4, 0);
|
||||
const pop = rn(burg.population * populationRate * urbanization);
|
||||
|
||||
const tags = (() => {
|
||||
if (pop > 200) return ["large", "tall"];
|
||||
if (pop > 100) return ["large"];
|
||||
if (pop > 50) return ["tall"];
|
||||
if (pop > 20) return ["low"];
|
||||
return ["small"];
|
||||
})();
|
||||
|
||||
const url = new URL("https://watabou.github.io/dwellings/");
|
||||
url.search = new URLSearchParams({pop, name: "", seed: burgSeed, tags});
|
||||
|
||||
const link = url.toString();
|
||||
return {link, preview: link + "&preview=1"};
|
||||
}
|
||||
|
||||
function add([x, y]) {
|
||||
const {cells} = pack;
|
||||
|
||||
|
|
@ -410,5 +547,5 @@ window.Burgs = (() => {
|
|||
return burgId;
|
||||
}
|
||||
|
||||
return {generate, getDefaultGroups, specify, defineGroup, getType, add};
|
||||
return {generate, getDefaultGroups, specify, defineGroup, getPreview, getType, add};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -830,21 +830,6 @@ export function resolveVersionConflicts(mapVersion) {
|
|||
});
|
||||
}
|
||||
|
||||
if (isOlderThan("1.97.0")) {
|
||||
// v1.97.00 changed MFCG link to an arbitrary preview URL
|
||||
options.showBurgPreview = options.showMFCGMap;
|
||||
delete options.showMFCGMap;
|
||||
|
||||
pack.burgs.forEach(burg => {
|
||||
if (!burg.i || burg.removed) return;
|
||||
|
||||
if (burg.MFCG) {
|
||||
burg.link = getBurgLink(burg);
|
||||
delete burg.MFCG;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isOlderThan("1.98.0")) {
|
||||
// v1.98.00 changed compass layer and rose element id
|
||||
const rose = compass.select("use");
|
||||
|
|
@ -966,6 +951,17 @@ export function resolveVersionConflicts(mapVersion) {
|
|||
groups: groups.map(name => ({name, active: true, preview: null}))
|
||||
};
|
||||
|
||||
pack.burgs.forEach(burg => {
|
||||
if (!burg.i || burg.removed) return;
|
||||
|
||||
if (burg.MFCG) {
|
||||
burg.link = getBurgLink(burg);
|
||||
delete burg.MFCG;
|
||||
}
|
||||
});
|
||||
|
||||
delete options.showBurgPreview;
|
||||
delete options.showMFCGMap;
|
||||
delete options.villageMaxPopulation;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ function editBurg(id) {
|
|||
byId("burgPopulation").on("change", changePopulation);
|
||||
burgBody.querySelectorAll(".burgFeature").forEach(el => el.on("click", toggleFeature));
|
||||
byId("burgLinkOpen").on("click", openBurgLink);
|
||||
byId("burgLinkEdit").on("click", changeBurgLink);
|
||||
|
||||
byId("burgStyleShow").on("click", showStyleSection);
|
||||
byId("burgStyleHide").on("click", hideStyleSection);
|
||||
|
|
@ -41,7 +40,7 @@ function editBurg(id) {
|
|||
byId("burgEditAnchorStyle").on("click", editGroupAnchorStyle);
|
||||
|
||||
byId("burgEmblem").on("click", openEmblemEdit);
|
||||
byId("burgTogglePreview").on("click", toggleBurgPreview);
|
||||
byId("burgSetPreviewLink").on("click", setCustomPreview);
|
||||
byId("burgEditEmblem").on("click", openEmblemEdit);
|
||||
byId("burgLocate").on("click", zoomIntoBurg);
|
||||
byId("burgRelocate").on("click", toggleRelocateBurg);
|
||||
|
|
@ -105,12 +104,7 @@ function editBurg(id) {
|
|||
COArenderer.trigger(coaID, b.coa);
|
||||
byId("burgEmblem").setAttribute("href", "#" + coaID);
|
||||
|
||||
if (options.showBurgPreview) {
|
||||
byId("burgPreviewSection").style.display = "block";
|
||||
updateBurgPreview(b);
|
||||
} else {
|
||||
byId("burgPreviewSection").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function dragBurgLabel() {
|
||||
|
|
@ -231,31 +225,37 @@ function editBurg(id) {
|
|||
}
|
||||
|
||||
function updateBurgPreview(burg) {
|
||||
const src = getBurgLink(burg) + "&preview=1";
|
||||
const preview = Burgs.getPreview(burg).preview;
|
||||
if (!preview) {
|
||||
byId("burgPreviewSection").style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
byId("burgPreviewSection").style.display = "block";
|
||||
|
||||
// recreate object to force reload (Chrome bug)
|
||||
const container = byId("burgPreviewObject");
|
||||
container.innerHTML = "";
|
||||
const object = document.createElement("object");
|
||||
object.style.width = "100%";
|
||||
object.data = src;
|
||||
object.data = preview;
|
||||
container.insertBefore(object, null);
|
||||
}
|
||||
|
||||
function openBurgLink() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
|
||||
openURL(getBurgLink(burg));
|
||||
const link = Burgs.getPreview(burg).link;
|
||||
if (link) openURL(link);
|
||||
}
|
||||
|
||||
function changeBurgLink() {
|
||||
function setCustomPreview() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
|
||||
prompt(
|
||||
"Provide custom link to the burg map. It can be a link to Medieval Fantasy City Generator, a different tool, or just an image. Leave empty to use the default map",
|
||||
{default: getBurgLink(burg), required: false},
|
||||
"Provide custom URL to the burg map. It can be a link to a generator or just an image. Leave empty to use the default map preview",
|
||||
{default: Burgs.getPreview(burg).link, required: false},
|
||||
link => {
|
||||
if (link) burg.link = link;
|
||||
else delete burg.link;
|
||||
|
|
@ -265,17 +265,11 @@ function editBurg(id) {
|
|||
}
|
||||
|
||||
function openEmblemEdit() {
|
||||
const id = +elSelected.attr("data-id"),
|
||||
burg = pack.burgs[id];
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
editEmblem("burg", "burgCOA" + id, burg);
|
||||
}
|
||||
|
||||
function toggleBurgPreview() {
|
||||
options.showBurgPreview = !options.showBurgPreview;
|
||||
byId("burgPreviewSection").style.display = options.showBurgPreview ? "block" : "none";
|
||||
byId("burgTogglePreview").className = options.showBurgPreview ? "icon-map" : "icon-map-o";
|
||||
}
|
||||
|
||||
function zoomIntoBurg() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
|
|
|
|||
|
|
@ -61,6 +61,14 @@ function editBurgGroups() {
|
|||
return /* html */ `<tr name="${group.name}">
|
||||
<td data-tip="Rendering order: higher values are rendered on top"><input type="number" name="order" min="1" max="999" step="1" required value="${group.order || ''}" /></td>
|
||||
<td data-tip="Type group name. It can contain only text, digits and underscore"><input type="text" name="name" value="${group.name}" required pattern="\\w+" /></td>
|
||||
<td data-tip="Burg preview generator">
|
||||
<select name="preview">
|
||||
<option value="" ${!group.preview ? "selected" : ""}>no</option>
|
||||
<option value="watabou-city" ${group.preview === "watabou-city" ? "selected" : ""}>Watabou City</option>
|
||||
<option value="watabou-village" ${group.preview === "watabou-village" ? "selected" : ""}>Watabou Village</option>
|
||||
<option value="watabou-dwelling" ${group.preview === "watabou-dwellings" ? "selected" : ""}>Watabou Dwelling</option>
|
||||
</select>
|
||||
</td>
|
||||
<td data-tip="Set min population constraint"><input type="number" name="min" min="0" step="any" value="${group.min || ''}" /></td>
|
||||
<td data-tip="Set max population constraint"><input type="number" name="max" min="0" step="any" value="${group.max || ''}" /></td>
|
||||
<td data-tip="Set population percentile"><input type="number" name="percentile" min="0" max="100" step="any" value="${group.percentile || ''}" /></td>
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
|||
}
|
||||
|
||||
function downloadBurgsData() {
|
||||
let data = `Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Group,Population,X,Y,Latitude,Longitude,Elevation (${heightUnit.value}),Temperature,Temperature likeness,Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town,Emblem,City Generator Link\n`; // headers
|
||||
let data = `Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Group,Population,X,Y,Latitude,Longitude,Elevation (${heightUnit.value}),Temperature,Temperature likeness,Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town,Emblem,Preview link\n`; // headers
|
||||
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
||||
|
||||
valid.forEach(b => {
|
||||
|
|
@ -458,7 +458,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
|||
data += b.temple ? "temple," : ",";
|
||||
data += b.shanty ? "shanty town," : ",";
|
||||
data += b.coa ? JSON.stringify(b.coa).replace(/"/g, "").replace(/,/g, ";") + "," : ",";
|
||||
data += getBurgLink(b);
|
||||
data += Burgs.getPreview(b).link;
|
||||
|
||||
data += "\n";
|
||||
});
|
||||
|
|
|
|||
|
|
@ -240,120 +240,6 @@ function togglePort(burg) {
|
|||
.attr("height", size);
|
||||
}
|
||||
|
||||
// TODO: rework this function to use the new data structure
|
||||
function getBurgLink(burg) {
|
||||
if (burg.link) return burg.link;
|
||||
if (burg.citadel || burg.walls || burg.temple || burg.shanty) return createMfcgLink(burg);
|
||||
return createVillageGeneratorLink(burg);
|
||||
}
|
||||
|
||||
function createMfcgLink(burg) {
|
||||
const {cells} = pack;
|
||||
const {i, name, population: burgPopulation, cell} = burg;
|
||||
const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0);
|
||||
|
||||
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
|
||||
const size = minmax(Math.ceil(sizeRaw), 6, 100);
|
||||
const population = rn(burgPopulation * populationRate * urbanization);
|
||||
|
||||
const river = cells.r[cell] ? 1 : 0;
|
||||
const coast = Number(burg.port > 0);
|
||||
const sea = (() => {
|
||||
if (!coast || !cells.haven[cell]) return null;
|
||||
|
||||
// calculate see direction: 0 = south, 0.5 = west, 1 = north, 1.5 = east
|
||||
const p1 = cells.p[cell];
|
||||
const p2 = cells.p[cells.haven[cell]];
|
||||
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
|
||||
if (deg < 0) deg += 360;
|
||||
return rn(normalize(deg, 0, 360) * 2, 2);
|
||||
})();
|
||||
|
||||
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||
const farms = +arableBiomes.includes(cells.biome[cell]);
|
||||
|
||||
const citadel = +burg.citadel;
|
||||
const urban_castle = +(citadel && each(2)(i));
|
||||
|
||||
const hub = Routes.isCrossroad(cell);
|
||||
const walls = +burg.walls;
|
||||
const plaza = +burg.plaza;
|
||||
const temple = +burg.temple;
|
||||
const shantytown = +burg.shanty;
|
||||
|
||||
const url = new URL("https://watabou.github.io/city-generator/");
|
||||
url.search = new URLSearchParams({
|
||||
name,
|
||||
population,
|
||||
size,
|
||||
seed: burgSeed,
|
||||
river,
|
||||
coast,
|
||||
farms,
|
||||
citadel,
|
||||
urban_castle,
|
||||
hub,
|
||||
plaza,
|
||||
temple,
|
||||
walls,
|
||||
shantytown,
|
||||
gates: -1
|
||||
});
|
||||
if (sea) url.searchParams.append("sea", sea);
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function createVillageGeneratorLink(burg) {
|
||||
const {cells, features} = pack;
|
||||
const {i, population, cell} = burg;
|
||||
|
||||
const pop = rn(population * populationRate * urbanization);
|
||||
const burgSeed = seed + String(i).padStart(4, 0);
|
||||
const tags = [];
|
||||
|
||||
if (cells.r[cell] && cells.haven[cell]) tags.push("estuary");
|
||||
else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district");
|
||||
else if (burg.port) tags.push("coast");
|
||||
else if (cells.conf[cell]) tags.push("confluence");
|
||||
else if (cells.r[cell]) tags.push("river");
|
||||
else if (pop < 200 && each(4)(cell)) tags.push("pond");
|
||||
|
||||
const roadsNumber = Object.values(pack.cells.routes[cell] || {}).filter(routeId => {
|
||||
const route = pack.routes.find(route => route.i === routeId);
|
||||
if (!route) return false;
|
||||
return route.group === "roads" || route.group === "trails";
|
||||
}).length;
|
||||
tags.push(roadsNumber > 1 ? "highway" : roadsNumber === 1 ? "dead end" : "isolated");
|
||||
|
||||
const biome = cells.biome[cell];
|
||||
const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||
if (!arableBiomes.includes(biome)) tags.push("uncultivated");
|
||||
else if (each(6)(cell)) tags.push("farmland");
|
||||
|
||||
const temp = grid.cells.temp[cells.g[cell]];
|
||||
if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards");
|
||||
|
||||
if (!burg.plaza) tags.push("no square");
|
||||
|
||||
if (pop < 100) tags.push("sparse");
|
||||
else if (pop > 300) tags.push("dense");
|
||||
|
||||
const width = (() => {
|
||||
if (pop > 1500) return 1600;
|
||||
if (pop > 1000) return 1400;
|
||||
if (pop > 500) return 1000;
|
||||
if (pop > 200) return 800;
|
||||
if (pop > 100) return 600;
|
||||
return 400;
|
||||
})();
|
||||
const height = rn(width / 2.2);
|
||||
|
||||
const url = new URL("https://watabou.github.io/village-generator/");
|
||||
url.search = new URLSearchParams({pop, name: "", seed: burgSeed, width, height, tags});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// draw legend box
|
||||
function drawLegend(name, data) {
|
||||
legend.selectAll("*").remove(); // fully redraw every time
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue