city tweaks
This commit is contained in:
parent
15c1031d72
commit
d762a88b22
12 changed files with 255 additions and 195 deletions
75
mapOutput.js
75
mapOutput.js
|
|
@ -1,6 +1,7 @@
|
|||
import { createNameDebug } from "./names.js";
|
||||
import { CELL_SIZE, INF, MAP_H, MAP_W, clamp, indexOf, inside, rand } from "./mapUtils.js";
|
||||
import { CELL_SIZE, INF, MAP_H, MAP_W, clamp, hash2, indexOf, inside, rand } from "./mapUtils.js";
|
||||
import { applyOutputOptions, attachIdsAndNames, recalculatePopulationAfterLanduse, tagInsidePrefecture } from "./mapGeneratorHelpers.js";
|
||||
import { LANDUSE } from "./landuseCodes.js";
|
||||
|
||||
const MUNICIPAL_SUFFIX_RE = /[市町村区]$/u;
|
||||
|
||||
|
|
@ -24,6 +25,48 @@ function municipalityNameFromRoot(root, center, fields, seed, ordinal = 0) {
|
|||
return `${value}${municipalitySuffixForCenter(center, fields, seed, ordinal)}`;
|
||||
}
|
||||
|
||||
function ensureMunicipalOfficeSettlementFootprints(adminCentersRaw, { sea, elevation, slope, ridgeField, plain, basinField, coastalLowland, populationDensity, landuse, humanRegionMask, roadInfluence, stationInfluence }, seed) {
|
||||
let changedCells = 0;
|
||||
const isBuildable = (i) => !sea[i] && (!humanRegionMask || humanRegionMask[i]) && elevation[i] < 0.78 && slope[i] < 0.62 && ridgeField[i] < 0.76;
|
||||
for (const [n, center] of (adminCentersRaw || []).entries()) {
|
||||
if (!center || !inside(center.x, center.y)) continue;
|
||||
const ci = indexOf(center.x, center.y);
|
||||
if (!isBuildable(ci)) continue;
|
||||
const existingUrban = landuse[ci] >= LANDUSE.OLD_URBAN && landuse[ci] <= LANDUSE.NEW_TOWN;
|
||||
const baseRadius = existingUrban ? 1.2 : (center.population || 0) >= 60000 ? 2.6 : 2.0;
|
||||
const scoreBoost = clamp((populationDensity[ci] || 0) * 0.45 + (roadInfluence?.[ci] || 0) * 0.22 + (stationInfluence?.[ci] || 0) * 0.26 + 0.24);
|
||||
const r = Math.ceil(baseRadius);
|
||||
for (let dy = -r; dy <= r; dy++) {
|
||||
for (let dx = -r; dx <= r; dx++) {
|
||||
const x = center.x + dx;
|
||||
const y = center.y + dy;
|
||||
if (!inside(x, y)) continue;
|
||||
const i = indexOf(x, y);
|
||||
if (!isBuildable(i)) continue;
|
||||
const d = Math.hypot(dx, dy);
|
||||
if (d > baseRadius) continue;
|
||||
const lowland = clamp(plain[i] * 0.28 + basinField[i] * 0.24 + coastalLowland[i] * 0.20 + (roadInfluence?.[i] || 0) * 0.14 - slope[i] * 0.28 - ridgeField[i] * 0.16 + 0.30);
|
||||
if (lowland <= 0.12) continue;
|
||||
if (d <= 0.85) {
|
||||
if (landuse[i] < LANDUSE.OLD_URBAN || landuse[i] === LANDUSE.FARMLAND || landuse[i] === LANDUSE.RURAL) {
|
||||
landuse[i] = LANDUSE.OLD_URBAN;
|
||||
changedCells++;
|
||||
}
|
||||
populationDensity[i] = Math.max(populationDensity[i] || 0, 0.34 + scoreBoost * 0.35);
|
||||
} else if (d <= baseRadius && landuse[i] <= LANDUSE.FARMLAND) {
|
||||
const keep = lowland * (1 - d / (baseRadius + 0.1)) + hash2(x, y, seed + 18800 + n) * 0.08;
|
||||
if (keep > 0.16) {
|
||||
landuse[i] = LANDUSE.SUBURB;
|
||||
changedCells++;
|
||||
populationDensity[i] = Math.max(populationDensity[i] || 0, 0.20 + scoreBoost * 0.20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changedCells;
|
||||
}
|
||||
|
||||
export function finishMapOutput({
|
||||
seed,
|
||||
options,
|
||||
|
|
@ -141,9 +184,11 @@ export function finishMapOutput({
|
|||
for (let i = 0; i < humanRegionMask.length; i++) {
|
||||
humanRegionMask[i] = !sea[i] && ((prefectureRegionId?.[i] ?? -1) >= 0 || prefectureMask[i]) ? 1 : 0;
|
||||
}
|
||||
const municipalOfficeUrbanizedCells = ensureMunicipalOfficeSettlementFootprints(adminCentersRaw, {
|
||||
sea, elevation, slope, ridgeField, plain, basinField, coastalLowland, populationDensity, landuse, humanRegionMask, roadInfluence, stationInfluence,
|
||||
}, seed);
|
||||
recalculatePopulationAfterLanduse(modernCities, satelliteCities, populationDensity, landuse, humanRegionMask, sea, stationInfluence, roadInfluence, railInfluence2);
|
||||
for (const city of modernCities) {
|
||||
if (city.isPrefecturalCapital) continue;
|
||||
const cap = cityPopulationCap(city);
|
||||
if (cap < INF && (city.population || 0) > cap) {
|
||||
city.population = Math.round(cap / 1000) * 1000;
|
||||
|
|
@ -153,29 +198,6 @@ export function finishMapOutput({
|
|||
}
|
||||
}
|
||||
|
||||
outputProgress("harbor works");
|
||||
|
||||
function makeHarborWorks(ports) {
|
||||
const out = [];
|
||||
for (const port of ports) {
|
||||
const parts = [];
|
||||
const limit = port.portClass === "major" ? 5 : port.portClass === "regional" ? 3 : 1;
|
||||
for (const [dx, dy] of [[1,0],[-1,0],[0,1],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]]) {
|
||||
const sx = port.x + dx;
|
||||
const sy = port.y + dy;
|
||||
if (!inside(sx, sy) || !sea[indexOf(sx, sy)]) continue;
|
||||
parts.push([[port.x, port.y], [sx, sy]]);
|
||||
const wx = sx + dx;
|
||||
const wy = sy + dy;
|
||||
if (port.portClass === "major" && inside(wx, wy) && sea[indexOf(wx, wy)] && rand(seed, sx * 101 + sy * 103) > 0.22) parts.push([[sx, sy], [wx, wy]]);
|
||||
if (parts.length >= limit) break;
|
||||
}
|
||||
if (parts.length) out.push({ port, segments: parts, kind: port.portClass === "major" ? "Major Harbor Works" : "Harbor Works" });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const harborWorks = makeHarborWorks(ports);
|
||||
let castleRuins = castles.filter((_, i) => i % 2 === 1).map((c) => ({ ...c, kind: "Castle Ruins" }));
|
||||
const nameFields = { elevation, slope, sea, river, plain, agriculture, ridgeField, valleyField, basinField, coastalLowland, flowAccum, landuse, populationDensity };
|
||||
const usedNames = new Set();
|
||||
|
|
@ -340,12 +362,11 @@ export function finishMapOutput({
|
|||
logisticsParks,
|
||||
satelliteCities,
|
||||
newTowns,
|
||||
harborWorks,
|
||||
landuse,
|
||||
adminCenters,
|
||||
adminId,
|
||||
adminBorders,
|
||||
adminDebug,
|
||||
adminDebug: adminDebug ? { ...adminDebug, municipalOfficeUrbanizedCells } : { municipalOfficeUrbanizedCells },
|
||||
castleRuins,
|
||||
riverPaths,
|
||||
mainRivers,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue