Urbanization rate:
diff --git a/modules/military-generator.js b/modules/military-generator.js
index e6dc44b1..3b509cbb 100644
--- a/modules/military-generator.js
+++ b/modules/military-generator.js
@@ -190,7 +190,7 @@
const regiments = nodes.filter(n => n.t).sort((a,b) => b.t - a.t).map((r, i) => {
const u = {}; u[r.u] = r.a;
(r.childen||[]).forEach(n => u[n.u] = u[n.u] ? u[n.u] += n.a : n.a);
- return {i, a:r.t, cell:r.cell, x:r.x, y:r.y, bx:r.x, by:r.y, u, n:r.n, name};
+ return {i, a:r.t, cell:r.cell, x:r.x, y:r.y, bx:r.x, by:r.y, u, n:r.n, name, state: s.i};
});
// generate name for regiments
@@ -210,9 +210,9 @@
return [
{icon: "⚔️", name:"infantry", rural:.25, urban:.2, crew:1, power:1, type:"melee", separate:0},
{icon: "🏹", name:"archers", rural:.12, urban:.2, crew:1, power:1, type:"ranged", separate:0},
- {icon: "🐴", name:"cavalry", rural:.12, urban:.03, crew:3, power:4, type:"mounted", separate:0},
- {icon: "💣", name:"artillery", rural:0, urban:.03, crew:8, power:12, type:"machinery", separate:0},
- {icon: "🌊", name:"fleet", rural:0, urban:.015, crew:100, power:50, type:"naval", separate:1}
+ {icon: "🐴", name:"cavalry", rural:.12, urban:.03, crew:2, power:2, type:"mounted", separate:0},
+ {icon: "💣", name:"artillery", rural:0, urban:.03, crew:8, power:12, type:"machinery", separate:0},
+ {icon: "🌊", name:"fleet", rural:0, urban:.015, crew:100, power:50, type:"naval", separate:1}
];
}
@@ -256,6 +256,26 @@
g.append("text").attr("class", "regimentIcon").attr("x", x1-size).attr("y", reg.y).text(reg.icon);
}
+ // move one regiment to another
+ const moveRegiment = function(reg, x, y) {
+ const el = armies.select("g#army"+reg.state).select("g#regiment"+reg.state+"-"+reg.i);
+ if (!el.size()) return;
+
+ const duration = Math.hypot(reg.x - x, reg.y - y) * 8;
+ reg.x = x; reg.y = y;
+ const size = +armies.attr("box-size");
+ const w = reg.n ? size * 4 : size * 6;
+ const h = size * 2;
+ const x1 = x => rn(x - w / 2, 2);
+ const y1 = y => rn(y - size, 2);
+
+ const move = d3.transition().duration(duration).ease(d3.easeSinInOut);
+ el.select("rect").transition(move).attr("x", x1(x)).attr("y", y1(y));
+ el.select("text").transition(move).attr("x", x).attr("y", y);
+ el.selectAll("rect:nth-of-type(2)").transition(move).attr("x", x1(x)-h).attr("y", y1(y));
+ el.select(".regimentIcon").transition(move).attr("x", x1(x)-size).attr("y", y);
+ }
+
// utilize si function to make regiment total text fit regiment box
const getTotal = reg => reg.a > (reg.n ? 999 : 99999) ? si(reg.a) : reg.a;
@@ -304,6 +324,6 @@
// note.legend = note.legend.replace(oldComposition, newComposition);
// }
- return {generate, getDefaultOptions, getName, generateNote, drawRegiments, drawRegiment, getTotal, getEmblem};
+ return {generate, getDefaultOptions, getName, generateNote, drawRegiments, drawRegiment, moveRegiment, getTotal, getEmblem};
})));
\ No newline at end of file
diff --git a/modules/ocean-layers.js b/modules/ocean-layers.js
index 7751b376..b5be82f4 100644
--- a/modules/ocean-layers.js
+++ b/modules/ocean-layers.js
@@ -33,15 +33,15 @@
const relaxed = chain.filter((v, i) => !(i%relax) || vertices.c[v].some(c => c >= pointsN));
if (relaxed.length < 4) continue;
const points = clipPoly(relaxed.map(v => vertices.p[v]), 1);
- const inside = d3.polygonContains(points, grid.points[i]);
- chains.push([t, points, inside]);
+ //const inside = d3.polygonContains(points, grid.points[i]);
+ chains.push([t, points]); //chains.push([t, points, inside]);
}
- const bbox = `M0,0h${graphWidth}v${graphHeight}h${-graphWidth}Z`;
+ //const bbox = `M0,0h${graphWidth}v${graphHeight}h${-graphWidth}Z`;
for (const t of limits) {
const layer = chains.filter(c => c[0] === t);
let path = layer.map(c => round(lineGen(c[1]))).join("");
- if (layer.every(c => !c[2])) path = bbox + path; // add outer ring if all segments are outside (works not for all cases)
+ //if (layer.every(c => !c[2])) path = bbox + path; // add outer ring if all segments are outside (works not for all cases)
if (path) oceanLayers.append("path").attr("d", path).attr("fill", "#ecf2f9").style("opacity", opacity);
}
diff --git a/modules/save-and-load.js b/modules/save-and-load.js
index bc33d999..47ac9274 100644
--- a/modules/save-and-load.js
+++ b/modules/save-and-load.js
@@ -1018,6 +1018,9 @@ function parseLoadedData(data) {
if (type === "magical") return "🔮";
else return "⚔️";
}
+
+ // 1.4 added state reference for regiments
+ pack.states.filter(s => s.military).forEach(s => s.military.forEach(r => r.state = s.i));
}
}()
diff --git a/modules/ui/battle-screen.js b/modules/ui/battle-screen.js
index 01127d2d..98819736 100644
--- a/modules/ui/battle-screen.js
+++ b/modules/ui/battle-screen.js
@@ -1,53 +1,83 @@
"use strict";
-function showBattleScreen(attacker, defender) {
- if (customization) return;
- closeDialogs(".stable");
+class Battle {
- const battle = {name:"Battle", attackers:[attacker], defenders:[defender]};
- const battleAttackers = document.getElementById("battleAttackers");
- const battleDefenders = document.getElementById("battleDefenders");
- addHeaders();
- addRegiment(battleAttackers, attacker);
- addRegiment(battleDefenders, defender);
+ constructor(attacker, defender) {
+ if (customization) return;
+ closeDialogs(".stable");
+ customization = 13; // enter customization to avoid unwanted dialog closing
- $("#battleScreen").dialog({
- title: battle.name, resizable: false, width: fitContent(), close: closeBattleScreen,
- position: {my: "center", at: "center", of: "#map"}
- });
+ Battle.prototype.context = this; // store context
+ this.x = defender.x;
+ this.y = defender.y;
+ this.name = this.getBattleName();
+ this.iteration = 0;
+ this.attackers = {regiments:[], distances:[], morale:100};
+ this.defenders = {regiments:[], distances:[], morale:100};
- if (modules.showBattleScreen) return;
- modules.showBattleScreen = true;
+ this.addHeaders();
+ this.addRegiment("attackers", attacker);
+ this.addRegiment("defenders", defender);
+ this.randomize();
+ this.calculateStrength("attackers");
+ this.calculateStrength("defenders");
+ this.getInitialMorale();
- // add listeners
- document.getElementById("battleAddRegiment").addEventListener("click", addSide);
+ $("#battleScreen").dialog({
+ title: this.name, resizable: false, width: fitContent(), close: this.closeBattleScreen,
+ position: {my: "center", at: "center", of: "#map"}
+ });
- function addHeaders() {
- document.getElementById("battleScreen").querySelectorAll("th").forEach(el => el.remove());
- const attackers = battleAttackers.querySelector("tr");
- const defenders = battleDefenders.querySelector("tr");
- let headers = "
| | ";
+ if (modules.Battle) return;
+ modules.Battle = true;
+
+ // add listeners
+ document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
+ document.getElementById("battleRoll").addEventListener("click", () => Battle.prototype.context.randomize());
+ document.getElementById("battleRun").addEventListener("click", () => Battle.prototype.context.run());
+ document.getElementById("battleApply").addEventListener("click", () => Battle.prototype.context.applyResults());
+ document.getElementById("battleCancel").addEventListener("click", () => Battle.prototype.context.cancelResults());
+
+ document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChangePhase(ev, "attackers"));
+ document.getElementById("battlePhase_attackers").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
+ document.getElementById("battlePhase_defenders").addEventListener("click", ev => this.toggleChangePhase(ev, "defenders"));
+ document.getElementById("battlePhase_defenders").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
+ document.getElementById("battleDie_attackers").addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
+ document.getElementById("battleDie_defenders").addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
+ }
+
+ getBattleName() {
+ const cell = findCell(this.x, this.y);
+ const burg = pack.cells.burg[cell] ? pack.burgs[pack.cells.burg[cell]].name : null;
+ return burg ? burg + " Battle" : Names.getCulture(pack.cells.culture[cell]) + " Battle"
+ }
+
+ addHeaders() {
+ let headers = "
| | ";
for (const u of options.military) {
const label = capitalize(u.name.replace(/_/g, ' '));
headers += `${u.icon} | `;
}
- headers += "Total | ";
- attackers.insertAdjacentHTML("beforebegin", headers);
- defenders.insertAdjacentHTML("beforebegin", headers);
+ headers += "Total |
|---|
";
+ battleAttackers.innerHTML = battleDefenders.innerHTML = headers;
}
- function addRegiment(div, regiment) {
- const state = ra(pack.states), supply = rand(1000) + " " + distanceUnitInput.value;
+ addRegiment(side, regiment) {
+ regiment.casualties = Object.keys(regiment.u).reduce((a,b) => (a[b]=0,a), {});
+ regiment.survivors = Object.assign({}, regiment.u);
+
+ const state = pack.states[regiment.state];
+ const distance = Math.hypot(this.y-regiment.by, this.x-regiment.bx) * distanceScaleInput.value | 0; // distance between regiment and its base
const color = state.color[0] === "#" ? state.color : "#999";
- const icon = `