refactor: drawMilitary

This commit is contained in:
Azgaar 2024-09-05 23:48:38 +02:00
parent e58cda2a5d
commit 0ea7e3a86d
12 changed files with 167 additions and 166 deletions

View file

@ -8108,5 +8108,6 @@
<script defer src="modules/renderers/draw-scalebar.js?v=1.103.00"></script> <script defer src="modules/renderers/draw-scalebar.js?v=1.103.00"></script>
<script defer src="modules/renderers/draw-temperature.js?v=1.103.00"></script> <script defer src="modules/renderers/draw-temperature.js?v=1.103.00"></script>
<script defer src="modules/renderers/draw-emblems.js?v=1.103.00"></script> <script defer src="modules/renderers/draw-emblems.js?v=1.103.00"></script>
<script defer src="modules/renderers/draw-military.js?v=1.103.00"></script>
</body> </body>
</html> </html>

View file

@ -80,7 +80,7 @@ let labels = viewbox.append("g").attr("id", "labels");
let icons = viewbox.append("g").attr("id", "icons"); let icons = viewbox.append("g").attr("id", "icons");
let burgIcons = icons.append("g").attr("id", "burgIcons"); let burgIcons = icons.append("g").attr("id", "burgIcons");
let anchors = icons.append("g").attr("id", "anchors"); let anchors = icons.append("g").attr("id", "anchors");
let armies = viewbox.append("g").attr("id", "armies").style("display", "none"); let armies = viewbox.append("g").attr("id", "armies");
let markers = viewbox.append("g").attr("id", "markers"); let markers = viewbox.append("g").attr("id", "markers");
let fogging = viewbox let fogging = viewbox
.append("g") .append("g")

View file

@ -944,5 +944,8 @@ export function resolveVersionConflicts(mapVersion) {
// v1.103.00 separated pole of inaccessibility detection from layer rendering // v1.103.00 separated pole of inaccessibility detection from layer rendering
BurgsAndStates.getPoles(); BurgsAndStates.getPoles();
Provinces.getPoles(); Provinces.getPoles();
// v1.103.00 removed regiments from initial render
viewbox.select("#armies").style("display", null);
} }
} }

View file

