Fantasy-Map-Generator/modules/ui/burg-editor.js
Azgaar fd9e010153 refactor(burgs): centralize burg management logic in Burgs module
Moved burg-related functions like `removeBurg`, `changeGroup`, and `toggleCapital` into the Burgs module to improve code organization and maintainability. Updated all references to these functions across the codebase. This change reduces duplication and ensures consistent behavior for burg management operations.
2025-04-17 01:03:14 +02:00

475 lines
15 KiB
JavaScript

"use strict";
function editBurg(id) {
if (customization) return;
closeDialogs(".stable");
if (!layerIsOn("toggleBurgIcons")) toggleBurgIcons();
if (!layerIsOn("toggleLabels")) toggleLabels();
const burg = id || d3.event.target.dataset.id;
elSelected = burgLabels.select("[data-id='" + burg + "']");
burgLabels.selectAll("text").call(d3.drag().on("start", dragBurgLabel)).classed("draggable", true);
updateGroupsList();
updateBurgValues();
$("#burgEditor").dialog({
title: "Edit Burg",
resizable: false,
close: closeBurgEditor,
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"}
});
if (modules.editBurg) return;
modules.editBurg = true;
// add listeners
byId("burgName").on("input", changeName);
byId("burgNameReRandom").on("click", generateNameRandom);
byId("burgGroup").on("change", changeGroup);
byId("burgGroupConfigure").on("click", editBurgGroups);
byId("burgType").on("change", changeType);
byId("burgCulture").on("change", changeCulture);
byId("burgNameReCulture").on("click", generateNameCulture);
byId("burgPopulation").on("change", changePopulation);
burgBody.querySelectorAll(".burgFeature").forEach(el => el.on("click", toggleFeature));
byId("burgLinkOpen").on("click", openBurgLink);
byId("burgStyleShow").on("click", showStyleSection);
byId("burgStyleHide").on("click", hideStyleSection);
byId("burgEditLabelStyle").on("click", editGroupLabelStyle);
byId("burgEditIconStyle").on("click", editGroupIconStyle);
byId("burgEditAnchorStyle").on("click", editGroupAnchorStyle);
byId("burgEmblem").on("click", openEmblemEdit);
byId("burgSetPreviewLink").on("click", setCustomPreview);
byId("burgEditEmblem").on("click", openEmblemEdit);
byId("burgLocate").on("click", zoomIntoBurg);
byId("burgRelocate").on("click", toggleRelocateBurg);
byId("burglLegend").on("click", editBurgLegend);
byId("burgLock").on("click", toggleBurgLockButton);
byId("burgRemove").on("click", removeSelectedBurg);
byId("burgTemperatureGraph").on("click", showTemperatureGraph);
function updateGroupsList() {
byId("burgGroup").options.length = 0; // remove all options
for (const {name} of options.burgs.groups) {
byId("burgGroup").options.add(new Option(name, name));
}
}
function updateBurgValues() {
const id = +elSelected.attr("data-id");
const b = pack.burgs[id];
const province = pack.cells.province[b.cell];
const provinceName = province ? pack.provinces[province].fullName + ", " : "";
const stateName = pack.states[b.state].fullName || pack.states[b.state].name;
byId("burgProvinceAndState").innerHTML = provinceName + stateName;
byId("burgName").value = b.name;
byId("burgGroup").value = b.group;
byId("burgType").value = b.type || "Generic";
byId("burgPopulation").value = rn(b.population * populationRate * urbanization);
byId("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
// update list and select culture
const cultureSelect = byId("burgCulture");
cultureSelect.options.length = 0;
const cultures = pack.cultures.filter(c => !c.removed);
cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture)));
const temperature = grid.cells.temp[pack.cells.g[b.cell]];
byId("burgTemperature").innerHTML = convertTemperature(temperature);
byId("burgTemperatureLikeIn").dataset.tip =
"Average yearly temperature is like in " + getTemperatureLikeness(temperature);
byId("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
// toggle features
byId("burgCapital").classList.toggle("inactive", !b.capital);
byId("burgPort").classList.toggle("inactive", !b.port);
byId("burgCitadel").classList.toggle("inactive", !b.citadel);
byId("burgWalls").classList.toggle("inactive", !b.walls);
byId("burgPlaza").classList.toggle("inactive", !b.plaza);
byId("burgTemple").classList.toggle("inactive", !b.temple);
byId("burgShanty").classList.toggle("inactive", !b.shanty);
updateBurgLockIcon();
// set emlem image
const coaID = "burgCOA" + id;
COArenderer.trigger(coaID, b.coa);
byId("burgEmblem").setAttribute("href", "#" + coaID);
updateBurgPreview(b);
}
function dragBurgLabel() {
const tr = parseTransform(this.getAttribute("transform"));
const dx = +tr[0] - d3.event.x,
dy = +tr[1] - d3.event.y;
d3.event.on("drag", function () {
const x = d3.event.x,
y = d3.event.y;
this.setAttribute("transform", `translate(${dx + x},${dy + y})`);
tip('Use dragging for fine-tuning only, to actually move burg use "Relocate" button', false, "warning");
});
}
function changeName() {
const id = +elSelected.attr("data-id");
pack.burgs[id].name = burgName.value;
elSelected.text(burgName.value);
}
function generateNameRandom() {
const base = rand(nameBases.length - 1);
burgName.value = Names.getBase(base);
changeName();
}
function changeGroup() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
Burgs.changeGroup(burg, this.value);
}
function changeType() {
const id = +elSelected.attr("data-id");
pack.burgs[id].type = this.value;
}
function changeCulture() {
const id = +elSelected.attr("data-id");
pack.burgs[id].culture = +this.value;
}
function generateNameCulture() {
const id = +elSelected.attr("data-id");
const culture = pack.burgs[id].culture;
burgName.value = Names.getCulture(culture);
changeName();
}
function changePopulation() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
pack.burgs[id].population = rn(burgPopulation.value / populationRate / urbanization, 4);
updateBurgPreview(burg);
}
function toggleFeature() {
const burgId = +elSelected.attr("data-id");
const burg = pack.burgs[burgId];
const feature = this.dataset.feature;
const value = Number(this.classList.contains("inactive"));
if (feature === "port") togglePort(burgId);
else if (feature === "capital") toggleCapital(burgId);
else burg[feature] = value;
this.classList.toggle("inactive", !burg[feature]);
byId("burgEditAnchorStyle").style.display = burg.port ? "inline-block" : "none";
updateBurgPreview(burg);
}
function togglePort(burgId) {
const burg = pack.burgs[burgId];
if (burg.port) {
burg.port = 0;
const anchor = document.querySelector("#anchors [data-id='" + burgId + "']");
if (anchor) anchor.remove();
} else {
const haven = pack.cells.haven[burg.cell];
if (!haven) tip("Port haven is not found, system won't be able to make a searoute", false, "warn");
const portFeature = haven ? pack.cells.f[haven] : -1;
burg.port = portFeature;
anchors
.select("#" + burg.group)
.append("use")
.attr("href", "#icon-anchor")
.attr("id", "anchor" + burg.i)
.attr("data-id", burg.i)
.attr("x", burg.x)
.attr("y", burg.y);
}
}
function toggleCapital(burgId) {
const {burgs, states} = pack;
if (burgs[burgId].capital)
return tip("To change capital please assign a capital status to another burg of this state", false, "error");
const stateId = burgs[burgId].state;
if (!stateId) return tip("Neutral lands cannot have a capital", false, "error");
const oldCapitalId = states[stateId].capital;
states[stateId].capital = burgId;
states[stateId].center = burgs[burgId].cell;
const capital = burgs[burgId];
capital.capital = 1;
Burgs.changeGroup(capital);
const oldCapital = burgs[oldCapitalId];
oldCapital.capital = 0;
Burgs.changeGroup(oldCapital);
}
function toggleBurgLockButton() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
burg.lock = !burg.lock;
updateBurgLockIcon();
}
function updateBurgLockIcon() {
const id = +elSelected.attr("data-id");
const b = pack.burgs[id];
if (b.lock) {
byId("burgLock").classList.remove("icon-lock-open");
byId("burgLock").classList.add("icon-lock");
} else {
byId("burgLock").classList.remove("icon-lock");
byId("burgLock").classList.add("icon-lock-open");
}
}
function showStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
byId("burgStyleSection").style.display = "inline-block";
}
function hideStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
byId("burgStyleSection").style.display = "none";
}
function editGroupLabelStyle() {
const g = elSelected.node().parentNode.id;
editStyle("labels", g);
}
function editGroupIconStyle() {
const g = elSelected.node().parentNode.id;
editStyle("burgIcons", g);
}
function editGroupAnchorStyle() {
const g = elSelected.node().parentNode.id;
editStyle("anchors", g);
}
function updateBurgPreview(burg) {
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 = preview;
container.insertBefore(object, null);
}
function openBurgLink() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
const link = Burgs.getPreview(burg).link;
if (link) openURL(link);
}
function setCustomPreview() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
prompt(
"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;
updateBurgPreview(burg);
}
);
}
function openEmblemEdit() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
editEmblem("burg", "burgCOA" + id, burg);
}
function zoomIntoBurg() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
const x = burg.x;
const y = burg.y;
zoomTo(x, y, 8, 2000);
}
function toggleRelocateBurg() {
const toggler = byId("toggleCells");
byId("burgRelocate").classList.toggle("pressed");
if (byId("burgRelocate").classList.contains("pressed")) {
viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick);
tip("Click on map to relocate burg. Hold Shift for continuous move", true);
if (!layerIsOn("toggleCells")) {
toggleCells();
toggler.dataset.forced = true;
}
} else {
clearMainTip();
viewbox.on("click", clicked).style("cursor", "default");
if (layerIsOn("toggleCells") && toggler.dataset.forced) {
toggleCells();
toggler.dataset.forced = false;
}
}
}
function relocateBurgOnClick() {
const cells = pack.cells;
const point = d3.mouse(this);
const cellId = findCell(...point);
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
if (cells.h[cellId] < 20) return tip("Cannot place burg into the water! Select a land cell", false, "error");
if (cells.burg[cellId] && cells.burg[cellId] !== id)
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
const newState = cells.state[cellId];
const oldState = burg.state;
if (newState !== oldState && burg.capital)
return tip("Capital cannot be relocated into another state!", false, "error");
// change UI
const x = rn(point[0], 2);
const y = rn(point[1], 2);
burgIcons.select(`#burg${id}`).attr("x", x).attr("y", y);
burgLabels.select(`#burgLabel${id}`).attr("transform", null).attr("x", x).attr("y", y);
const anchor = anchors.select("use[data-id='" + id + "']");
if (anchor.size()) {
const size = anchor.attr("width");
const xa = rn(x - size * 0.47, 2);
const ya = rn(y - size * 0.47, 2);
anchor.attr("transform", null).attr("x", xa).attr("y", ya);
}
// change data
cells.burg[burg.cell] = 0;
cells.burg[cellId] = id;
burg.cell = cellId;
burg.state = newState;
burg.x = x;
burg.y = y;
if (burg.capital) pack.states[newState].center = burg.cell;
if (d3.event.shiftKey === false) toggleRelocateBurg();
}
function editBurgLegend() {
const id = elSelected.attr("data-id");
const name = elSelected.text();
editNotes("burg" + id, name);
}
function showTemperatureGraph() {
const id = elSelected.attr("data-id");
showBurgTemperatureGraph(id);
}
function removeSelectedBurg() {
const burgId = +elSelected.attr("data-id");
const burg = pack.burgs[burgId];
if (burg.capital) {
alertMessage.innerHTML = /* html */ `You cannot remove the capital. You must change the state capital first`;
$("#alert").dialog({
resizable: false,
title: "Remove burg",
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
});
} else {
confirmationDialog({
title: "Remove burg",
message: "Are you sure you want to remove the burg? <br>This action cannot be reverted",
confirm: "Remove",
onConfirm: () => {
Burgs.remove(burgId);
$("#burgEditor").dialog("close");
}
});
}
}
function closeBurgEditor() {
byId("burgRelocate").classList.remove("pressed");
burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false);
unselect();
}
}
// in °C, array from -1 °C; source: https://en.wikipedia.org/wiki/List_of_city_by_average_temperature
const meanTempCityMap = {
"-5": "Snag (Yukon)",
"-4": "Yellowknife (Canada)",
"-3": "Okhotsk (Russia)",
"-2": "Fairbanks (Alaska)",
"-1": "Nuuk (Greenland)",
0: "Murmansk (Russia)",
1: "Arkhangelsk (Russia)",
2: "Anchorage (Alaska)",
3: "Tromsø (Norway)",
4: "Reykjavik (Iceland)",
5: "Harbin (China)",
6: "Stockholm (Sweden)",
7: "Montreal (Canada)",
8: "Prague (Czechia)",
9: "Copenhagen (Denmark)",
10: "London (England)",
11: "Antwerp (Belgium)",
12: "Paris (France)",
13: "Milan (Italy)",
14: "Washington (D.C.)",
15: "Rome (Italy)",
16: "Dubrovnik (Croatia)",
17: "Lisbon (Portugal)",
18: "Barcelona (Spain)",
19: "Marrakesh (Morocco)",
20: "Alexandria (Egypt)",
21: "Tegucigalpa (Honduras)",
22: "Guangzhou (China)",
23: "Rio de Janeiro (Brazil)",
24: "Dakar (Senegal)",
25: "Miami (USA)",
26: "Jakarta (Indonesia)",
27: "Mogadishu (Somalia)",
28: "Bangkok (Thailand)",
29: "Niamey (Niger)",
30: "Khartoum (Sudan)"
};
function getTemperatureLikeness(temperature) {
if (temperature < -5) return "Yakutsk (Russia)";
if (temperature > 30) return "Mecca (Saudi Arabia)";
return meanTempCityMap[temperature] || null;
}