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
|
|
@ -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";
|
||||
}
|
||||
updateBurgPreview(b);
|
||||
}
|
||||
|
||||
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