@ -2,7 +2,7 @@
window.Military = (function () { window.Military = (function () {
const generate = function () { const generate = function () {
TIME && console.time("generateMilitaryForces"); TIME && console.time("generateMilitary");
const {cells, states} = pack; const {cells, states} = pack;
const {p} = cells; const {p} = cells;
const valid = states.filter(s => s.i && !s.removed); // valid states const valid = states.filter(s => s.i && !s.removed); // valid states
@ -252,8 +252,6 @@ window.Military = (function () {
delete s.temp; // do not store temp data delete s.temp; // do not store temp data
}); });
redraw();
function createRegiments(nodes, s) { function createRegiments(nodes, s) {
if (!nodes.length) return []; if (!nodes.length) return [];
@ -312,19 +310,9 @@ window.Military = (function () {
return regiments; return regiments;
} }
TIME && console.timeEnd("generateMilitaryForces"); TIME && console.timeEnd("generateMilitary");
}; };
function redraw() {
const validStates = pack.states.filter(s => s.i && !s.removed);
armies.selectAll("g > g").each(function () {
const index = notes.findIndex(n => n.id === this.id);
if (index != -1) notes.splice(index, 1);
});
armies.selectAll("g").remove();
validStates.forEach(s => drawRegiments(s.military, s.i));
}
const getDefaultOptions = function () { const getDefaultOptions = function () {
return [ return [
{icon: "⚔️", name: "infantry", rural: 0.25, urban: 0.2, crew: 1, power: 1, type: "melee", separate: 0}, {icon: "⚔️", name: "infantry", rural: 0.25, urban: 0.2, crew: 1, power: 1, type: "melee", separate: 0},
@ -335,122 +323,6 @@ window.Military = (function () {
]; ];
}; };
const drawRegiments = function (regiments, s) {
const size = +armies.attr("box-size");
const w = d => (d.n ? size * 4 : size * 6);
const h = size * 2;
const x = d => rn(d.x - w(d) / 2, 2);
const y = d => rn(d.y - size, 2);
const baseColor = pack.states[s].color[0] === "#" ? pack.states[s].color : "#999";
const darkerColor = d3.color(baseColor).darker().hex();
const army = armies
.append("g")
.attr("id", "army" + s)
.attr("fill", baseColor)
.attr("color", darkerColor);
const g = army
.selectAll("g")
.data(regiments)
.enter()
.append("g")
.attr("id", d => "regiment" + s + "-" + d.i)
.attr("data-name", d => d.name)
.attr("data-state", s)
.attr("data-id", d => d.i)
.attr("transform", d => (d.angle ? `rotate(${d.angle})` : null))
.attr("transform-origin", d => `${d.x}px ${d.y}px`);
g.append("rect")
.attr("x", d => x(d))
.attr("y", d => y(d))
.attr("width", d => w(d))
.attr("height", h);
g.append("text")
.attr("x", d => d.x)
.attr("y", d => d.y)
.text(d => getTotal(d));
g.append("rect")
.attr("fill", "currentColor")
.attr("x", d => x(d) - h)
.attr("y", d => y(d))
.attr("width", h)
.attr("height", h);
g.append("text")
.attr("class", "regimentIcon")
.attr("x", d => x(d) - size)
.attr("y", d => d.y)
.text(d => d.icon);
};
const drawRegiment = function (reg, stateId) {
const size = +armies.attr("box-size");
const w = reg.n ? size * 4 : size * 6;
const h = size * 2;
const x1 = rn(reg.x - w / 2, 2);
const y1 = rn(reg.y - size, 2);
let army = armies.select("g#army" + stateId);
if (!army.size()) {
const baseColor = pack.states[stateId].color[0] === "#" ? pack.states[stateId].color : "#999";
const darkerColor = d3.color(baseColor).darker().hex();
army = armies
.append("g")
.attr("id", "army" + stateId)
.attr("fill", baseColor)
.attr("color", darkerColor);
}
const g = army
.append("g")
.attr("id", "regiment" + stateId + "-" + reg.i)
.attr("data-name", reg.name)
.attr("data-state", stateId)
.attr("data-id", reg.i)
.attr("transform", `rotate(${reg.angle || 0})`)
.attr("transform-origin", `${reg.x}px ${reg.y}px`);
g.append("rect").attr("x", x1).attr("y", y1).attr("width", w).attr("height", h);
g.append("text").attr("x", reg.x).attr("y", reg.y).text(getTotal(reg));
g.append("rect")
.attr("fill", "currentColor")
.attr("x", x1 - h)
.attr("y", y1)
.attr("width", h)
.attr("height", h);
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 // 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); const getTotal = reg => (reg.a > (reg.n ? 999 : 99999) ? si(reg.a) : reg.a);
@ -513,13 +385,9 @@ window.Military = (function () {
return { return {
generate, generate,
redraw,
getDefaultOptions, getDefaultOptions,
getName, getName,
generateNote, generateNote,
drawRegiments,
drawRegiment,
moveRegiment,
getTotal, getTotal,
getEmblem getEmblem
}; };

View file

@ -0,0 +1,126 @@
"use strict";
function drawMilitary() {
TIME && console.time("drawMilitary");
armies.selectAll("g").remove();
pack.states.filter(s => s.i && !s.removed).forEach(s => drawRegiments(s.military, s.i));
TIME && console.timeEnd("drawMilitary");
}
const drawRegiments = function (regiments, s) {
const size = +armies.attr("box-size");
const w = d => (d.n ? size * 4 : size * 6);
const h = size * 2;
const x = d => rn(d.x - w(d) / 2, 2);
const y = d => rn(d.y - size, 2);
const baseColor = pack.states[s].color[0] === "#" ? pack.states[s].color : "#999";
const darkerColor = d3.color(baseColor).darker().hex();
const army = armies
.append("g")
.attr("id", "army" + s)
.attr("fill", baseColor)
.attr("color", darkerColor);
const g = army
.selectAll("g")
.data(regiments)
.enter()
.append("g")
.attr("id", d => "regiment" + s + "-" + d.i)
.attr("data-name", d => d.name)
.attr("data-state", s)
.attr("data-id", d => d.i)
.attr("transform", d => (d.angle ? `rotate(${d.angle})` : null))
.attr("transform-origin", d => `${d.x}px ${d.y}px`);
g.append("rect")
.attr("x", d => x(d))
.attr("y", d => y(d))
.attr("width", d => w(d))
.attr("height", h);
g.append("text")
.attr("x", d => d.x)
.attr("y", d => d.y)
.text(d => Military.getTotal(d));
g.append("rect")
.attr("fill", "currentColor")
.attr("x", d => x(d) - h)
.attr("y", d => y(d))
.attr("width", h)
.attr("height", h);
g.append("text")
.attr("class", "regimentIcon")
.attr("x", d => x(d) - size)
.attr("y", d => d.y)
.text(d => d.icon);
};
const drawRegiment = function (reg, stateId) {
const size = +armies.attr("box-size");
const w = reg.n ? size * 4 : size * 6;
const h = size * 2;
const x1 = rn(reg.x - w / 2, 2);
const y1 = rn(reg.y - size, 2);
let army = armies.select("g#army" + stateId);
if (!army.size()) {
const baseColor = pack.states[stateId].color[0] === "#" ? pack.states[stateId].color : "#999";
const darkerColor = d3.color(baseColor).darker().hex();
army = armies
.append("g")
.attr("id", "army" + stateId)
.attr("fill", baseColor)
.attr("color", darkerColor);
}
const g = army
.append("g")
.attr("id", "regiment" + stateId + "-" + reg.i)
.attr("data-name", reg.name)
.attr("data-state", stateId)
.attr("data-id", reg.i)
.attr("transform", `rotate(${reg.angle || 0})`)
.attr("transform-origin", `${reg.x}px ${reg.y}px`);
g.append("rect").attr("x", x1).attr("y", y1).attr("width", w).attr("height", h);
g.append("text").attr("x", reg.x).attr("y", reg.y).text(getTotal(reg));
g.append("rect")
.attr("fill", "currentColor")
.attr("x", x1 - h)
.attr("y", y1)
.attr("width", h)
.attr("height", h);
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);
};

View file

@ -53,7 +53,7 @@ window.Submap = (function () {
} }
}; };
stage("Resampling heightmap, temperature and precipitation."); stage("Resampling heightmap, temperature and precipitation");
// resample heightmap from old WorldState // resample heightmap from old WorldState
const n = grid.points.length; const n = grid.points.length;
grid.cells.h = new Uint8Array(n); // heightmap grid.cells.h = new Uint8Array(n); // heightmap
@ -87,7 +87,7 @@ window.Submap = (function () {
} }
if (options.depressRivers) { if (options.depressRivers) {
stage("Generating riverbeds."); stage("Generating riverbeds");
const rbeds = new Uint16Array(grid.cells.i.length); const rbeds = new Uint16Array(grid.cells.i.length);
// and erode riverbeds // and erode riverbeds
@ -96,7 +96,7 @@ window.Submap = (function () {
if (oldpc < 0) return; // ignore out-of-map marker (-1) if (oldpc < 0) return; // ignore out-of-map marker (-1)
const oldc = parentMap.pack.cells.g[oldpc]; const oldc = parentMap.pack.cells.g[oldpc];
const targetCells = forwardGridMap[oldc]; const targetCells = forwardGridMap[oldc];
if (!targetCells) throw "TargetCell shouldn't be empty."; if (!targetCells) throw "TargetCell shouldn't be empty";
targetCells.forEach(c => { targetCells.forEach(c => {
if (grid.cells.h[c] < 20) return; if (grid.cells.h[c] < 20) return;
rbeds[c] = 1; rbeds[c] = 1;
@ -110,7 +110,7 @@ window.Submap = (function () {
}); });
} }
stage("Detect features, ocean and generating lakes."); stage("Detect features, ocean and generating lakes");
markFeatures(); markFeatures();
markupGridOcean(); markupGridOcean();
@ -125,11 +125,11 @@ window.Submap = (function () {
calculateMapCoordinates(); calculateMapCoordinates();
// calculateTemperatures(); // calculateTemperatures();
// generatePrecipitation(); // generatePrecipitation();
stage("Cell cleanup."); stage("Cell cleanup");
reGraph(); reGraph();
// remove misclassified cells // remove misclassified cells
stage("Define coastline."); stage("Define coastline");
drawCoastline(); drawCoastline();
/****************************************************/ /****************************************************/
@ -147,7 +147,7 @@ window.Submap = (function () {
cells.religion = new Uint16Array(pn); cells.religion = new Uint16Array(pn);
cells.province = new Uint16Array(pn); cells.province = new Uint16Array(pn);
stage("Resampling culture, state and religion map."); stage("Resampling culture, state and religion map");
for (const [id, gridCellId] of cells.g.entries()) { for (const [id, gridCellId] of cells.g.entries()) {
const oldGridId = reverseGridMap[gridCellId]; const oldGridId = reverseGridMap[gridCellId];
if (oldGridId === undefined) { if (oldGridId === undefined) {
@ -206,13 +206,13 @@ window.Submap = (function () {
forwardMap[oldid].push(id); forwardMap[oldid].push(id);
} }
stage("Regenerating river network."); stage("Regenerating river network");
Rivers.generate(); Rivers.generate();
Lakes.defineGroup(); Lakes.defineGroup();
// biome calculation based on (resampled) grid.cells.temp and prec // biome calculation based on (resampled) grid.cells.temp and prec
// it's safe to recalculate. // it's safe to recalculate.
stage("Regenerating Biome."); stage("Regenerating Biome");
Biomes.define(); Biomes.define();
// recalculate suitability and population // recalculate suitability and population
// TODO: normalize according to the base-map // TODO: normalize according to the base-map
@ -233,11 +233,11 @@ window.Submap = (function () {
c.center = newCenters.length ? newCenters[0] : pack.cells.culture.findIndex(x => x === i); c.center = newCenters.length ? newCenters[0] : pack.cells.culture.findIndex(x => x === i);
}); });
stage("Porting and locking burgs."); stage("Porting and locking burgs");
copyBurgs(parentMap, projection, options); copyBurgs(parentMap, projection, options);
// transfer states, mark states without land as removed. // transfer states, mark states without land as removed.
stage("Porting states."); stage("Porting states");
const validStates = new Set(pack.cells.state); const validStates = new Set(pack.cells.state);
pack.states = parentMap.pack.states; pack.states = parentMap.pack.states;
// keep valid states and neighbors only // keep valid states and neighbors only
@ -253,7 +253,7 @@ window.Submap = (function () {
}); });
// transfer provinces, mark provinces without land as removed. // transfer provinces, mark provinces without land as removed.
stage("Porting provinces."); stage("Porting provinces");
const validProvinces = new Set(pack.cells.province); const validProvinces = new Set(pack.cells.province);
pack.provinces = parentMap.pack.provinces; pack.provinces = parentMap.pack.provinces;
// mark uneccesary provinces // mark uneccesary provinces
@ -269,7 +269,7 @@ window.Submap = (function () {
BurgsAndStates.drawBurgs(); BurgsAndStates.drawBurgs();
stage("Regenerating routes network."); stage("Regenerating routes network");
regenerateRoutes(); regenerateRoutes();
drawStateLabels(); drawStateLabels();
@ -277,7 +277,7 @@ window.Submap = (function () {
Rivers.specify(); Rivers.specify();
Lakes.generateName(); Lakes.generateName();
stage("Porting military."); stage("Porting military");
for (const s of pack.states) { for (const s of pack.states) {
if (!s.military) continue; if (!s.military) continue;
for (const m of s.military) { for (const m of s.military) {
@ -288,9 +288,9 @@ window.Submap = (function () {
} }
s.military = s.military.filter(m => m.cell).map((m, i) => ({...m, i})); s.military = s.military.filter(m => m.cell).map((m, i) => ({...m, i}));
} }
Military.redraw(); drawMilitary();
stage("Copying markers."); stage("Copying markers");
for (const m of pack.markers) { for (const m of pack.markers) {
const [x, y] = projection(m.x, m.y); const [x, y] = projection(m.x, m.y);
if (!inMap(x, y)) { if (!inMap(x, y)) {
@ -304,14 +304,14 @@ window.Submap = (function () {
} }
if (layerIsOn("toggleMarkers")) drawMarkers(); if (layerIsOn("toggleMarkers")) drawMarkers();
stage("Redraw emblems."); stage("Redraw emblems");
drawEmblems(); drawEmblems();
stage("Regenerating Zones."); stage("Regenerating Zones");
Zones.generate(); Zones.generate();
Names.getMapName(); Names.getMapName();
stage("Restoring Notes."); stage("Restoring Notes");
notes = parentMap.notes; notes = parentMap.notes;
stage("Submap done."); stage("Submap done");
WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`); WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`);
showStatistics(); showStatistics();

View file

@ -277,7 +277,7 @@ class Battle {
const shift = side === "attackers" ? attackers.length * -8 : (defenders.length - 1) * 8; const shift = side === "attackers" ? attackers.length * -8 : (defenders.length - 1) * 8;
regiment.px = regiment.x; regiment.px = regiment.x;
regiment.py = regiment.y; regiment.py = regiment.y;
Military.moveRegiment(regiment, defenders[0].x, defenders[0].y + shift); moveRegiment(regiment, defenders[0].x, defenders[0].y + shift);
}); });
} }
@ -909,7 +909,7 @@ class Battle {
cancelResults() { cancelResults() {
// move regiments back to initial positions // move regiments back to initial positions
this.attackers.regiments.concat(this.defenders.regiments).forEach(r => Military.moveRegiment(r, r.px, r.py)); this.attackers.regiments.concat(this.defenders.regiments).forEach(r => moveRegiment(r, r.px, r.py));
$("#battleScreen").dialog("close"); $("#battleScreen").dialog("close");
this.cleanData(); this.cleanData();
} }

View file

@ -185,6 +185,7 @@ function restoreLayers() {
if (layerIsOn("toggleBorders")) drawBorders(); if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleStates")) drawStates(); if (layerIsOn("toggleStates")) drawStates();
if (layerIsOn("toggleRivers")) drawRivers(); if (layerIsOn("toggleRivers")) drawRivers();
if (layerIsOn("toggleMilitary")) drawMilitary();
} }
function toggleHeight(event) { function toggleHeight(event) {
@ -824,11 +825,11 @@ function drawRoutes() {
function toggleMilitary(event) { function toggleMilitary(event) {
if (!layerIsOn("toggleMilitary")) { if (!layerIsOn("toggleMilitary")) {
turnButtonOn("toggleMilitary"); turnButtonOn("toggleMilitary");
$("#armies").fadeIn(); drawMilitary();
if (event && isCtrlClick(event)) editStyle("armies"); if (event && isCtrlClick(event)) editStyle("armies");
} else { } else {
if (event && isCtrlClick(event)) return editStyle("armies"); if (event && isCtrlClick(event)) return editStyle("armies");
$("#armies").fadeOut(); armies.selectAll("g").remove();
turnButtonOff("toggleMilitary"); turnButtonOff("toggleMilitary");
} }
} }

View file

@ -246,7 +246,7 @@ function editRegiment(selector) {
reg.name = Military.getName(reg, military); reg.name = Military.getName(reg, military);
military.push(reg); military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend Military.generateNote(reg, pack.states[state]); // add legend
Military.drawRegiment(reg, state); drawRegiment(reg, state);
if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click(); if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click();
toggleAdd(); toggleAdd();
} }
@ -296,7 +296,7 @@ function editRegiment(selector) {
(defender.px = defender.x), (defender.py = defender.y); (defender.px = defender.x), (defender.py = defender.y);
// move attacker to defender // move attacker to defender
Military.moveRegiment(attacker, defender.x, defender.y - 8); moveRegiment(attacker, defender.x, defender.y - 8);
// draw battle icon // draw battle icon
const attack = d3 const attack = d3

View file

@ -179,7 +179,7 @@ function overviewRegiments(state) {
reg.name = Military.getName(reg, military); reg.name = Military.getName(reg, military);
military.push(reg); military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend Military.generateNote(reg, pack.states[state]); // add legend
Military.drawRegiment(reg, state); drawRegiment(reg, state);
toggleAdd(); toggleAdd();
} }

View file

@ -953,7 +953,7 @@ styleArmiesSize.on("input", e => {
armies.selectAll("g").remove(); // clear armies layer armies.selectAll("g").remove(); // clear armies layer
pack.states.forEach(s => { pack.states.forEach(s => {
if (!s.i || s.removed || !s.military.length) return; if (!s.i || s.removed || !s.military.length) return;
Military.drawRegiments(s.military, s.i); drawRegiments(s.military, s.i);
}); });
}); });

View file

@ -506,8 +506,8 @@ function regenerateEmblems() {
function regenerateReligions() { function regenerateReligions() {
Religions.generate(); Religions.generate();
if (!layerIsOn("toggleReligions")) toggleReligions(); if (layerIsOn("toggleReligions")) drawReligions();
else drawReligions(); else toggleReligions();
refreshAllEditors(); refreshAllEditors();
} }
@ -523,7 +523,9 @@ function regenerateCultures() {
function regenerateMilitary() { function regenerateMilitary() {
Military.generate(); Military.generate();
if (!layerIsOn("toggleMilitary")) toggleMilitary(); if (layerIsOn("toggleMilitary")) drawMilitary();
else toggleMilitary();
if (byId("militaryOverviewRefresh").offsetParent) militaryOverviewRefresh.click(); if (byId("militaryOverviewRefresh").offsetParent) militaryOverviewRefresh.click();
} }