feat: zones editor - update editor

This commit is contained in:
Azgaar 2024-08-28 16:52:07 +02:00
parent 58f8a59a26
commit 40d08ccc84
7 changed files with 102 additions and 138 deletions

View file

@ -4846,7 +4846,11 @@
<div id="zonesBottom"> <div id="zonesBottom">
<button id="zonesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button> <button id="zonesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
<button id="zonesEditStyle" data-tip="Edit zones style in Style Editor" class="icon-adjust"></button> <button id="zonesEditStyle" data-tip="Edit zones style in Style Editor" class="icon-adjust"></button>
<button id="zonesLegend" data-tip="Toggle Legend box" class="icon-list-bullet"></button> <button
id="zonesLegend"
data-tip="Toggle Legend box (shows all non-hidden zones)"
class="icon-list-bullet"
></button>
<button <button
id="zonesPercentage" id="zonesPercentage"
data-tip="Toggle percentage / absolute values views" data-tip="Toggle percentage / absolute values views"

View file

@ -228,24 +228,20 @@ function editBurg(id) {
const burgsToRemove = burgsInGroup.filter(b => !(pack.burgs[b].capital || pack.burgs[b].lock)); const burgsToRemove = burgsInGroup.filter(b => !(pack.burgs[b].capital || pack.burgs[b].lock));
const capital = burgsToRemove.length < burgsInGroup.length; const capital = burgsToRemove.length < burgsInGroup.length;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${ confirmationDialog({
basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
}?
<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${
burgsToRemove.length
}`;
$("#alert").dialog({
resizable: false,
title: "Remove burg group", title: "Remove burg group",
buttons: { message: `Are you sure you want to remove ${
Remove: function () { basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
$(this).dialog("close"); }?<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${
burgsToRemove.length
}. This action cannot be reverted`,
confirm: "Remove",
onConfirm: () => {
$("#burgEditor").dialog("close"); $("#burgEditor").dialog("close");
hideGroupSection(); hideGroupSection();
burgsToRemove.forEach(b => removeBurg(b)); burgsToRemove.forEach(b => removeBurg(b));
if (!basic && !capital) { if (!basic && !capital) {
// entirely remove group
const labelG = document.querySelector("#burgLabels > #" + group.id); const labelG = document.querySelector("#burgLabels > #" + group.id);
const iconG = document.querySelector("#burgIcons > #" + group.id); const iconG = document.querySelector("#burgIcons > #" + group.id);
const anchorG = document.querySelector("#anchors > #" + group.id); const anchorG = document.querySelector("#anchors > #" + group.id);
@ -253,10 +249,6 @@ function editBurg(id) {
if (iconG) iconG.remove(); if (iconG) iconG.remove();
if (anchorG) anchorG.remove(); if (anchorG) anchorG.remove();
} }
},
Cancel: function () {
$(this).dialog("close");
}
} }
}); });
} }
@ -509,19 +501,13 @@ function editBurg(id) {
} }
}); });
} else { } else {
alertMessage.innerHTML = "Are you sure you want to remove the burg?"; confirmationDialog({
$("#alert").dialog({
resizable: false,
title: "Remove burg", title: "Remove burg",
buttons: { message: "Are you sure you want to remove the burg? <br>This action cannot be reverted",
Remove: function () { confirm: "Remove",
$(this).dialog("close"); onConfirm: () => {
removeBurg(id); // see Editors module removeBurg(id); // see Editors module
$("#burgEditor").dialog("close"); $("#burgEditor").dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
} }
}); });
} }

View file

@ -245,7 +245,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
confirmationDialog({ confirmationDialog({
title: "Remove burg", title: "Remove burg",
message: "Are you sure you want to remove the burg? This actiove cannot be reverted", message: "Are you sure you want to remove the burg? <br>This action cannot be reverted",
confirm: "Remove", confirm: "Remove",
onConfirm: () => { onConfirm: () => {
removeBurg(burg); removeBurg(burg);

View file

@ -1882,13 +1882,16 @@ function toggleZones(event) {
} }
function drawZones() { function drawZones() {
const zonesHtml = pack.zones.map(drawZone); const filterBy = byId("zonesFilterType").value;
const isFiltered = filterBy && filterBy !== "all";
const zonesHtml = pack.zones.filter(zone => !zone.hidden && (!isFiltered || zone.type === filterBy)).map(drawZone);
zones.html(zonesHtml.join("")); zones.html(zonesHtml.join(""));
} }
function drawZone({i, cells, color}) { function drawZone({i, cells, type, color}) {
const cellsPath = cells.map(cell => "M" + getPackPolygon(cell).join(" ")).join(" "); const cellsPath = cells.map(cell => "M" + getPackPolygon(cell).join(" ")).join(" ");
return `<path id="zone${i}" data-id="${i}" d="${cellsPath}" fill="${color}" />`; return `<path id="zone${i}" data-id="${i}" data-type="${type}" d="${cellsPath}" fill="${color}" />`;
} }
function toggleEmblems(event) { function toggleEmblems(event) {

View file

@ -389,20 +389,13 @@ function editRoute(id) {
} }
function removeRoute() { function removeRoute() {
alertMessage.innerHTML = "Are you sure you want to remove the route"; confirmationDialog({
$("#alert").dialog({
resizable: false,
width: "22em",
title: "Remove route", title: "Remove route",
buttons: { message: "Are you sure you want to remove the route? <br>This action cannot be reverted",
Remove: function () { confirm: "Remove",
onConfirm: () => {
Routes.remove(getRoute()); Routes.remove(getRoute());
$(this).dialog("close");
$("#routeEditor").dialog("close"); $("#routeEditor").dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
} }
}); });
} }

View file

@ -144,22 +144,14 @@ function overviewRoutes() {
function triggerRouteRemove() { function triggerRouteRemove() {
const routeId = +this.parentNode.dataset.id; const routeId = +this.parentNode.dataset.id;
confirmationDialog({
alertMessage.innerHTML = `Are you sure you want to remove the route?`;
$("#alert").dialog({
resizable: false,
width: "22em",
title: "Remove route", title: "Remove route",
buttons: { message: "Are you sure you want to remove the route? <br>This action cannot be reverted",
Remove: function () { confirm: "Remove",
onConfirm: () => {
const route = pack.routes.find(r => r.i === routeId); const route = pack.routes.find(r => r.i === routeId);
Routes.remove(route); Routes.remove(route);
routesOverviewAddLines(); routesOverviewAddLines();
$(this).dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
} }
}); });
} }

View file

@ -33,14 +33,17 @@ function editZones() {
byId("zonesRemove").on("click", toggleEraseMode); byId("zonesRemove").on("click", toggleEraseMode);
body.on("click", function (ev) { body.on("click", function (ev) {
const el = ev.target, const el = ev.target;
cl = el.classList, const cl = el.classList;
zone = el.parentNode.dataset.id; const zoneId = el.parentNode.dataset.id;
if (el.tagName === "FILL-BOX") changeFill(el); const zone = pack.zones.find(z => "zone" + z.i === zoneId);
else if (cl.contains("culturePopulation")) changePopulation(zone); if (!zone) return;
else if (cl.contains("icon-trash-empty")) zoneRemove(zone);
else if (cl.contains("icon-eye")) toggleVisibility(el); if (el.tagName === "FILL-BOX") changeFill(el, zone);
else if (cl.contains("icon-pin")) toggleFog(zone, cl); else if (cl.contains("zonePopulation")) changePopulation(zone);
else if (cl.contains("icon-trash-empty")) zoneRemove(zoneId, zone);
else if (cl.contains("icon-eye")) toggleVisibility(zone);
else if (cl.contains("icon-pin")) toggleFog(zoneId, cl);
if (customization) selectZone(el); if (customization) selectZone(el);
}); });
@ -54,9 +57,8 @@ function editZones() {
// update type filter with a list of used types // update type filter with a list of used types
function updateFilters() { function updateFilters() {
const types = unique(pack.zones.map(zone => zone.type));
const filterSelect = byId("zonesFilterType"); const filterSelect = byId("zonesFilterType");
const types = unique(pack.zones.map(zone => zone.type));
const typeToFilterBy = types.includes(zonesFilterType.value) ? zonesFilterType.value : "all"; const typeToFilterBy = types.includes(zonesFilterType.value) ? zonesFilterType.value : "all";
filterSelect.innerHTML = filterSelect.innerHTML =
@ -83,7 +85,9 @@ function editZones() {
)}; Urban population: ${si(urban)}. Click to change`; )}; Urban population: ${si(urban)}. Click to change`;
const focused = defs.select("#fog #focusZone" + i).size(); const focused = defs.select("#fog #focusZone" + i).size();
return `<div class="states" data-id="zone${i}" data-fill="${color}" data-description="${name}" return /* html */ `<div class="states" style="${
hidden ? "opacity: 0.5" : null
}" data-id="zone${i}" data-fill="${color}" data-description="${name}"
data-type="${type}" data-cells=${cells.length} data-area=${area} data-population=${population}> data-type="${type}" data-cells=${cells.length} data-area=${area} data-population=${population}>
<fill-box fill="${color}"></fill-box> <fill-box fill="${color}"></fill-box>
<input data-tip="Zone description. Click and type to change" style="width: 11em" class="zoneName" value="${name}" autocorrect="off" spellcheck="false"> <input data-tip="Zone description. Click and type to change" style="width: 11em" class="zoneName" value="${name}" autocorrect="off" spellcheck="false">
@ -93,14 +97,12 @@ function editZones() {
<span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span> <span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span>
<div data-tip="Zone area" class="biomeArea hide">${si(area) + unit}</div> <div data-tip="Zone area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span> <span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div> <div data-tip="${populationTip}" class="zonePopulation hide pointer">${si(population)}</div>
<span data-tip="Drag to raise or lower the zone" class="icon-resize-vertical hide"></span> <span data-tip="Drag to raise or lower the zone" class="icon-resize-vertical hide"></span>
<span data-tip="Toggle zone focus" class="icon-pin ${focused ? "" : " inactive"} hide ${ <span data-tip="Toggle zone focus" class="icon-pin ${focused ? "" : " inactive"} hide ${
cells.length ? "" : " placeholder" cells.length ? "" : " placeholder"
}"></span> }"></span>
<span data-tip="Toggle zone visibility" class="icon-eye ${hidden ? " inactive" : ""} hide ${ <span data-tip="Toggle zone visibility" class="icon-eye hide ${cells.length ? "" : " placeholder"}"></span>
cells.length ? "" : " placeholder"
}"></span>
<span data-tip="Remove zone" class="icon-trash-empty hide"></span> <span data-tip="Remove zone" class="icon-trash-empty hide"></span>
</div>`; </div>`;
}); });
@ -114,7 +116,7 @@ function editZones() {
(d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) *
populationRate; populationRate;
zonesFooterPopulation.dataset.population = totalPop; zonesFooterPopulation.dataset.population = totalPop;
zonesFooterNumber.innerHTML = `${filteredZones.length} of ${zones.length}`; zonesFooterNumber.innerHTML = `${filteredZones.length} of ${pack.zones.length}`;
zonesFooterCells.innerHTML = pack.cells.i.length; zonesFooterCells.innerHTML = pack.cells.i.length;
zonesFooterArea.innerHTML = si(totalArea) + unit; zonesFooterArea.innerHTML = si(totalArea) + unit;
zonesFooterPopulation.innerHTML = si(totalPop); zonesFooterPopulation.innerHTML = si(totalPop);
@ -140,15 +142,7 @@ function editZones() {
} }
function filterZonesByType() { function filterZonesByType() {
const typeToFilterBy = this.value; drawZones();
const zones = Array.from(document.querySelectorAll("#zones > g"));
for (const zone of zones) {
const type = zone.dataset.type;
const visible = typeToFilterBy === "all" || type === typeToFilterBy;
zone.style.display = visible ? "block" : "none";
}
zonesEditorAddLines(); zonesEditorAddLines();
} }
@ -313,21 +307,24 @@ function editZones() {
if (selected) selected.classList.remove("selected"); if (selected) selected.classList.remove("selected");
} }
function changeFill(el) { function changeFill(el, zone) {
const fill = el.getAttribute("fill"); const fill = el.getAttribute("fill");
const callback = newFill => { const callback = newFill => {
el.fill = newFill; el.fill = newFill;
byId(el.parentNode.dataset.id).setAttribute("fill", newFill); byId(el.parentNode.dataset.id).setAttribute("fill", newFill);
zone.color = newFill;
}; };
openPicker(fill, callback); openPicker(fill, callback);
} }
function toggleVisibility(el) { function toggleVisibility(zone) {
const zone = zones.select("#" + el.parentNode.dataset.id); const isHidden = Boolean(zone.hidden);
const inactive = zone.style("display") === "none"; if (isHidden) delete zone.hidden;
inactive ? zone.style("display", "block") : zone.style("display", "none"); else zone.hidden = true;
el.classList.toggle("inactive");
drawZones();
zonesEditorAddLines();
} }
function toggleFog(z, cl) { function toggleFog(z, cl) {
@ -347,19 +344,10 @@ function editZones() {
} }
function toggleLegend() { function toggleLegend() {
if (legend.selectAll("*").size()) { const filterBy = byId("zonesFilterType").value;
clearLegend(); const isFiltered = filterBy && filterBy !== "all";
return; const visibleZones = pack.zones.filter(zone => !zone.hidden && (!isFiltered || zone.type === filterBy));
} // hide legend const data = visibleZones.map(({i, name, color}) => ["zone" + i, color, name]);
const data = [];
zones.selectAll("g").each(function () {
const id = this.dataset.id;
const description = this.dataset.description;
const fill = this.getAttribute("fill");
data.push([id, fill, description]);
});
drawLegend("Zones", data); drawLegend("Zones", data);
} }
@ -373,8 +361,7 @@ function editZones() {
body.querySelectorAll(":scope > div").forEach(function (el) { body.querySelectorAll(":scope > div").forEach(function (el) {
el.querySelector(".stateCells").innerHTML = rn((+el.dataset.cells / totalCells) * 100, 2) + "%"; el.querySelector(".stateCells").innerHTML = rn((+el.dataset.cells / totalCells) * 100, 2) + "%";
el.querySelector(".biomeArea").innerHTML = rn((+el.dataset.area / totalArea) * 100, 2) + "%"; el.querySelector(".biomeArea").innerHTML = rn((+el.dataset.area / totalArea) * 100, 2) + "%";
el.querySelector(".culturePopulation").innerHTML = el.querySelector(".zonePopulation").innerHTML = rn((+el.dataset.population / totalPopulation) * 100, 2) + "%";
rn((+el.dataset.population / totalPopulation) * 100, 2) + "%";
}); });
} else { } else {
body.dataset.type = "absolute"; body.dataset.type = "absolute";
@ -421,22 +408,13 @@ function editZones() {
} }
function changePopulation(zone) { function changePopulation(zone) {
const dataCells = zones.select("#" + zone).attr("data-cells"); const landCells = zone.cells.filter(i => pack.cells.h[i] >= 20);
const cells = dataCells if (!landCells.length) return tip("Zone does not have any land cells, cannot change population", false, "error");
? dataCells
.split(",")
.map(i => +i)
.filter(i => pack.cells.h[i] >= 20)
: [];
if (!cells.length) {
tip("Zone does not have any land cells, cannot change population", false, "error");
return;
}
const burgs = pack.burgs.filter(b => !b.removed && cells.includes(b.cell));
const rural = rn(d3.sum(cells.map(i => pack.cells.pop[i])) * populationRate); const burgs = pack.burgs.filter(b => !b.removed && landCells.includes(b.cell));
const rural = rn(d3.sum(landCells.map(i => pack.cells.pop[i])) * populationRate);
const urban = rn( const urban = rn(
d3.sum(cells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization d3.sum(landCells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization
); );
const total = rural + urban; const total = rural + urban;
const l = n => Number(n).toLocaleString(); const l = n => Number(n).toLocaleString();
@ -478,12 +456,12 @@ function editZones() {
function applyPopulationChange() { function applyPopulationChange() {
const ruralChange = ruralPop.value / rural; const ruralChange = ruralPop.value / rural;
if (isFinite(ruralChange) && ruralChange !== 1) { if (isFinite(ruralChange) && ruralChange !== 1) {
cells.forEach(i => (pack.cells.pop[i] *= ruralChange)); landCells.forEach(i => (pack.cells.pop[i] *= ruralChange));
} }
if (!isFinite(ruralChange) && +ruralPop.value > 0) { if (!isFinite(ruralChange) && +ruralPop.value > 0) {
const points = ruralPop.value / populationRate; const points = ruralPop.value / populationRate;
const pop = rn(points / cells.length); const pop = rn(points / landCells.length);
cells.forEach(i => (pack.cells.pop[i] = pop)); landCells.forEach(i => (pack.cells.pop[i] = pop));
} }
const urbanChange = urbanPop.value / urban; const urbanChange = urbanPop.value / urban;
@ -500,9 +478,17 @@ function editZones() {
} }
} }
function zoneRemove(zone) { function zoneRemove(zoneId, zone) {
zones.select("#" + zone).remove(); confirmationDialog({
unfog("focusZone" + zone); title: "Remove zone",
message: "Are you sure you want to remove the zone? <br>This action cannot be reverted",
confirm: "Remove",
onConfirm: () => {
pack.zones = pack.zones.filter(z => z.i !== zone.i);
zones.select("#" + zoneId).remove();
unfog("focusZone" + zoneId);
zonesEditorAddLines(); zonesEditorAddLines();
} }
});
}
} }