"use strict";
function overviewMilitary() {
if (customization) return;
closeDialogs("#militaryOverview, .stable");
if (!layerIsOn("toggleStates")) toggleStates();
if (!layerIsOn("toggleBorders")) toggleBorders();
const body = document.getElementById("militaryBody");
addLines();
$("#militaryOverview").dialog();
if (modules.overviewMilitary) return;
modules.overviewMilitary = true;
updateHeaders();
$("#militaryOverview").dialog({
title: "Military Overview", resizable: false, width: fitContent(),
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
});
// add listeners
document.getElementById("militaryOverviewRefresh").addEventListener("click", addLines);
document.getElementById("militaryOptionsButton").addEventListener("click", militaryCustomize);
document.getElementById("militaryOverviewRecalculate").addEventListener("click", militaryRecalculate);
document.getElementById("militaryExport").addEventListener("click", downloadMilitaryData);
body.addEventListener("change", function(ev) {
const el = ev.target, line = el.parentNode, state = +line.dataset.id;
changeAlert(state, line, +el.value);
});
// update military types in header and tooltips
function updateHeaders() {
const header = document.getElementById("militaryHeader");
header.querySelectorAll(".removable").forEach(el => el.remove());
const insert = html => document.getElementById("militaryTotal").insertAdjacentHTML("beforebegin", html);
for (const u of options.military) {
const label = capitalize(u.name.replace(/_/g, ' '));
insert(`
${label}
`);
}
header.querySelectorAll(".removable").forEach(function(e) {
e.addEventListener("click", function() {sortLines(this);});
});
}
// add line for each state
function addLines() {
body.innerHTML = "";
let lines = "";
const states = pack.states.filter(s => s.i && !s.removed);
for (const s of states) {
const population = rn((s.rural + s.urban * urbanization.value) * populationRate.value);
const getForces = u => s.military.reduce((s, r) => s+(r.u[u.name]||0), 0);
const total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0);
const rate = total / population * 100;
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
const lineData = options.military.map(u => `${getForces(u)}
`).join(" ");
lines += ``;
}
body.insertAdjacentHTML("beforeend", lines);
updateFooter();
// add listeners
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev)));
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev)));
applySorting(militaryHeader);
}
function changeAlert(state, line, alert) {
const s = pack.states[state];
const dif = s.alert || alert ? alert / s.alert : 0; // modifier
s.alert = line.dataset.alert = alert;
s.military.forEach(r => {
Object.keys(r.u).forEach(u => r.u[u] = rn(r.u[u] * dif)); // change units value
r.a = d3.sum(Object.values(r.u)); // change total
armies.select(`g>g#regiment${s.i}-${r.i}>text`).text(Military.getTotal(r)); // change icon text
});
const getForces = u => s.military.reduce((s, r) => s+(r.u[u.name]||0), 0);
options.military.forEach(u => line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u));
const population = rn((s.rural + s.urban * urbanization.value) * populationRate.value);
const total = line.dataset.total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0);
const rate = line.dataset.rate = total / population * 100;
line.querySelector("div[data-type='total']>b").innerHTML = si(total);
line.querySelector("div[data-type='rate']").innerHTML = rn(rate, 2) + "%";
updateFooter();
}
function updateFooter() {
const lines = Array.from(body.querySelectorAll(":scope > div"));
const statesNumber = militaryFooterStates.innerHTML = pack.states.filter(s => s.i && !s.removed).length;
militaryFooterForces.innerHTML = si(d3.sum(lines.map(el => el.dataset.total)) / statesNumber);
militaryFooterRate.innerHTML = rn(d3.sum(lines.map(el => el.dataset.rate)) / statesNumber, 2) + "%";
militaryFooterAlert.innerHTML = rn(d3.sum(lines.map(el => el.dataset.alert)) / statesNumber, 2);
}
function stateHighlightOn(event) {
if (!layerIsOn("toggleStates")) return;
const state = +event.target.dataset.id;
if (customization || !state) return;
const path = regions.select("#state"+state).attr("d");
debug.append("path").attr("class", "highlight").attr("d", path)
.attr("fill", "none").attr("stroke", "red").attr("stroke-width", 1).attr("opacity", 1)
.attr("filter", "url(#blur1)").call(transition);
}
function transition(path) {
const duration = (path.node().getTotalLength() + 5000) / 2;
path.transition().duration(duration).attrTween("stroke-dasharray", tweenDash);
}
function tweenDash() {
const l = this.getTotalLength();
const i = d3.interpolateString("0," + l, l + "," + l);
return t => i(t);
}
function removePath(path) {
path.transition().duration(1000).attr("opacity", 0).remove();
}
function stateHighlightOff() {
debug.selectAll(".highlight").each(function() {
d3.select(this).call(removePath);
});
}
function militaryCustomize() {
const types = ["default", "melee", "ranged", "mounted", "machinery", "naval"];
const table = document.getElementById("militaryOptions").querySelector("tbody");
removeUnitLines();
options.military.map(u => addUnitLine(u));
$("#militaryOptions").dialog({
title: "Edit Military Units", resizable: false, width: fitContent(),
position: {my: "center", at: "center", of: "svg"},
buttons: {
Apply: function() {applyMilitaryOptions(); $(this).dialog("close");},
Add: () => addUnitLine({name: "custom", rural: 0.2, urban: 0.5, crew: 1, type: "default"}),
Restore: restoreDefaultUnits,
Cancel: function() {$(this).dialog("close");}
}, open: function() {
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
buttons[0].addEventListener("mousemove", () => tip("Apply military units settings. All forces will be recalculated!"));
buttons[1].addEventListener("mousemove", () => tip("Add new military unit to the table"));
buttons[2].addEventListener("mousemove", () => tip("Restore default military units and settings"));
buttons[3].addEventListener("mousemove", () => tip("Close the window without saving the changes"));
}
});
function removeUnitLines() {
table.querySelectorAll("tr").forEach(el => el.remove());
}
function addUnitLine(u) {
const row = `
|
|
|
|
|
|
|
`;
table.insertAdjacentHTML("beforeend", row);
}
function restoreDefaultUnits() {
removeUnitLines();
[{name:"infantry", rural:.25, urban:.2, crew:1, type:"melee", separate:0},
{name:"archers", rural:.12, urban:.2, crew:1, type:"ranged", separate:0},
{name:"cavalry", rural:.12, urban:.03, crew:3, type:"mounted", separate:0},
{name:"artillery", rural:0, urban:.03, crew:8, type:"machinery", separate:0},
{name:"fleet", rural:0, urban:.015, crew:100, type:"naval", separate:1}].map(u => addUnitLine(u));
}
function applyMilitaryOptions() {
options.military = Array.from(table.querySelectorAll("tr")).map(r => {
const [name, rural, urban, crew, type, separate] = Array.from(r.querySelectorAll("input, select")).map(d => d.value||d.checked);
return {name:name.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, '_'), rural:+rural||0, urban:+urban||0, crew:+crew||0, type, separate:+separate||0};
});
localStorage.setItem("military", JSON.stringify(options.military));
Military.generate();
updateHeaders();
addLines();
}
}
function militaryRecalculate() {
alertMessage.innerHTML = "Are you sure you want to recalculate military forces for all states?";
$("#alert").dialog({resizable: false, title: "Remove regiment",
buttons: {
Recalculate: function() {
$(this).dialog("close");
Military.generate();
addLines();
},
Cancel: function() {$(this).dialog("close");}
}
});
}
function downloadMilitaryData() {
const units = options.military.map(u => u.name);
let data = "Id,State,"+units.map(u => capitalize(u)).join(",")+",Total,Population,Rate,War Alert\n"; // headers
body.querySelectorAll(":scope > div").forEach(function(el) {
data += el.dataset.id + ",";
data += el.dataset.state + ",";
data += units.map(u => el.dataset[u]).join(",") + ",";
data += el.dataset.total + ",";
data += el.dataset.population + ",";
data += rn(el.dataset.rate,2) + "%,";
data += el.dataset.alert + "\n";
});
const name = getFileName("Military") + ".csv";
downloadFile(data, name);
}
}