diff --git a/public/modules/ui/battle-screen.js b/public/modules/ui/battle-screen.js
index 5b441a7b4..dff70f42b 100644
--- a/public/modules/ui/battle-screen.js
+++ b/public/modules/ui/battle-screen.js
@@ -28,7 +28,7 @@ class Battle {
title: this.name,
resizable: false,
width: fitContent(),
- position: {my: "center", at: "center", of: "#map"},
+ position: { my: "center", at: "center", of: "#map" },
close: () => Battle.prototype.context.cancelResults()
});
@@ -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) 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";
};
@@ -156,9 +156,9 @@ class Battle {
${iconHtml}`;
const body = `
-
+
${s.name.slice(0, 11)}
${r.icon}
${r.name.slice(0, 24)}
@@ -224,7 +223,7 @@ class Battle {
resizable: false,
width: fitContent(),
title: "Add regiment to the battle",
- position: {my: "left center", at: "right+10 center", of: "#battleScreen"},
+ position: { my: "left center", at: "right+10 center", of: "#battleScreen" },
close: addSideClosed,
buttons: {
"Add to attackers": () => addSideClicked("attackers"),
@@ -290,7 +289,7 @@ class Battle {
changeName(ev) {
this.name = ev.target.value;
- $("#battleScreen").dialog({title: this.name});
+ $("#battleScreen").dialog({ title: this.name });
}
generateName(type) {
@@ -300,7 +299,7 @@ class Battle {
: Names.getBase(rand(nameBases.length - 1));
ensureEl("battleNamePlace").value = this.place = place;
ensureEl("battleNameFull").value = this.name = this.defineName();
- $("#battleScreen").dialog({title: this.name});
+ $("#battleScreen").dialog({ title: this.name });
}
getJoinedForces(regiments) {
@@ -314,6 +313,7 @@ class Battle {
}
calculateStrength(side) {
+ 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: 0.25,
- ranged: 0.25,
- mounted: 0.2,
- machinery: 0.5,
- naval: 0.2,
- armored: 0.1,
- aviation: 0.25,
- magical: 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: 0.3,
- ranged: 0.5,
- mounted: 0.2,
- machinery: 0.5,
- naval: 0.2,
- armored: 0.1,
- aviation: 0.25,
- magical: 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: 0.2,
- ranged: 0.5,
- mounted: 0.2,
- machinery: 3,
- naval: 1,
+ melee: hasCitadel ? 0.3 : 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: 1,
- ranged: 0.6,
- mounted: 0.5,
- machinery: 1,
- naval: 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
@@ -782,7 +782,7 @@ class Battle {
button.style.opacity = 0.5;
div.style.display = "block";
- document.getElementsByTagName("body")[0].on("click", hideSection, {once: true});
+ document.getElementsByTagName("body")[0].on("click", hideSection, { once: true });
}
changeType(ev) {
@@ -793,7 +793,7 @@ class Battle {
this.calculateStrength("attackers");
this.calculateStrength("defenders");
this.name = this.defineName();
- $("#battleScreen").dialog({title: this.name});
+ $("#battleScreen").dialog({ title: this.name });
}
changePhase(ev, side) {
diff --git a/public/modules/ui/regiment-editor.js b/public/modules/ui/regiment-editor.js
index 3f9253519..9f7d8410d 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);
@@ -254,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);
@@ -266,6 +268,117 @@ function editRegiment(selector) {
}
}
+ function toggleBurgAttack() {
+ 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);
+ } 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;
+ 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;
+ }
+ 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;
+ }
+
+
+ const attacker = getRegiment();
+ const burgStateId = Number(pack.burgs[burgId].state);
+ const burgStateRegiments = pack.states[burgStateId].military;
+ const defendingBurg = target;
+ 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;
+
+ burgStateRegiments.forEach((el, i) => {
+ if (el.t === 0 || !el.a) return;
+
+ const distX = el.x - dx;
+ const distY = el.y - dy;
+ const dist = Math.sqrt(distX * distX + distY * distY);
+
+ if (dist < closestDistance) {
+ closestDistance = dist;
+ closestIndex = i;
+ }
+ });
+
+ 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");
+ return;
+ }
+
+ // save initial position to temp attribute
+ (attacker.px = attacker.x), (attacker.py = attacker.y);
+ (defendingRegiment.px = defendingRegiment.x), (defendingRegiment.py = defendingRegiment.y);
+
+ // move attacker to burg
+ moveRegiment(attacker, dx, dy - 8);
+ // Move defender to burg
+ moveRegiment(defendingRegiment, ax, ay);
+ // draw battle icon
+ const attack = d3
+ .transition()
+ .delay(300)
+ .duration(700)
+ .ease(d3.easeSinInOut)
+ .on("end", () => new Battle(attacker, defendingRegiment));
+ 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,
@@ -487,6 +600,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;
diff --git a/src/index.html b/src/index.html
index 11dbf51e0..7d55e344e 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3756,6 +3756,7 @@
+
-
External images
+
External images (Links that end with .png, .jpg, or .svg)
Paste link to the image here:
diff --git a/src/modules/military-generator.ts b/src/modules/military-generator.ts
index 398c7d2cb..5c154be4c 100644
--- a/src/modules/military-generator.ts
+++ b/src/modules/military-generator.ts
@@ -568,7 +568,7 @@ class MilitaryModule {
return `${number}${proper ? ` (${proper}) ` : ` `}${form}`;
}
- // utilize si function to make regiment total text fit regiment box
+ // utilize this function to make regiment total text fit regiment box
getTotal(reg: MilitaryRegiment) {
return reg.a > (reg.n ? 999 : 99999) ? si(reg.a) : reg.a;
}