feature(hierarchy tree): dynamically load, control dialog

This commit is contained in:
Azgaar 2022-06-12 00:22:39 +03:00
parent b9a578e0be
commit 6e5e570c90
8 changed files with 639 additions and 573 deletions

View file

@ -32,7 +32,7 @@ function clicked() {
else if (grand.id === "coastline") editCoastline();
else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) {
const node = document.getElementById("island_" + pack.cells.f[i]);
const node = byId("island_" + pack.cells.f[i]);
editCoastline(node);
} else if (grand.id === "lakes") editLake();
}
@ -58,10 +58,10 @@ function closeDialogs(except = "#except") {
// move brush radius circle
function moveCircle(x, y, r = 20) {
let circle = document.getElementById("brushCircle");
let circle = byId("brushCircle");
if (!circle) {
const html = /* html */ `<circle id="brushCircle" cx=${x} cy=${y} r=${r}></circle>`;
document.getElementById("debug").insertAdjacentHTML("afterBegin", html);
byId("debug").insertAdjacentHTML("afterBegin", html);
} else {
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
@ -70,7 +70,7 @@ function moveCircle(x, y, r = 20) {
}
function removeCircle() {
if (document.getElementById("brushCircle")) document.getElementById("brushCircle").remove();
if (byId("brushCircle")) byId("brushCircle").remove();
}
// get browser-defined fit-content
@ -79,8 +79,8 @@ function fitContent() {
}
// apply sorting behaviour for lines on Editor header click
document.querySelectorAll(".sortable").forEach(function (e) {
e.addEventListener("click", function () {
document.querySelectorAll(".sortable").forEach(function (event) {
event.on("click", function () {
sortLines(this);
});
});
@ -90,7 +90,7 @@ function applySortingByHeader(headerContainer) {
.getElementById(headerContainer)
.querySelectorAll(".sortable")
.forEach(function (element) {
element.addEventListener("click", function () {
element.on("click", function () {
sortLines(this);
});
});
@ -235,7 +235,7 @@ function removeBurg(id) {
if (burg.coa) {
const coaId = "burgCOA" + id;
if (document.getElementById(coaId)) document.getElementById(coaId).remove();
if (byId(coaId)) byId(coaId).remove();
emblems.select(`#burgEmblems > use[data-i='${id}']`).remove();
delete burg.coa; // remove to save data
}
@ -629,7 +629,7 @@ function createPicker() {
}
function updateSelectedRect(fill) {
document.getElementById("picker").querySelector("rect.selected").classList.remove("selected");
byId("picker").querySelector("rect.selected").classList.remove("selected");
document
.getElementById("picker")
.querySelector("rect[fill='" + fill.toLowerCase() + "']")
@ -687,7 +687,7 @@ function openPicker(fill, callback) {
updateSelectedRect(fill);
openPicker.updateFill = function () {
const selected = document.getElementById("picker").querySelector("rect.selected");
const selected = byId("picker").querySelector("rect.selected");
if (!selected) return;
callback(selected.getAttribute("fill"));
};
@ -888,8 +888,8 @@ function selectIcon(initial, callback) {
if (!callback) return;
$("#iconSelector").dialog();
const table = document.getElementById("iconTable");
const input = document.getElementById("iconInput");
const table = byId("iconTable");
const input = byId("iconInput");
input.value = initial;
if (!table.innerHTML) {
@ -1119,13 +1119,11 @@ function selectIcon(initial, callback) {
}
function getAreaUnit(squareMark = "²") {
return document.getElementById("areaUnit").value === "square"
? document.getElementById("distanceUnitInput").value + squareMark
: document.getElementById("areaUnit").value;
return byId("areaUnit").value === "square" ? byId("distanceUnitInput").value + squareMark : byId("areaUnit").value;
}
function getArea(rawArea) {
const distanceScale = document.getElementById("distanceScaleInput")?.value;
const distanceScale = byId("distanceScaleInput")?.value;
return rawArea * distanceScale ** 2;
}
@ -1150,26 +1148,26 @@ function confirmationDialog(options) {
}
};
document.getElementById("alertMessage").innerHTML = message;
byId("alertMessage").innerHTML = message;
$("#alert").dialog({resizable: false, title, buttons});
}
// add and register event listeners to clean up on editor closure
function listen(element, event, handler) {
element.addEventListener(event, handler);
element.on(event, handler);
return () => element.removeEventListener(event, handler);
}
// Calls the refresh functionality on all editors currently open.
function refreshAllEditors() {
TIME && console.time("refreshAllEditors");
if (document.getElementById("culturesEditorRefresh")?.offsetParent) culturesEditorRefresh.click();
if (document.getElementById("biomesEditorRefresh")?.offsetParent) biomesEditorRefresh.click();
if (document.getElementById("diplomacyEditorRefresh")?.offsetParent) diplomacyEditorRefresh.click();
if (document.getElementById("provincesEditorRefresh")?.offsetParent) provincesEditorRefresh.click();
if (document.getElementById("religionsEditorRefresh")?.offsetParent) religionsEditorRefresh.click();
if (document.getElementById("statesEditorRefresh")?.offsetParent) statesEditorRefresh.click();
if (document.getElementById("zonesEditorRefresh")?.offsetParent) zonesEditorRefresh.click();
if (byId("culturesEditorRefresh")?.offsetParent) culturesEditorRefresh.click();
if (byId("biomesEditorRefresh")?.offsetParent) biomesEditorRefresh.click();
if (byId("diplomacyEditorRefresh")?.offsetParent) diplomacyEditorRefresh.click();
if (byId("provincesEditorRefresh")?.offsetParent) provincesEditorRefresh.click();
if (byId("religionsEditorRefresh")?.offsetParent) religionsEditorRefresh.click();
if (byId("statesEditorRefresh")?.offsetParent) statesEditorRefresh.click();
if (byId("zonesEditorRefresh")?.offsetParent) zonesEditorRefresh.click();
TIME && console.timeEnd("refreshAllEditors");
}

View file

@ -13,7 +13,9 @@ function editRegiment(selector) {
drawBase();
$("#regimentEditor").dialog({
title: "Edit Regiment", resizable: false, close: closeEditor,
title: "Edit Regiment",
resizable: false,
close: closeEditor,
position: {my: "left top", at: "left+10 top+10", of: "#map"}
});
@ -40,17 +42,19 @@ function editRegiment(selector) {
}
function updateRegimentData(regiment) {
document.getElementById("regimentType").className = regiment.n ? "icon-anchor" :"icon-users";
document.getElementById("regimentType").className = regiment.n ? "icon-anchor" : "icon-users";
document.getElementById("regimentName").value = regiment.name;
document.getElementById("regimentEmblem").value = regiment.icon;
const composition = document.getElementById("regimentComposition");
composition.innerHTML = options.military.map(u => {
return `<div data-tip="${capitalize(u.name)} number. Input to change">
composition.innerHTML = options.military
.map(u => {
return `<div data-tip="${capitalize(u.name)} number. Input to change">
<div class="label">${capitalize(u.name)}:</div>
<input data-u="${u.name}" type="number" min=0 step=1 value="${(regiment.u[u.name]||0)}">
<i>${u.type}</i></div>`
}).join("");
<input data-u="${u.name}" type="number" min=0 step=1 value="${regiment.u[u.name] || 0}">
<i>${u.type}</i></div>`;
})
.join("");
composition.querySelectorAll("input").forEach(el => el.addEventListener("change", changeUnit));
}
@ -58,26 +62,49 @@ function editRegiment(selector) {
function drawBase() {
const reg = regiment();
const clr = pack.states[elSelected.dataset.state].color;
const base = viewbox.insert("g", "g#armies").attr("id", "regimentBase").attr("stroke-width", .3).attr("stroke", "#000").attr("cursor", "move");
base.on("mouseenter", () => {tip("Regiment base. Drag to re-base the regiment", true);}).on("mouseleave", () => {tip('', true);});
const base = viewbox
.insert("g", "g#armies")
.attr("id", "regimentBase")
.attr("stroke-width", 0.3)
.attr("stroke", "#000")
.attr("cursor", "move");
base
.on("mouseenter", () => {
tip("Regiment base. Drag to re-base the regiment", true);
})
.on("mouseleave", () => {
tip("", true);
});
base.append("line").attr("x1", reg.bx).attr("y1", reg.by).attr("x2", reg.x).attr("y2", reg.y).attr("class", "dragLine");
base.append("circle").attr("cx", reg.bx).attr("cy", reg.by).attr("r", 2).attr("fill", clr).call(d3.drag().on("drag", dragBase));
base
.append("line")
.attr("x1", reg.bx)
.attr("y1", reg.by)
.attr("x2", reg.x)
.attr("y2", reg.y)
.attr("class", "regimentDragLine");
base
.append("circle")
.attr("cx", reg.bx)
.attr("cy", reg.by)
.attr("r", 2)
.attr("fill", clr)
.call(d3.drag().on("drag", dragBase));
}
function changeType() {
const reg = regiment();
reg.n = +!reg.n;
document.getElementById("regimentType").className = reg.n ? "icon-anchor" :"icon-users";
document.getElementById("regimentType").className = reg.n ? "icon-anchor" : "icon-users";
const size = +armies.attr("box-size");
const baseRect = elSelected.querySelectorAll("rect")[0];
const iconRect = elSelected.querySelectorAll("rect")[1];
const icon = elSelected.querySelector(".regimentIcon");
const x = reg.n ? reg.x-size*2 : reg.x-size*3;
const x = reg.n ? reg.x - size * 2 : reg.x - size * 3;
baseRect.setAttribute("x", x);
baseRect.setAttribute("width", reg.n ? size*4 : size*6);
iconRect.setAttribute("x", x - size*2);
baseRect.setAttribute("width", reg.n ? size * 4 : size * 6);
iconRect.setAttribute("x", x - size * 2);
icon.setAttribute("x", x - size);
elSelected.querySelector("text").innerHTML = Military.getTotal(reg);
}
@ -87,13 +114,17 @@ function editRegiment(selector) {
}
function restoreName() {
const reg = regiment(), regs = pack.states[elSelected.dataset.state].military;
const reg = regiment(),
regs = pack.states[elSelected.dataset.state].military;
const name = Military.getName(reg, regs);
elSelected.dataset.name = reg.name = document.getElementById("regimentName").value = name;
}
function selectEmblem() {
selectIcon(regimentEmblem.value, v => {regimentEmblem.value = v; changeEmblem()});
selectIcon(regimentEmblem.value, v => {
regimentEmblem.value = v;
changeEmblem();
});
}
function changeEmblem() {
@ -104,7 +135,7 @@ function editRegiment(selector) {
function changeUnit() {
const u = this.dataset.u;
const reg = regiment();
reg.u[u] = (+this.value)||0;
reg.u[u] = +this.value || 0;
reg.a = d3.sum(Object.values(reg.u));
elSelected.querySelector("text").innerHTML = Military.getTotal(reg);
if (militaryOverviewRefresh.offsetParent) militaryOverviewRefresh.click();
@ -112,24 +143,47 @@ function editRegiment(selector) {
}
function splitRegiment() {
const reg = regiment(), u1 = reg.u;
const state = +elSelected.dataset.state, military = pack.states[state].military;
const i = last(military).i + 1, u2 = Object.assign({}, u1); // u clone
const reg = regiment(),
u1 = reg.u;
const state = +elSelected.dataset.state,
military = pack.states[state].military;
const i = last(military).i + 1,
u2 = Object.assign({}, u1); // u clone
Object.keys(u2).forEach(u => u2[u] = Math.floor(u2[u]/2)); // halved new reg
Object.keys(u2).forEach(u => (u2[u] = Math.floor(u2[u] / 2))); // halved new reg
const a = d3.sum(Object.values(u2)); // new reg total
if (!a) {tip("Not enough forces to split", false, "error"); return}; // nothing to add
if (!a) {
tip("Not enough forces to split", false, "error");
return;
} // nothing to add
// update old regiment
Object.keys(u1).forEach(u => u1[u] = Math.ceil(u1[u]/2)); // halved old reg
Object.keys(u1).forEach(u => (u1[u] = Math.ceil(u1[u] / 2))); // halved old reg
reg.a = d3.sum(Object.values(u1)); // old reg total
regimentComposition.querySelectorAll("input").forEach(el => el.value = reg.u[el.dataset.u]||0);
regimentComposition.querySelectorAll("input").forEach(el => (el.value = reg.u[el.dataset.u] || 0));
elSelected.querySelector("text").innerHTML = Military.getTotal(reg);
// create new regiment
const shift = +armies.attr("box-size") * 2;
const y = function(x, y) {do {y+=shift} while (military.find(r => r.x === x && r.y === y)); return y;}
const newReg = {a, cell:reg.cell, i, n:reg.n, u:u2, x:reg.x, y:y(reg.x, reg.y), bx:reg.bx, by:reg.by, state, icon: reg.icon};
const y = function (x, y) {
do {
y += shift;
} while (military.find(r => r.x === x && r.y === y));
return y;
};
const newReg = {
a,
cell: reg.cell,
i,
n: reg.n,
u: u2,
x: reg.x,
y: y(reg.x, reg.y),
bx: reg.bx,
by: reg.by,
state,
icon: reg.icon
};
newReg.name = Military.getName(newReg, military);
military.push(newReg);
Military.generateNote(newReg, pack.states[state]); // add legend
@ -152,11 +206,13 @@ function editRegiment(selector) {
function addRegimentOnClick() {
const point = d3.mouse(this);
const cell = findCell(point[0], point[1]);
const x = pack.cells.p[cell][0], y = pack.cells.p[cell][1];
const state = +elSelected.dataset.state, military = pack.states[state].military;
const x = pack.cells.p[cell][0],
y = pack.cells.p[cell][1];
const state = +elSelected.dataset.state,
military = pack.states[state].military;
const i = military.length ? last(military).i + 1 : 0;
const n = +(pack.cells.h[cell] < 20); // naval or land
const reg = {a:0, cell, i, n, u:{}, x, y, bx:x, by:y, state, icon:"🛡️"};
const reg = {a: 0, cell, i, n, u: {}, x, y, bx: x, by: y, state, icon: "🛡️"};
reg.name = Military.getName(reg, military);
military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend
@ -179,30 +235,59 @@ function editRegiment(selector) {
}
function attackRegimentOnClick() {
const target = d3.event.target, regSelected = target.parentElement, army = regSelected.parentElement;
const oldState = +elSelected.dataset.state, newState = +regSelected.dataset.state;
const target = d3.event.target,
regSelected = target.parentElement,
army = regSelected.parentElement;
const oldState = +elSelected.dataset.state,
newState = +regSelected.dataset.state;
if (army.parentElement.id !== "armies") {tip("Please click on a regiment to attack", false, "error"); return;}
if (regSelected === elSelected) {tip("Regiment cannot attack itself", false, "error"); return;}
if (oldState === newState) {tip("Cannot attack fraternal regiment", false, "error"); return;}
if (army.parentElement.id !== "armies") {
tip("Please click on a regiment to attack", false, "error");
return;
}
if (regSelected === elSelected) {
tip("Regiment cannot attack itself", false, "error");
return;
}
if (oldState === newState) {
tip("Cannot attack fraternal regiment", false, "error");
return;
}
const attacker = regiment();
const defender = pack.states[regSelected.dataset.state].military.find(r => r.i == regSelected.dataset.id);
if (!attacker.a || !defender.a) {tip("Regiment has no troops to battle", false, "error"); return;}
if (!attacker.a || !defender.a) {
tip("Regiment has no troops to battle", false, "error");
return;
}
// save initial position to temp attribute
attacker.px = attacker.x, attacker.py = attacker.y;
defender.px = defender.x, defender.py = defender.y;
(attacker.px = attacker.x), (attacker.py = attacker.y);
(defender.px = defender.x), (defender.py = defender.y);
// move attacker to defender
Military.moveRegiment(attacker, defender.x, defender.y-8);
Military.moveRegiment(attacker, defender.x, defender.y - 8);
// draw battle icon
const attack = d3.transition().delay(300).duration(700).ease(d3.easeSinInOut).on("end", () => new Battle(attacker, defender));
svg.append("text").attr("x", window.innerWidth/2).attr("y", window.innerHeight/2)
.text("⚔️").attr("font-size", 0).attr("opacity", 1)
.style("dominant-baseline", "central").style("text-anchor", "middle")
.transition(attack).attr("font-size", 1000).attr("opacity", .2).remove();
const attack = d3
.transition()
.delay(300)
.duration(700)
.ease(d3.easeSinInOut)
.on("end", () => new Battle(attacker, defender));
svg
.append("text")
.attr("x", window.innerWidth / 2)
.attr("y", window.innerHeight / 2)
.text("⚔️")
.attr("font-size", 0)
.attr("opacity", 1)
.style("dominant-baseline", "central")
.style("text-anchor", "middle")
.transition(attack)
.attr("font-size", 1000)
.attr("opacity", 0.2)
.remove();
clearMainTip();
$("#regimentEditor").dialog("close");
@ -222,18 +307,27 @@ function editRegiment(selector) {
}
function attachRegimentOnClick() {
const target = d3.event.target, regSelected = target.parentElement, army = regSelected.parentElement;
const oldState = +elSelected.dataset.state, newState = +regSelected.dataset.state;
const target = d3.event.target,
regSelected = target.parentElement,
army = regSelected.parentElement;
const oldState = +elSelected.dataset.state,
newState = +regSelected.dataset.state;
if (army.parentElement.id !== "armies") {tip("Please click on a regiment", false, "error"); return;}
if (regSelected === elSelected) {tip("Cannot attach regiment to itself. Please click on another regiment", false, "error"); return;}
if (army.parentElement.id !== "armies") {
tip("Please click on a regiment", false, "error");
return;
}
if (regSelected === elSelected) {
tip("Cannot attach regiment to itself. Please click on another regiment", false, "error");
return;
}
const reg = regiment(); // reg to be attached
const sel = pack.states[newState].military.find(r => r.i == regSelected.dataset.id); // reg to attach to
for (const unit of options.military) {
const u = unit.name;
if (reg.u[u]) sel.u[u] ? sel.u[u] += reg.u[u] : sel.u[u] = reg.u[u];
if (reg.u[u]) sel.u[u] ? (sel.u[u] += reg.u[u]) : (sel.u[u] = reg.u[u]);
}
sel.a = d3.sum(Object.values(sel.u)); // reg total
regSelected.querySelector("text").innerHTML = Military.getTotal(sel); // update selected reg total text
@ -247,7 +341,7 @@ function editRegiment(selector) {
if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click();
$("#regimentEditor").dialog("close");
editRegiment("#"+regSelected.id);
editRegiment("#" + regSelected.id);
}
function regenerateLegend() {
@ -264,9 +358,11 @@ function editRegiment(selector) {
function removeRegiment() {
alertMessage.innerHTML = "Are you sure you want to remove the regiment?";
$("#alert").dialog({resizable: false, title: "Remove regiment",
$("#alert").dialog({
resizable: false,
title: "Remove regiment",
buttons: {
Remove: function() {
Remove: function () {
$(this).dialog("close");
const military = pack.states[elSelected.dataset.state].military;
const regIndex = military.indexOf(regiment());
@ -281,7 +377,9 @@ function editRegiment(selector) {
if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click();
$("#regimentEditor").dialog("close");
},
Cancel: function() {$(this).dialog("close");}
Cancel: function () {
$(this).dialog("close");
}
}
});
}
@ -305,16 +403,17 @@ function editRegiment(selector) {
const self = elSelected === this;
const baseLine = viewbox.select("g#regimentBase > line");
d3.event.on("drag", function() {
const x = reg.x = d3.event.x, y = reg.y = d3.event.y;
d3.event.on("drag", function () {
const x = (reg.x = d3.event.x),
y = (reg.y = d3.event.y);
baseRect.setAttribute("x", x1(x));
baseRect.setAttribute("y", y1(y));
text.setAttribute("x", x);
text.setAttribute("y", y);
iconRect.setAttribute("x", x1(x)-h);
iconRect.setAttribute("x", x1(x) - h);
iconRect.setAttribute("y", y1(y));
icon.setAttribute("x", x1(x)-size);
icon.setAttribute("x", x1(x) - size);
icon.setAttribute("y", y);
if (self) baseLine.attr("x2", x).attr("y2", y);
});
@ -324,13 +423,16 @@ function editRegiment(selector) {
const baseLine = viewbox.select("g#regimentBase > line");
const reg = regiment();
d3.event.on("drag", function() {
d3.event.on("drag", function () {
this.setAttribute("cx", d3.event.x);
this.setAttribute("cy", d3.event.y);
baseLine.attr("x1", d3.event.x).attr("y1", d3.event.y);
});
d3.event.on("end", function() {reg.bx = d3.event.x; reg.by = d3.event.y;});
d3.event.on("end", function () {
reg.bx = d3.event.x;
reg.by = d3.event.y;
});
}
function closeEditor() {
@ -343,5 +445,4 @@ function editRegiment(selector) {
restoreDefaultEvents();
elSelected = null;
}
}
}