From e4fc40e022393e9709922996798b9b345cbc05c7 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 24 Apr 2026 02:07:19 +0800 Subject: [PATCH 01/32] to do: add the actual siege --- public/modules/ui/regiment-editor.js | 90 +++++++++++++++++++++++++--- src/index.html | 1 + src/renderers/draw-burg-icons.ts | 2 +- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index d3144f499..dced0a010 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -17,7 +17,7 @@ function editRegiment(selector) { title: "Edit Regiment", resizable: false, close: closeEditor, - position: {my: "left top", at: "left+10 top+10", of: "#map"} + position: {my: "left top", at: "left+10 top+10", of: "#map" } }); if (modules.editRegiment) return; @@ -28,7 +28,8 @@ function editRegiment(selector) { byId("regimentType").addEventListener("click", changeType); byId("regimentName").addEventListener("change", changeName); byId("regimentEmblemChange").addEventListener("click", changeEmblem); - byId("regimentAttack").addEventListener("click", toggleAttack); + byId("regimentAttack").addEventListener("click", toggleRegimenAttack); + byId("burgAttack").addEventListener("click", toggleBurgAttack); byId("regimentRegenerateLegend").addEventListener("click", regenerateLegend); byId("regimentLegend").addEventListener("click", editLegend); byId("regimentSplit").addEventListener("click", splitRegiment); @@ -92,7 +93,7 @@ function editRegiment(selector) { function drawRotationControl() { const reg = getRegiment(); - const {x, width, y, height} = elSelected.getBBox(); + const { x, width, y, height } = elSelected.getBBox(); debug .append("circle") @@ -116,7 +117,7 @@ function editRegiment(selector) { const reg = getRegiment(); d3.event.on("drag", function () { - const {x, y} = d3.event; + const { x, y } = d3.event; const angle = rn(Math.atan2(y - reg.y, x - reg.x) * (180 / Math.PI), 2); elSelected.setAttribute("transform", `rotate(${angle})`); this.setAttribute("transform", `rotate(${angle})`); @@ -244,7 +245,7 @@ function editRegiment(selector) { 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 @@ -253,7 +254,7 @@ function editRegiment(selector) { toggleAdd(); } - function toggleAttack() { + function toggleRegimenAttack() { byId("regimentAttack").classList.toggle("pressed"); if (byId("regimentAttack").classList.contains("pressed")) { viewbox.style("cursor", "crosshair").on("click", attackRegimentOnClick); @@ -266,6 +267,81 @@ function editRegiment(selector) { } } + function toggleBurgAttack() { + byId("burgAttack").classList.toggle("pressed"); + if (byId("burgAttack").classList.contains("pressed")) { + viewbox.style("cursor", "crosshair").on("click", attackBurgOnClick); + tip("Click on another burg to initiate a siege", true); + armies.selectAll(":scope > g").classed("draggable", false); + } else { + clearMainTip(); + armies.selectAll(":scope > g").classed("draggable", true); + viewbox.on("click", clicked).style("cursor", "default"); + } + } + + function attackBurgOnClick() { + const target = d3.event.target, + burgSelected = target.parentElement, + burg = burgSelected.parentElement, + fraternalBurg = getRegiment().state == target.getAttribute("state"); + + // future changes might replace "icons" + //if (String(burg.parentElement.id) != "armies") { + if (String(burg.id) != "burgIcons") { + tip("Please click on a burg to siege", false, "error"); + //tip(target.getAttribute("state"), false, "error"); + return; + } + if (burgSelected === elSelected) { + tip("Regiment cannot attack itself", false, "error"); + return; + } + if (fraternalBurg) { + tip("Cannot attack fraternal burg", false, "error"); + return; + } + /* + const attacker = getRegiment(); + const defender = pack.states[burgSelected.dataset.state].military.find(r => r.i == burgSelected.dataset.id); + 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); + + // move attacker to defender + 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("text-rendering", "optimizeSpeed") + .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"); + } + function attackRegimentOnClick() { const target = d3.event.target, regSelected = target.parentElement, @@ -437,7 +513,7 @@ function editRegiment(selector) { const rotationControl = debug.select("#rotationControl"); d3.event.on("drag", function () { - const {x, y} = d3.event; + const { x, y } = d3.event; reg.x = x; reg.y = y; const x1 = rn(x - w / 2, 2); diff --git a/src/index.html b/src/index.html index 6faa14ca5..2f86752ca 100644 --- a/src/index.html +++ b/src/index.html @@ -3733,6 +3733,7 @@
+ - +
- External images + External images (Links that ends with .png, .jpg, or .svg)
Paste link to the image here: From 81a53ba5c4fbb1ef61dcf593c2627edf82ab977a Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 02:45:50 +0800 Subject: [PATCH 14/32] Refactor: changed ById to ensureEl --- public/modules/ui/battle-screen.js | 2 ++ public/modules/ui/regiment-editor.js | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index e418609f1..b82aa5b67 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -728,6 +728,8 @@ class Battle { this.updateTable("attackers"); this.updateTable("defenders"); + console.log(this.defender.phase + " Casualty: " + this.defenders.casualties + " Moral: " + defenders.morale); + // prepare for next iteration this.iteration += 1; this.selectPhase(); diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index e9418c037..20a5ac950 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -29,6 +29,7 @@ function editRegiment(selector) { ensureEl("regimentName").addEventListener("change", changeName); ensureEl("regimentEmblemChange").addEventListener("click", changeEmblem); ensureEl("regimentAttack").addEventListener("click", toggleAttack); + ensureEl("regimentBurgAttack").addEventListener("click", toggleBurgAttack); ensureEl("regimentRegenerateLegend").addEventListener("click", regenerateLegend); ensureEl("regimentLegend").addEventListener("click", editLegend); ensureEl("regimentSplit").addEventListener("click", splitRegiment); @@ -267,9 +268,10 @@ function editRegiment(selector) { } function toggleBurgAttack() { - byId("regimentAttack").classList.remove("pressed"); - byId("regimentBurgAttack").classList.toggle("pressed"); - if (byId("regimentBurgAttack").classList.contains("pressed")) { + console.log("kshafkuegfeg"); + ensureEl("regimentAttack").classList.remove("pressed"); + ensureEl("regimentBurgAttack").classList.toggle("pressed"); + if (ensureEl("regimentBurgAttack").classList.contains("pressed")) { viewbox.style("cursor", "crosshair").on("click", attackBurgOnClick); tip("Click on another burg to initiate a siege", true); armies.selectAll(":scope > g").classed("draggable", false); From f2588f02a25a9a6cc98a92ed16479f7b9290577c Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 04:04:11 +0800 Subject: [PATCH 15/32] fix: error when all regiment of a state is removed fix: nearest regiment --- public/modules/ui/battle-screen.js | 3 --- public/modules/ui/regiment-editor.js | 40 +++++++++++++++------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index b82aa5b67..32d195cac 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -1,6 +1,5 @@ "use strict"; class Battle { - constructor(attacker, defender, burg = -1) { if (customization) return; closeDialogs(".stable"); @@ -728,8 +727,6 @@ class Battle { this.updateTable("attackers"); this.updateTable("defenders"); - console.log(this.defender.phase + " Casualty: " + this.defenders.casualties + " Moral: " + defenders.morale); - // prepare for next iteration this.iteration += 1; this.selectPhase(); diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 20a5ac950..2cd074b4c 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -255,6 +255,7 @@ function editRegiment(selector) { } function toggleAttack() { + ensureEl("regimentBurgAttack").classList.remove("pressed"); ensureEl("regimentAttack").classList.toggle("pressed"); if (ensureEl("regimentAttack").classList.contains("pressed")) { viewbox.style("cursor", "crosshair").on("click", attackRegimentOnClick); @@ -268,7 +269,6 @@ function editRegiment(selector) { } function toggleBurgAttack() { - console.log("kshafkuegfeg"); ensureEl("regimentAttack").classList.remove("pressed"); ensureEl("regimentBurgAttack").classList.toggle("pressed"); if (ensureEl("regimentBurgAttack").classList.contains("pressed")) { @@ -312,35 +312,36 @@ function editRegiment(selector) { tip("Cannot attack fraternal burg", false, "error"); return; } + if (pack.states[pack.burgs[burgId].state].military.length == 0) { + tip("Burg's state has no military", false, "error"); + return; + } const attacker = getRegiment(); const burgStateId = Number(pack.burgs[burgId].state); const burgStateRegiments = pack.states[burgStateId].military; const defendingBurg = target; - let closestRegimentIndex = 0; - let closestRegiment = -1; + const bx = +defendingBurg.getAttribute("x"); + const by = +defendingBurg.getAttribute("y"); - // Find closest regiment - burgStateRegiments.forEach((element, index) => { - if (element.t != 0 && element.a) { - const defendingRegimentX = element.x; - const defendingRegimentY = element.y; + let closestIndex = -1; + let closestDistance = Infinity; - const distance = Math.sqrt((defendingRegimentX - defendingBurg.getAttribute("x")) ** 2 + (defendingRegimentY - defendingBurg.getAttribute("y")) ** 2) + burgStateRegiments.forEach((el, i) => { + if (el.t === 0 || !el.a) return; - if (closestRegiment == -1) { - closestRegiment = distance; - } - if (closestRegiment > distance) { - closestRegiment = distance; - closestRegimentIndex = index; - } + const dx = el.x - bx; + const dy = el.y - by; + const dist = Math.sqrt(dx * dx + dy * dy); + + if (dist < closestDistance) { + closestDistance = dist; + closestIndex = i; } }); - // Note: could've straight up used 'element' instead of doing this long line - // but there's lots of bugs when 'element' is used - const defendingRegiment = pack.states[pack.burgs[burgId].state].military[closestRegimentIndex]; + + const defendingRegiment = pack.states[pack.burgs[burgId].state].military[closestIndex]; if (!attacker.a || !defendingRegiment.a) { tip("Regiment has no troops to battle", false, "error"); @@ -602,6 +603,7 @@ function editRegiment(selector) { armies.selectAll("g>g").call(d3.drag().on("drag", null)); ensureEl("regimentAdd").classList.remove("pressed"); ensureEl("regimentAttack").classList.remove("pressed"); + ensureEl("regimentBurgAttack").classList.remove("pressed"); ensureEl("regimentAttach").classList.remove("pressed"); restoreDefaultEvents(); elSelected = null; From 577935c648112f5cd55891e1f465360bb2c2f1a0 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 04:15:53 +0800 Subject: [PATCH 16/32] fix: last regiment that is depleted causes error when a foreign regiment attempts to initiate another siege --- public/modules/ui/regiment-editor.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 2cd074b4c..c1ec4cef2 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -341,8 +341,11 @@ function editRegiment(selector) { } }); - const defendingRegiment = pack.states[pack.burgs[burgId].state].military[closestIndex]; + if (closestIndex == -1 && pack.states[pack.burgs[burgId].state].military.length == 1) + closestIndex = 0; + const defendingRegiment = pack.states[pack.burgs[burgId].state].military[closestIndex]; + console.log(pack.states[pack.burgs[burgId].state].military.length); if (!attacker.a || !defendingRegiment.a) { tip("Regiment has no troops to battle", false, "error"); return; From cb0098f3e10a2666bf6c12c4dbc456cc761a0786 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 04:17:05 +0800 Subject: [PATCH 17/32] refactor: removed debug lines --- public/modules/ui/regiment-editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index c1ec4cef2..43fd7c5db 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -345,7 +345,6 @@ function editRegiment(selector) { closestIndex = 0; const defendingRegiment = pack.states[pack.burgs[burgId].state].military[closestIndex]; - console.log(pack.states[pack.burgs[burgId].state].military.length); if (!attacker.a || !defendingRegiment.a) { tip("Regiment has no troops to battle", false, "error"); return; From 434e4624728ec71974c3845aa6b4a82f7377df93 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 04:27:16 +0800 Subject: [PATCH 18/32] refactor: inline boolean eval fix: force the type to "siege" to prevent occations where defending regiments veers off to the burg's cell --- public/modules/ui/battle-screen.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index 32d195cac..cc1e50644 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -13,11 +13,9 @@ class Battle { this.attackers = { regiments: [], distances: [], morale: 100, casualties: 0, power: 0 }; this.defenders = { regiments: [], distances: [], morale: 100, casualties: 0, power: 0 }; - this.hasCastle = false; - if (burg != -1) - { - this.hasCastle = Boolean(pack.burgs[burg].citadel); - } + this.hasCastle = (burg != -1) ? Boolean(pack.burgs[burg].citadel) : false; + this.isSiege = burg != -1; + this.addHeaders(); this.addRegiment("attackers", attacker); this.addRegiment("defenders", defender); @@ -69,6 +67,7 @@ class Battle { } defineType() { + const attacker = this.attackers.regiments[0]; const defender = this.defenders.regiments[0]; const getType = () => { @@ -78,7 +77,7 @@ class Battle { if (attacker.n && defender.n) return "naval"; // attacker and defender are navals if (typesA.every(t => t === "aviation") && typesD.every(t => t === "aviation")) return "air"; // if attackers and defender have only aviation units if (attacker.n && !defender.n && typesA.some(t => t !== "naval")) return "landing"; // if attacked is naval with non-naval units and defender is not naval - if (!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) return "siege"; // defender is in walled town + if ((!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) || this.isSiege) return "siege"; // defender is in walled town if (P(0.1) && [5, 6, 7, 8, 9, 12].includes(pack.cells.biome[this.cell])) return "ambush"; // 20% if defenders are in forest or marshes return "field"; }; From f334e7d829828e33763ba7ba8b3fb4cdb86d0fe6 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 05:47:49 +0800 Subject: [PATCH 19/32] refactor: discarded third param in Battle class refactor: putting back some white spaces and layout to its default --- public/modules/ui/battle-screen.js | 154 ++++++++++++++------------- public/modules/ui/regiment-editor.js | 10 +- 2 files changed, 81 insertions(+), 83 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index cc1e50644..b1345fa90 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -1,6 +1,6 @@ "use strict"; class Battle { - constructor(attacker, defender, burg = -1) { + constructor(attacker, defender) { if (customization) return; closeDialogs(".stable"); customization = 13; // enter customization to avoid unwanted dialog closing @@ -10,11 +10,8 @@ class Battle { this.x = defender.x; this.y = defender.y; this.cell = findCell(this.x, this.y); - this.attackers = { regiments: [], distances: [], morale: 100, casualties: 0, power: 0 }; - this.defenders = { regiments: [], distances: [], morale: 100, casualties: 0, power: 0 }; - - this.hasCastle = (burg != -1) ? Boolean(pack.burgs[burg].citadel) : false; - this.isSiege = burg != -1; + this.attackers = {regiments: [], distances: [], morale: 100, casualties: 0, power: 0}; + this.defenders = {regiments: [], distances: [], morale: 100, casualties: 0, power: 0}; this.addHeaders(); this.addRegiment("attackers", attacker); @@ -67,7 +64,6 @@ class Battle { } defineType() { - const attacker = this.attackers.regiments[0]; const defender = this.defenders.regiments[0]; const getType = () => { @@ -77,7 +73,7 @@ class Battle { if (attacker.n && defender.n) return "naval"; // attacker and defender are navals if (typesA.every(t => t === "aviation") && typesD.every(t => t === "aviation")) return "air"; // if attackers and defender have only aviation units if (attacker.n && !defender.n && typesA.some(t => t !== "naval")) return "landing"; // if attacked is naval with non-naval units and defender is not naval - if ((!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) || this.isSiege) return "siege"; // defender is in walled town + if ((!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) || pack.burgs[pack.cells.burg[this.cell]].citadel) return "siege"; // defender is in walled town if (P(0.1) && [5, 6, 7, 8, 9, 12].includes(pack.cells.biome[this.cell])) return "ambush"; // 20% if defenders are in forest or marshes return "field"; }; @@ -160,7 +156,8 @@ class Battle { ${iconHtml}`; const body = ``; - let initial = `${icon}${icon}${regiment.name.slice(0, 24)}`; let casualties = `${state.fullName.slice( 0, @@ -169,16 +166,20 @@ class Battle { let survivors = `Distance to base: ${distance} ${distanceUnitInput.value}`; for (const u of options.military) { - initial += `${regiment.u[u.name] || 0 + initial += `${ + regiment.u[u.name] || 0 }`; casualties += `0`; - survivors += `${regiment.u[u.name] || 0 + survivors += `${ + regiment.u[u.name] || 0 }`; } - initial += `${regiment.a || 0}`; + initial += `${ + regiment.a || 0}`; casualties += `0`; - survivors += `${regiment.a || 0 + survivors += `${ + regiment.a || 0 }`; const div = side === "attackers" ? battleAttackers : battleDefenders; @@ -312,6 +313,7 @@ class Battle { } calculateStrength(side) { + const hascastle = pack.burgs[pack.cells.burg[this.cell]].citadel; const scheme = { // field battle phases skirmish: { @@ -324,8 +326,8 @@ class Battle { aviation: 1.8, magical: 1.8 }, // ranged excel - melee: { melee: 2, ranged: 1.2, mounted: 1.5, machinery: 0.5, naval: 0.2, armored: 2, aviation: 0.8, magical: 0.8 }, // melee excel - pursue: { melee: 1, ranged: 1, mounted: 4, machinery: 0.05, naval: 1, armored: 1, aviation: 1.5, magical: 0.6 }, // mounted excel + melee: {melee: 2, ranged: 1.2, mounted: 1.5, machinery: 0.5, naval: 0.2, armored: 2, aviation: 0.8, magical: 0.8}, // melee excel + pursue: {melee: 1, ranged: 1, mounted: 4, machinery: 0.05, naval: 1, armored: 1, aviation: 1.5, magical: 0.6}, // mounted excel retreat: { melee: 0.1, ranged: 0.01, @@ -338,7 +340,7 @@ class Battle { }, // reduced // naval battle phases - shelling: { melee: 0, ranged: 0.2, mounted: 0, machinery: 2, naval: 2, armored: 0, aviation: 0.1, magical: 0.5 }, // naval and machinery excel + shelling: {melee: 0, ranged: 0.2, mounted: 0, machinery: 2, naval: 2, armored: 0, aviation: 0.1, magical: 0.5}, // naval and machinery excel boarding: { melee: 1, ranged: 0.5, @@ -349,7 +351,7 @@ class Battle { aviation: 0, magical: 0.2 }, // melee excel - chase: { melee: 0, ranged: 0.15, mounted: 0, machinery: 1, naval: 1, armored: 0, aviation: 0.15, magical: 0.5 }, // reduced + chase: {melee: 0, ranged: 0.15, mounted: 0, machinery: 1, naval: 1, armored: 0, aviation: 0.15, magical: 0.5}, // reduced withdrawal: { melee: 0, ranged: 0.02, @@ -363,56 +365,56 @@ class Battle { // siege phases blockade: { - melee: (this.hasCastle) ? 0.08 : 0.25, - ranged: (this.hasCastle) ? 0.20 : 0.25, - mounted: (this.hasCastle) ? 0.03 : 0.2, - machinery: (this.hasCastle) ? 0.8 : 0.5, - naval: (this.hasCastle) ? 0.06 : 0.2, - armored: (this.hasCastle) ? 0.09 : 0.1, - aviation: (this.hasCastle) ? 0.25 : 0.25, - magical: (this.hasCastle) ? 0.025 : 0.25 + melee: (hascastle) ? 0.08 : 0.25, + ranged: (hascastle) ? 0.20 : 0.25, + mounted: (hascastle) ? 0.03 : 0.2, + machinery: (hascastle) ? 0.8 : 0.5, + naval: (hascastle) ? 0.06 : 0.2, + armored: (hascastle) ? 0.09 : 0.1, + aviation: (hascastle) ? 0.25 : 0.25, + magical: (hascastle) ? 0.025 : 0.25 }, // no active actions sheltering: { - melee: (this.hasCastle) ? 0.4 : 0.3, - ranged: (this.hasCastle) ? 0.8 : 0.5, - mounted: (this.hasCastle) ? 0.08 : 0.2, - machinery: (this.hasCastle) ? 0.6 : 0.5, - naval: (this.hasCastle) ? 0.01 : 0.2, - armored: (this.hasCastle) ? 0.1 : 0.1, - aviation: (this.hasCastle) ? 0.1 : 0.25, - magical: (this.hasCastle) ? 0.25 : 0.25 + melee: (hascastle) ? 0.4 : 0.3, + ranged: (hascastle) ? 0.8 : 0.5, + mounted: (hascastle) ? 0.08 : 0.2, + machinery: (hascastle) ? 0.6 : 0.5, + naval: (hascastle) ? 0.01 : 0.2, + armored: (hascastle) ? 0.1 : 0.1, + aviation: (hascastle) ? 0.1 : 0.25, + magical: (hascastle) ? 0.25 : 0.25 }, // no active actions - sortie: { melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1 }, // melee excel + sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel bombardment: { - melee: (this.hasCastle) ? 0.1 : 0.2, - ranged: (this.hasCastle) ? 0.6 : 0.5, - mounted: (this.hasCastle) ? 0.25 : 0.2, - machinery: (this.hasCastle) ? 0.4 : 3, - naval: (this.hasCastle) ? 0.1 : 1, - armored: (this.hasCastle) ? 0.5 : 0.5, - aviation: (this.hasCastle) ? 1 : 1, - magical: (this.hasCastle) ? 1 : 1 + melee: (hascastle) ? 0.1 : 0.2, + ranged: (hascastle) ? 0.6 : 0.5, + mounted: (hascastle) ? 0.25 : 0.2, + machinery: (hascastle) ? 0.4 : 3, + naval: (hascastle) ? 0.1 : 1, + armored: 0.5, + aviation: 1, + magical: 1 }, // machinery excel storming: { - melee: (this.hasCastle) ? 0.8 : 1, - ranged: (this.hasCastle) ? 0.5 : 0.6, - mounted: (this.hasCastle) ? 0.3 : 0.5, - machinery: (this.hasCastle) ? 0.5 : 1, - naval: (this.hasCastle) ? 0.01 : 0.1, - armored: (this.hasCastle) ? 0.1 : 0.1, - aviation: (this.hasCastle) ? 0.5 : 0.5, - magical: (this.hasCastle) ? 0.5 : 0.5 + melee: (hascastle) ? 0.8 : 1, + ranged: (hascastle) ? 0.5 : 0.6, + mounted: (hascastle) ? 0.3 : 0.5, + machinery: (hascastle) ? 0.5 : 1, + naval: (hascastle) ? 0.01 : 0.1, + armored: 0.1, + aviation: 0.5, + magical: 0.5 }, // melee excel - defense: { melee: 2, ranged: 3, mounted: 1, machinery: 1, naval: 0.1, armored: 1, aviation: 0.5, magical: 1 }, // ranged excel + defense: {melee: 2, ranged: 3, mounted: 1, machinery: 1, naval: 0.1, armored: 1, aviation: 0.5, magical: 1}, // ranged excel looting: { - melee: (this.hasCastle) ? 1.6 : 1.6, - ranged: (this.hasCastle) ? 1.6 : 1.6, - mounted: (this.hasCastle) ? 0.4 : 0.5, - machinery: (this.hasCastle) ? 0.1 : 0.2, - naval: (this.hasCastle) ? 0.01 : 0.02, - armored: (this.hasCastle) ? 0.2 : 0.2, - aviation: (this.hasCastle) ? 0.1 : 0.1, - magical: (this.hasCastle) ? 0.3 : 0.3 + melee: 1.6, + ranged: 1.6, + mounted: 0.5, + machinery: 0.2, + naval: 0.02, + armored: 0.2, + aviation: 0.1, + magical: 0.3 }, // melee excel surrendering: { melee: 0.1, @@ -426,7 +428,7 @@ class Battle { }, // reduced // ambush phases - surprise: { melee: 2, ranged: 2.4, mounted: 1, machinery: 1, naval: 1, armored: 1, aviation: 0.8, magical: 1.2 }, // increased + surprise: {melee: 2, ranged: 2.4, mounted: 1, machinery: 1, naval: 1, armored: 1, aviation: 0.8, magical: 1.2}, // increased shock: { melee: 0.5, ranged: 0.5, @@ -471,8 +473,8 @@ class Battle { }, // reduced // air battle phases - maneuvering: { melee: 0, ranged: 0.1, mounted: 0, machinery: 0.2, naval: 0, armored: 0, aviation: 1, magical: 0.2 }, // aviation - dogfight: { melee: 0, ranged: 0.1, mounted: 0, machinery: 0.1, naval: 0, armored: 0, aviation: 2, magical: 0.1 } // aviation + maneuvering: {melee: 0, ranged: 0.1, mounted: 0, machinery: 0.2, naval: 0, armored: 0, aviation: 1, magical: 0.2}, // aviation + dogfight: {melee: 0, ranged: 0.1, mounted: 0, machinery: 0.1, naval: 0, armored: 0, aviation: 2, magical: 0.1} // aviation }; const forces = this.getJoinedForces(this[side].regiments); @@ -836,18 +838,18 @@ class Battle { losses === 1 ? "is destroyed" : losses > 0.8 - ? "is almost completely destroyed" - : losses > 0.5 - ? "suffered terrible losses" - : losses > 0.3 - ? "suffered severe losses" - : losses > 0.2 - ? "suffered heavy losses" - : losses > 0.05 - ? "suffered significant losses" - : losses > 0 - ? "suffered unsignificant losses" - : "left the battle without loss"; + ? "is almost completely destroyed" + : losses > 0.5 + ? "suffered terrible losses" + : losses > 0.3 + ? "suffered severe losses" + : losses > 0.2 + ? "suffered heavy losses" + : losses > 0.05 + ? "suffered significant losses" + : losses > 0 + ? "suffered unsignificant losses" + : "left the battle without loss"; const casualties = Object.keys(r.casualties) .map(t => (r.casualties[t] ? `${Math.abs(r.casualties[t])} ${t}` : null)) .filter(c => c); @@ -866,7 +868,7 @@ class Battle { const i = last(pack.markers)?.i + 1 || 0; { // append battlefield marker - const marker = { i, x: this.x, y: this.y, cell: this.cell, icon: "⚔️", type: "battlefields", dy: 52 }; + const marker = {i, x: this.x, y: this.y, cell: this.cell, icon: "⚔️", type: "battlefields", dy: 52}; pack.markers.push(marker); const markerHTML = drawMarker(marker); ensureEl("markers").insertAdjacentHTML("beforeend", markerHTML); @@ -887,7 +889,7 @@ class Battle { \r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses( this.defenders.casualties )}%`; - notes.push({ id: `marker${i}`, name: this.name, legend }); + notes.push({id: `marker${i}`, name: this.name, legend}); tip(`${this.name} is over. ${result}`, true, "success", 4000); diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 43fd7c5db..a90158e34 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -17,7 +17,7 @@ function editRegiment(selector) { title: "Edit Regiment", resizable: false, close: closeEditor, - position: { my: "left top", at: "left+10 top+10", of: "#map" } + position: {my: "left top", at: "left+10 top+10", of: "#map"} }); if (modules.editRegiment) return; @@ -117,7 +117,7 @@ function editRegiment(selector) { const reg = getRegiment(); d3.event.on("drag", function () { - const { x, y } = d3.event; + const {x, y} = d3.event; const angle = rn(Math.atan2(y - reg.y, x - reg.x) * (180 / Math.PI), 2); elSelected.setAttribute("transform", `rotate(${angle})`); this.setAttribute("transform", `rotate(${angle})`); @@ -288,7 +288,6 @@ function editRegiment(selector) { burg = burgSelected.parentElement, isFraternalBurg = getRegiment().state == target.getAttribute("state"); - if (String(burg.id) != "burgIcons") { tip("Please click on a burg to attack", false, "error"); return; @@ -298,12 +297,10 @@ function editRegiment(selector) { tip("Please click on a castled burg or a burg that has castle to attack", false, "error"); return; } - if (pack.burgs[burgId].type != "Naval" && getRegiment().n) { tip("Burg is too far from sea, regiment cannot engage. Please choose a burg near the shoreline.", false, "error"); return; } - if (burgSelected === elSelected) { tip("Regiment cannot attack itself", false, "error"); return; @@ -321,7 +318,6 @@ function editRegiment(selector) { const burgStateId = Number(pack.burgs[burgId].state); const burgStateRegiments = pack.states[burgStateId].military; const defendingBurg = target; - const bx = +defendingBurg.getAttribute("x"); const by = +defendingBurg.getAttribute("y"); @@ -364,7 +360,7 @@ function editRegiment(selector) { .delay(300) .duration(700) .ease(d3.easeSinInOut) - .on("end", () => new Battle(attacker, defendingRegiment, Number(target.id.replace(/\D/g, "")))); + .on("end", () => new Battle(attacker, defendingRegiment)); svg .append("text") .attr("text-rendering", "optimizeSpeed") From dcde024b0c4bb1e925804ed5b434b8041e51d3b1 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 06:05:03 +0800 Subject: [PATCH 20/32] refactor: removed parenthesis, renamed hasCitadel fix: fraternal burgs no longer targetable --- public/modules/ui/battle-screen.js | 56 ++++++++++++++-------------- public/modules/ui/regiment-editor.js | 5 +-- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index b1345fa90..329b3fdea 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -73,7 +73,7 @@ class Battle { if (attacker.n && defender.n) return "naval"; // attacker and defender are navals if (typesA.every(t => t === "aviation") && typesD.every(t => t === "aviation")) return "air"; // if attackers and defender have only aviation units if (attacker.n && !defender.n && typesA.some(t => t !== "naval")) return "landing"; // if attacked is naval with non-naval units and defender is not naval - if ((!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) || pack.burgs[pack.cells.burg[this.cell]].citadel) return "siege"; // defender is in walled town + if (!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls || pack.burgs[pack.cells.burg[this.cell]].citadel) return "siege"; // defender is in walled town if (P(0.1) && [5, 6, 7, 8, 9, 12].includes(pack.cells.biome[this.cell])) return "ambush"; // 20% if defenders are in forest or marshes return "field"; }; @@ -313,7 +313,7 @@ class Battle { } calculateStrength(side) { - const hascastle = pack.burgs[pack.cells.burg[this.cell]].citadel; + const hasCitadel = pack.burgs[pack.cells.burg[this.cell]].citadel; const scheme = { // field battle phases skirmish: { @@ -365,42 +365,42 @@ class Battle { // siege phases blockade: { - melee: (hascastle) ? 0.08 : 0.25, - ranged: (hascastle) ? 0.20 : 0.25, - mounted: (hascastle) ? 0.03 : 0.2, - machinery: (hascastle) ? 0.8 : 0.5, - naval: (hascastle) ? 0.06 : 0.2, - armored: (hascastle) ? 0.09 : 0.1, - aviation: (hascastle) ? 0.25 : 0.25, - magical: (hascastle) ? 0.025 : 0.25 + melee: (hasCitadel) ? 0.08 : 0.25, + ranged: (hasCitadel) ? 0.20 : 0.25, + mounted: (hasCitadel) ? 0.03 : 0.2, + machinery: (hasCitadel) ? 0.8 : 0.5, + naval: (hasCitadel) ? 0.06 : 0.2, + armored: (hasCitadel) ? 0.09 : 0.1, + aviation: (hasCitadel) ? 0.25 : 0.25, + magical: (hasCitadel) ? 0.025 : 0.25 }, // no active actions sheltering: { - melee: (hascastle) ? 0.4 : 0.3, - ranged: (hascastle) ? 0.8 : 0.5, - mounted: (hascastle) ? 0.08 : 0.2, - machinery: (hascastle) ? 0.6 : 0.5, - naval: (hascastle) ? 0.01 : 0.2, - armored: (hascastle) ? 0.1 : 0.1, - aviation: (hascastle) ? 0.1 : 0.25, - magical: (hascastle) ? 0.25 : 0.25 + melee: (hasCitadel) ? 0.4 : 0.3, + ranged: (hasCitadel) ? 0.8 : 0.5, + mounted: (hasCitadel) ? 0.08 : 0.2, + machinery: (hasCitadel) ? 0.6 : 0.5, + naval: (hasCitadel) ? 0.01 : 0.2, + armored: (hasCitadel) ? 0.1 : 0.1, + aviation: (hasCitadel) ? 0.1 : 0.25, + magical: (hasCitadel) ? 0.25 : 0.25 }, // no active actions sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel bombardment: { - melee: (hascastle) ? 0.1 : 0.2, - ranged: (hascastle) ? 0.6 : 0.5, - mounted: (hascastle) ? 0.25 : 0.2, - machinery: (hascastle) ? 0.4 : 3, - naval: (hascastle) ? 0.1 : 1, + melee: (hasCitadel) ? 0.1 : 0.2, + ranged: (hasCitadel) ? 0.6 : 0.5, + mounted: (hasCitadel) ? 0.25 : 0.2, + machinery: (hasCitadel) ? 0.4 : 3, + naval: (hasCitadel) ? 0.1 : 1, armored: 0.5, aviation: 1, magical: 1 }, // machinery excel storming: { - melee: (hascastle) ? 0.8 : 1, - ranged: (hascastle) ? 0.5 : 0.6, - mounted: (hascastle) ? 0.3 : 0.5, - machinery: (hascastle) ? 0.5 : 1, - naval: (hascastle) ? 0.01 : 0.1, + melee: (hasCitadel) ? 0.8 : 1, + ranged: (hasCitadel) ? 0.5 : 0.6, + mounted: (hasCitadel) ? 0.3 : 0.5, + machinery: (hasCitadel) ? 0.5 : 1, + naval: (hasCitadel) ? 0.01 : 0.1, armored: 0.1, aviation: 0.5, magical: 0.5 diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index a90158e34..44827e9e5 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -285,8 +285,7 @@ function editRegiment(selector) { function attackBurgOnClick() { const target = d3.event.target, burgSelected = target.parentElement, - burg = burgSelected.parentElement, - isFraternalBurg = getRegiment().state == target.getAttribute("state"); + burg = burgSelected.parentElement; if (String(burg.id) != "burgIcons") { tip("Please click on a burg to attack", false, "error"); @@ -305,7 +304,7 @@ function editRegiment(selector) { tip("Regiment cannot attack itself", false, "error"); return; } - if (isFraternalBurg) { + if (getRegiment().state == pack.burgs[burgId].state) { tip("Cannot attack fraternal burg", false, "error"); return; } From 75f6c686243d67dbcdada776650ce223e6ff3c62 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 06:11:57 +0800 Subject: [PATCH 21/32] refactor: removed brackets --- public/modules/ui/battle-screen.js | 52 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index 329b3fdea..52f785c2a 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -365,42 +365,42 @@ class Battle { // siege phases blockade: { - melee: (hasCitadel) ? 0.08 : 0.25, - ranged: (hasCitadel) ? 0.20 : 0.25, - mounted: (hasCitadel) ? 0.03 : 0.2, - machinery: (hasCitadel) ? 0.8 : 0.5, - naval: (hasCitadel) ? 0.06 : 0.2, - armored: (hasCitadel) ? 0.09 : 0.1, - aviation: (hasCitadel) ? 0.25 : 0.25, - magical: (hasCitadel) ? 0.025 : 0.25 + melee: hasCitadel ? 0.08 : 0.25, + ranged: hasCitadel ? 0.20 : 0.25, + mounted: hasCitadel ? 0.03 : 0.2, + machinery: hasCitadel ? 0.8 : 0.5, + naval: hasCitadel ? 0.06 : 0.2, + armored: hasCitadel ? 0.09 : 0.1, + aviation: hasCitadel ? 0.25 : 0.25, + magical: hasCitadel ? 0.025 : 0.25 }, // no active actions sheltering: { - melee: (hasCitadel) ? 0.4 : 0.3, - ranged: (hasCitadel) ? 0.8 : 0.5, - mounted: (hasCitadel) ? 0.08 : 0.2, - machinery: (hasCitadel) ? 0.6 : 0.5, - naval: (hasCitadel) ? 0.01 : 0.2, - armored: (hasCitadel) ? 0.1 : 0.1, - aviation: (hasCitadel) ? 0.1 : 0.25, - magical: (hasCitadel) ? 0.25 : 0.25 + melee: hasCitadel ? 0.4 : 0.3, + ranged: hasCitadel ? 0.8 : 0.5, + mounted: hasCitadel ? 0.08 : 0.2, + machinery: hasCitadel ? 0.6 : 0.5, + naval: hasCitadel ? 0.01 : 0.2, + armored: hasCitadel ? 0.1 : 0.1, + aviation: hasCitadel ? 0.1 : 0.25, + magical: hasCitadel ? 0.25 : 0.25 }, // no active actions sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel bombardment: { - melee: (hasCitadel) ? 0.1 : 0.2, - ranged: (hasCitadel) ? 0.6 : 0.5, - mounted: (hasCitadel) ? 0.25 : 0.2, - machinery: (hasCitadel) ? 0.4 : 3, - naval: (hasCitadel) ? 0.1 : 1, + melee: hasCitadel ? 0.1 : 0.2, + ranged: hasCitadel ? 0.6 : 0.5, + mounted: hasCitadel ? 0.25 : 0.2, + machinery: hasCitadel ? 0.4 : 3, + naval: hasCitadel ? 0.1 : 1, armored: 0.5, aviation: 1, magical: 1 }, // machinery excel storming: { - melee: (hasCitadel) ? 0.8 : 1, - ranged: (hasCitadel) ? 0.5 : 0.6, - mounted: (hasCitadel) ? 0.3 : 0.5, - machinery: (hasCitadel) ? 0.5 : 1, - naval: (hasCitadel) ? 0.01 : 0.1, + melee: hasCitadel ? 0.8 : 1, + ranged: hasCitadel ? 0.5 : 0.6, + mounted: hasCitadel ? 0.3 : 0.5, + machinery: hasCitadel ? 0.5 : 1, + naval: hasCitadel ? 0.01 : 0.1, armored: 0.1, aviation: 0.5, magical: 0.5 From aa6616f5cf30666d659c62490126124e0840ce1a Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 06:15:21 +0800 Subject: [PATCH 22/32] adjustment: bombardmen melee adjusted from 0.1 to 0.3 --- public/modules/ui/battle-screen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index 52f785c2a..95eeab419 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -386,7 +386,7 @@ class Battle { }, // no active actions sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel bombardment: { - melee: hasCitadel ? 0.1 : 0.2, + melee: hasCitadel ? 0.3 : 0.2, ranged: hasCitadel ? 0.6 : 0.5, mounted: hasCitadel ? 0.25 : 0.2, machinery: hasCitadel ? 0.4 : 3, From 5d84beb05e1ae64cd4b4508324722729a7504439 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 14:49:23 +0800 Subject: [PATCH 23/32] fix: changed byId to ensureEl --- public/modules/ui/battle-screen.js | 6 +++--- public/modules/ui/regiment-editor.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index 95eeab419..f40b3f09c 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -85,7 +85,7 @@ class Battle { setType() { ensureEl("battleType").className = "icon-button-" + this.type; - const sideSpecific = ensureEl("battlePhases_" + this.type + "_attackers"); + const sideSpecific = ensureEl("battlePhases" + this.type + "_attackers"); const attackers = sideSpecific ? sideSpecific.content : ensureEl("battlePhases_" + this.type).content; const defenders = sideSpecific ? ensureEl("battlePhases_" + this.type + "_defenders").content : attackers; @@ -297,8 +297,8 @@ class Battle { type === "culture" ? Names.getCulture(pack.cells.culture[this.cell], null, null, "") : Names.getBase(rand(nameBases.length - 1)); - byId("battleNamePlace").value = this.place = place; - byId("battleNameFull").value = this.name = this.defineName(); + ensureEl("battleNamePlace").value = this.place = place; + ensureEl("battleNameFull").value = this.name = this.defineName(); $("#battleScreen").dialog({ title: this.name }); } diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 44827e9e5..ef2bd333c 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -286,12 +286,12 @@ function editRegiment(selector) { const target = d3.event.target, burgSelected = target.parentElement, burg = burgSelected.parentElement; + const burgId = target.dataset.id; if (String(burg.id) != "burgIcons") { tip("Please click on a burg to attack", false, "error"); return; } - const burgId = Number(target.id.replace(/\D/g, "")); if (pack.burgs[burgId].walls != 1 && pack.burgs[burgId].citadel != 1) { tip("Please click on a castled burg or a burg that has castle to attack", false, "error"); return; From b6270fd2b3acebe0cfa45abdba392676a19e8625 Mon Sep 17 00:00:00 2001 From: allen Date: Sat, 2 May 2026 14:55:37 +0800 Subject: [PATCH 24/32] . --- public/modules/ui/battle-screen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index f40b3f09c..333d802c2 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -85,7 +85,7 @@ class Battle { setType() { ensureEl("battleType").className = "icon-button-" + this.type; - const sideSpecific = ensureEl("battlePhases" + this.type + "_attackers"); + const sideSpecific = ensureEl("battlePhases_" + this.type + "_attackers"); const attackers = sideSpecific ? sideSpecific.content : ensureEl("battlePhases_" + this.type).content; const defenders = sideSpecific ? ensureEl("battlePhases_" + this.type + "_defenders").content : attackers; From 70f1326294e333403cbd67632c8eaf6103485e33 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:36:58 +0800 Subject: [PATCH 25/32] improvement: added another guard clause --- public/modules/ui/regiment-editor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index ef2bd333c..7469b7d5a 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -338,7 +338,8 @@ function editRegiment(selector) { if (closestIndex == -1 && pack.states[pack.burgs[burgId].state].military.length == 1) closestIndex = 0; - + if (closestIndex == -1) + return; const defendingRegiment = pack.states[pack.burgs[burgId].state].military[closestIndex]; if (!attacker.a || !defendingRegiment.a) { tip("Regiment has no troops to battle", false, "error"); From 230bdac672706190367d07c65f041cfd36392dd1 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:45:55 +0800 Subject: [PATCH 26/32] refactor: stricter conversion --- public/modules/ui/regiment-editor.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 7469b7d5a..e9c80e956 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -317,8 +317,10 @@ function editRegiment(selector) { const burgStateId = Number(pack.burgs[burgId].state); const burgStateRegiments = pack.states[burgStateId].military; const defendingBurg = target; - const bx = +defendingBurg.getAttribute("x"); - const by = +defendingBurg.getAttribute("y"); + const dx = +defendingBurg.getAttribute("x"); + const dy = +defendingBurg.getAttribute("y"); + const ax = +attacker.getAttribute("x"); + const ay = +attacker.getAttribute("y"); let closestIndex = -1; let closestDistance = Infinity; @@ -326,9 +328,9 @@ function editRegiment(selector) { burgStateRegiments.forEach((el, i) => { if (el.t === 0 || !el.a) return; - const dx = el.x - bx; - const dy = el.y - by; - const dist = Math.sqrt(dx * dx + dy * dy); + const distX = el.x - dx; + const distY = el.y - dy; + const dist = Math.sqrt(distX * distX + distY * distY); if (dist < closestDistance) { closestDistance = dist; @@ -351,9 +353,9 @@ function editRegiment(selector) { (defendingRegiment.px = defendingRegiment.x), (defendingRegiment.py = defendingRegiment.y); // move attacker to burg - moveRegiment(attacker, defendingBurg.getAttribute("x"), defendingBurg.getAttribute("y") - 8); + moveRegiment(attacker, dx, dy - 8); // Move defender to burg - moveRegiment(defendingRegiment, defendingBurg.getAttribute("x"), defendingBurg.getAttribute("y")); + moveRegiment(defendingRegiment, ax, ay); // draw battle icon const attack = d3 .transition() From a340a03c684d70c6b5356f9ef0f820f619757057 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:48:46 +0800 Subject: [PATCH 27/32] refactor: grammar corrected --- public/modules/ui/regiment-editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index e9c80e956..74b81a440 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -293,7 +293,7 @@ function editRegiment(selector) { return; } if (pack.burgs[burgId].walls != 1 && pack.burgs[burgId].citadel != 1) { - tip("Please click on a castled burg or a burg that has castle to attack", false, "error"); + tip("Please click on a burg with walls or a citadel to attack", false, "error"); return; } if (pack.burgs[burgId].type != "Naval" && getRegiment().n) { From c39fdf4678a90180773dae4af54b5161871c536b Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:50:54 +0800 Subject: [PATCH 28/32] refactor: whitespace after inline brackets removed --- public/modules/ui/regiment-editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 74b81a440..543fb53bf 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -93,7 +93,7 @@ function editRegiment(selector) { function drawRotationControl() { const reg = getRegiment(); - const { x, width, y, height } = elSelected.getBBox(); + const {x, width, y, height} = elSelected.getBBox(); debug .append("circle") @@ -245,7 +245,7 @@ function editRegiment(selector) { 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 @@ -553,7 +553,7 @@ function editRegiment(selector) { const rotationControl = debug.select("#rotationControl"); d3.event.on("drag", function () { - const { x, y } = d3.event; + const {x, y} = d3.event; reg.x = x; reg.y = y; const x1 = rn(x - w / 2, 2); From e6034cf847ea11c2dedf94b2a8e9e5d3df341603 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:52:09 +0800 Subject: [PATCH 29/32] refactor: grammer corrected --- src/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index 311a7272c..79307bef7 100644 --- a/src/index.html +++ b/src/index.html @@ -5936,7 +5936,7 @@
- External images (Links that ends with .png, .jpg, or .svg) + External images (Links that end with .png, .jpg, or .svg)
Paste link to the image here: From 6728e57e42daadb81e3d95f0fb1fce9b52112f50 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:54:33 +0800 Subject: [PATCH 30/32] fix: operator precedence --- public/modules/ui/battle-screen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js index 333d802c2..dff70f42b 100644 --- a/public/modules/ui/battle-screen.js +++ b/public/modules/ui/battle-screen.js @@ -73,7 +73,7 @@ class Battle { if (attacker.n && defender.n) return "naval"; // attacker and defender are navals if (typesA.every(t => t === "aviation") && typesD.every(t => t === "aviation")) return "air"; // if attackers and defender have only aviation units if (attacker.n && !defender.n && typesA.some(t => t !== "naval")) return "landing"; // if attacked is naval with non-naval units and defender is not naval - if (!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls || pack.burgs[pack.cells.burg[this.cell]].citadel) return "siege"; // defender is in walled town + if (!defender.n && (pack.burgs[pack.cells.burg[this.cell]].walls || pack.burgs[pack.cells.burg[this.cell]].citadel)) return "siege"; // defender is in walled town if (P(0.1) && [5, 6, 7, 8, 9, 12].includes(pack.cells.biome[this.cell])) return "ambush"; // 20% if defenders are in forest or marshes return "field"; }; From b7bb7fb071ad61bcf7fb491af15165dcf48aa961 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:56:35 +0800 Subject: [PATCH 31/32] refactor: removed copy pasted code --- public/modules/ui/regiment-editor.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index 543fb53bf..b4bacd786 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -300,10 +300,7 @@ function editRegiment(selector) { tip("Burg is too far from sea, regiment cannot engage. Please choose a burg near the shoreline.", false, "error"); return; } - if (burgSelected === elSelected) { - tip("Regiment cannot attack itself", false, "error"); - return; - } + if (getRegiment().state == pack.burgs[burgId].state) { tip("Cannot attack fraternal burg", false, "error"); return; From 3e2158e51e4457ba19ce6be576bf2b539e87fc33 Mon Sep 17 00:00:00 2001 From: allen Date: Fri, 8 May 2026 00:58:25 +0800 Subject: [PATCH 32/32] refactor: guard clause ordering --- public/modules/ui/regiment-editor.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js index b4bacd786..9f7d8410d 100644 --- a/public/modules/ui/regiment-editor.js +++ b/public/modules/ui/regiment-editor.js @@ -288,10 +288,18 @@ function editRegiment(selector) { burg = burgSelected.parentElement; const burgId = target.dataset.id; + if (pack.states[pack.burgs[burgId].state].military.length == 0) { + tip("Burg's state has no military", false, "error"); + return; + } if (String(burg.id) != "burgIcons") { tip("Please click on a burg to attack", false, "error"); return; } + if (getRegiment().state == pack.burgs[burgId].state) { + tip("Cannot attack fraternal burg", false, "error"); + return; + } if (pack.burgs[burgId].walls != 1 && pack.burgs[burgId].citadel != 1) { tip("Please click on a burg with walls or a citadel to attack", false, "error"); return; @@ -300,15 +308,7 @@ function editRegiment(selector) { tip("Burg is too far from sea, regiment cannot engage. Please choose a burg near the shoreline.", false, "error"); return; } - - if (getRegiment().state == pack.burgs[burgId].state) { - tip("Cannot attack fraternal burg", false, "error"); - return; - } - if (pack.states[pack.burgs[burgId].state].military.length == 0) { - tip("Burg's state has no military", false, "error"); - return; - } + const attacker = getRegiment(); const burgStateId = Number(pack.burgs[burgId].state);