Fix population aggregation system to eliminate double-counting

- Fixed core issue where cells.pop and burg.population were both being counted
- Changed aggregation logic across all modules to use either burg OR cell population, never both
- If cell has burg: count only burg population (represents all people in that area)
- If cell has no burg: count only cells.pop (represents scattered population)

Files modified:
- modules/burgs-and-states.js: Fixed state population aggregation
- modules/ui/provinces-editor.js: Fixed province population aggregation
- modules/dynamic/editors/cultures-editor.js: Fixed culture population aggregation
- modules/dynamic/editors/religions-editor.js: Fixed religion population aggregation
- modules/ui/biomes-editor.js: Fixed biome population aggregation
- modules/ui/zones-editor.js: Fixed zone population calculations (2 locations)
- modules/military-generator.js: Redesigned military generation to use only burg populations

Military system changes:
- Removed rural military generation (all forces now come from settlements)
- Only burgs with 500+ people can maintain military forces
- Military strength based on actual burg population (2.5% mobilization rate)

Result: Population totals now consistent across all CSV exports (~2M total vs previous 40x discrepancy)
This commit is contained in:
barrulus 2025-08-13 18:54:32 +01:00
parent 334ef2b58b
commit e669549390
18 changed files with 2960 additions and 297 deletions

View file

@ -79,9 +79,17 @@ function editZones() {
const lines = filteredZones.map(({i, name, type, cells, color, hidden}) => {
const area = getArea(d3.sum(cells.map(i => pack.cells.area[i])));
const rural = d3.sum(cells.map(i => pack.cells.pop[i])) * populationRate;
const urban =
d3.sum(cells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
// Calculate population: burg population for settled cells, cells.pop for unsettled
let rural = 0, urban = 0;
cells.forEach(i => {
if (pack.cells.burg[i]) {
// Burg represents ALL population for this cell
urban += pack.burgs[pack.cells.burg[i]].population * 1000 * urbanization;
} else {
// Only count cells.pop for unsettled areas
rural += pack.cells.pop[i] * populationRate;
}
});
const population = rn(rural + urban);
const populationTip = `Total population: ${si(population)}; Rural population: ${si(
rural
@ -412,10 +420,19 @@ function editZones() {
if (!landCells.length) return tip("Zone does not have any land cells, cannot change population", false, "error");
const burgs = pack.burgs.filter(b => !b.removed && landCells.includes(b.cell));
const rural = rn(d3.sum(landCells.map(i => pack.cells.pop[i])) * populationRate);
const urban = rn(
d3.sum(landCells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization
);
// Calculate population: burg population for settled cells, cells.pop for unsettled
let rural = 0, urban = 0;
landCells.forEach(i => {
if (pack.cells.burg[i]) {
// Burg represents ALL population for this cell
urban += pack.burgs[pack.cells.burg[i]].population * 1000 * urbanization;
} else {
// Only count cells.pop for unsettled areas
rural += pack.cells.pop[i] * populationRate;
}
});
rural = rn(rural);
urban = rn(urban);
const total = rural + urban;
const l = n => Number(n).toLocaleString();