mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
Sky burgs + air routes: layer, editor toggles, styling, altitude, icons, and generators
This commit is contained in:
parent
e94ec5b123
commit
c075e704fd
6 changed files with 45 additions and 12 deletions
1
main.js
1
main.js
|
|
@ -114,6 +114,7 @@ labels.append("g").attr("id", "states");
|
|||
labels.append("g").attr("id", "addedLabels");
|
||||
|
||||
burgIcons.append("g").attr("id", "cities");
|
||||
burgIcons.append("g").attr("id", "skyburgs");
|
||||
burgLabels.append("g").attr("id", "cities");
|
||||
anchors.append("g").attr("id", "cities");
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,21 @@ function drawBurgIcons() {
|
|||
.attr("width", townsAnchorsSize)
|
||||
.attr("height", townsAnchorsSize);
|
||||
|
||||
// Sky burgs (flying or sky port)
|
||||
const sky = pack.burgs.filter(b => b.i && !b.removed && (b.flying || b.skyPort));
|
||||
const skyIcons = burgIcons.select("#skyburgs");
|
||||
const skySize = skyIcons.attr("size") || 0.6;
|
||||
skyIcons
|
||||
.selectAll("circle")
|
||||
.data(sky)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.attr("id", d => "burg" + d.i)
|
||||
.attr("data-id", d => d.i)
|
||||
.attr("cx", d => d.x)
|
||||
.attr("cy", d => d.y)
|
||||
.attr("r", skySize);
|
||||
|
||||
TIME && console.timeEnd("drawBurgIcons");
|
||||
|
||||
// Sky burgs (flying or sky port) — styled separately
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ window.Routes = (function () {
|
|||
|
||||
for (const burg of burgs) {
|
||||
if (burg.i && !burg.removed) {
|
||||
// Exclude flying / sky port burgs from land road/trail graphs
|
||||
if (burg.flying || burg.skyPort) continue;
|
||||
const {feature, capital, port} = burg;
|
||||
addBurg(burgsByFeature, feature, burg);
|
||||
|
||||
|
|
@ -846,13 +848,11 @@ window.Routes = (function () {
|
|||
function generateAirRoutes() {
|
||||
TIME && console.time("generateAirRoutes");
|
||||
const air = [];
|
||||
|
||||
const skyPorts = pack.burgs.filter(b => b && b.i && !b.removed && (b.skyPort || b.flying));
|
||||
if (skyPorts.length < 2) {
|
||||
TIME && console.timeEnd("generateAirRoutes");
|
||||
return air;
|
||||
}
|
||||
|
||||
// Use Urquhart edges to avoid a complete graph
|
||||
const points = skyPorts.map(b => [b.x, b.y]);
|
||||
const edges = calculateUrquhartEdges(points);
|
||||
|
|
|
|||
|
|
@ -334,6 +334,7 @@ function editBurg(id) {
|
|||
}
|
||||
// Regenerate routes to reflect air network
|
||||
regenerateRoutes();
|
||||
if (layerIsOn("toggleBurgIcons")) drawBurgIcons();
|
||||
}
|
||||
else if (feature === "flying") {
|
||||
burg.flying = +turnOn;
|
||||
|
|
@ -348,6 +349,7 @@ function editBurg(id) {
|
|||
} catch (e) { ERROR && console.error(e); }
|
||||
}
|
||||
regenerateRoutes();
|
||||
if (layerIsOn("toggleBurgIcons")) drawBurgIcons();
|
||||
}
|
||||
else if (feature === "capital") toggleCapital(id);
|
||||
else burg[feature] = +turnOn;
|
||||
|
|
@ -541,11 +543,18 @@ function editBurg(id) {
|
|||
}
|
||||
burg.x = x;
|
||||
burg.y = y;
|
||||
if (burg.capital) pack.states[newState].center = burg.cell;
|
||||
|
||||
if (burg.capital) pack.states[burg.state].center = burg.cell;
|
||||
|
||||
if (d3.event.shiftKey === false) toggleRelocateBurg();
|
||||
}
|
||||
|
||||
function changeAltitude() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
burg.altitude = Math.max(0, Math.round(+byId("burgAltitude").value));
|
||||
if (burg.flying) byId("burgElevation").innerHTML = `${burg.altitude} m (sky altitude)`;
|
||||
}
|
||||
|
||||
function editBurgLegend() {
|
||||
const id = elSelected.attr("data-id");
|
||||
const name = elSelected.text();
|
||||
|
|
|
|||
|
|
@ -288,7 +288,6 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
|||
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
|
||||
|
||||
const id = addBurg(point); // add new burg
|
||||
|
||||
// Mark flying burgs and assign to Sky State, make them sky ports
|
||||
if (pack.cells.h[cell] < 20) {
|
||||
const burg = pack.burgs[id];
|
||||
|
|
@ -299,6 +298,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
|||
if (burg.state !== skyStateId) burg.state = skyStateId;
|
||||
// Keep as non-sea port
|
||||
burg.port = 0;
|
||||
if (layerIsOn("toggleBurgIcons")) drawBurgIcons();
|
||||
}
|
||||
|
||||
if (d3.event.shiftKey === false) {
|
||||
|
|
|
|||
|
|
@ -189,7 +189,9 @@ function addBurg(point) {
|
|||
|
||||
BurgsAndStates.defineBurgFeatures(burg);
|
||||
|
||||
const newRoute = Routes.connect(cellId);
|
||||
// Do not auto-connect routes for water-placed (flying) burgs
|
||||
const isWater = cells.h[cellId] < 20;
|
||||
const newRoute = isWater ? null : Routes.connect(cellId);
|
||||
if (newRoute && layerIsOn("toggleRoutes")) {
|
||||
routes
|
||||
.select("#" + newRoute.group)
|
||||
|
|
@ -230,7 +232,6 @@ function moveBurgToGroup(id, g) {
|
|||
// Ensure a dedicated locked Sky State exists; create if missing and return its id
|
||||
function ensureSkyState(anchorBurgId) {
|
||||
const {states, burgs, cultures, cells} = pack;
|
||||
|
||||
// Reuse existing sky state if present
|
||||
let sky = states.find(s => s && s.i && !s.removed && s.skyRealm);
|
||||
if (sky) return sky.i;
|
||||
|
|
@ -258,7 +259,6 @@ function ensureSkyState(anchorBurgId) {
|
|||
lock: 1,
|
||||
skyRealm: 1
|
||||
};
|
||||
|
||||
states.push(newState);
|
||||
|
||||
// Assign the burg and its cell to the Sky State
|
||||
|
|
@ -269,7 +269,6 @@ function ensureSkyState(anchorBurgId) {
|
|||
// Move to cities layer for capitals
|
||||
moveBurgToGroup(anchorBurgId, "cities");
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
@ -367,7 +366,7 @@ function getBurgLink(burg) {
|
|||
|
||||
const population = burg.population * populationRate * urbanization;
|
||||
if (population >= options.villageMaxPopulation || burg.citadel || burg.walls || burg.temple || burg.shanty)
|
||||
return createMfcgLink(burg);
|
||||
return createMfcgLink(burg, false);
|
||||
|
||||
return createVillageGeneratorLink(burg);
|
||||
}
|
||||
|
|
@ -386,6 +385,7 @@ function createMfcgLink(burg, isSky = false) {
|
|||
|
||||
const sea = !isSky && coast && cells.haven[cell]
|
||||
? (() => {
|
||||
// calculate sea 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;
|
||||
|
|
@ -407,6 +407,7 @@ function createMfcgLink(burg, isSky = false) {
|
|||
const greens = isSky ? 1 : undefined;
|
||||
const gates = isSky ? 0 : -1;
|
||||
|
||||
const url = new URL("https://watabou.github.io/city-generator/");
|
||||
const params = {
|
||||
name,
|
||||
population,
|
||||
|
|
@ -425,8 +426,6 @@ function createMfcgLink(burg, isSky = false) {
|
|||
};
|
||||
if (greens !== undefined) params.greens = greens;
|
||||
if (gates !== undefined) params.gates = gates;
|
||||
|
||||
const url = new URL("https://watabou.github.io/city-generator/");
|
||||
url.search = new URLSearchParams(params);
|
||||
if (sea) url.searchParams.append("sea", sea);
|
||||
|
||||
|
|
@ -483,6 +482,15 @@ function createVillageGeneratorLink(burg) {
|
|||
return url.toString();
|
||||
}
|
||||
|
||||
// helper: draw legend entry for Air routes
|
||||
function drawAirRoutesLegend() {
|
||||
const group = document.querySelector("#airroutes");
|
||||
if (!group) return tip("Air routes group not found", false, "error");
|
||||
const stroke = group.getAttribute("stroke") || "#8a2be2";
|
||||
const data = [["airroutes", stroke, "Air routes"]];
|
||||
drawLegend("Routes", data);
|
||||
}
|
||||
|
||||
// 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