This commit is contained in:
33333-33333 2026-05-29 22:00:42 +09:00
commit 6cef9a3abe
11 changed files with 1184 additions and 235 deletions

View file

@ -17,8 +17,10 @@ import { buildCoarseCostGraph, refineCoarsePath, routeCoarsePath } from "./mapTr
// 3. make sparse approximate transport paths without full-resolution A*
// 4. synthesize population and land-use fields in one raster pass
export function generateMapFeatures(seed, terrain) {
export function generateMapFeatures(seed, terrain, options = {}) {
const SPEED_TOLERANCE = 0.90;
const patchMode = options?.patchMode === true || options?.generationContext?.hasBoundaryWorld === true;
const topCenterSuppression = clamp(Number.isFinite(options?.topCenterSuppression) ? options.topCenterSuppression : (patchMode ? 0.68 : 0), 0, 0.95);
const featureTimings = [];
const nowMs = () => typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
let timingMark = nowMs();
@ -502,7 +504,11 @@ export function generateMapFeatures(seed, terrain) {
// regional pass.
modernCities.sort((a, b) => b.capacity - a.capacity || b.score - a.score);
const regionalCapitalSlots = Math.max(1, Math.min(2, Math.floor(Math.sqrt(Math.max(1, modernCities.length)) / 2)));
const baseRegionalCapitalSlots = Math.max(1, Math.min(2, Math.floor(Math.sqrt(Math.max(1, modernCities.length)) / 2)));
const regionalCapitalSlots = patchMode
? Math.max(0, Math.min(1, Math.round(baseRegionalCapitalSlots * (1 - topCenterSuppression))))
: baseRegionalCapitalSlots;
const topCenterGeoThreshold = 0.80 + topCenterSuppression * 0.16;
for (const [rank, city] of modernCities.entries()) {
const i = indexOf(city.x, city.y);
const st = regionStats.get(city.regionId);
@ -514,8 +520,12 @@ export function generateMapFeatures(seed, terrain) {
fieldValue(geoAccessibility, i, 0) * 0.18 +
Math.log10((city.capacity || 26000) + 1) / 7 * 0.26
);
const isTopCenter = rank < regionalCapitalSlots || geoTierScore > 0.8;
const isRegionalCapital = isFirstInRegion && (isTopCenter || (city.capacity || 0) > 210000 || (st?.highCentralityCells || 0) > 220);
const slotTopCenter = regionalCapitalSlots > 0 && rank < regionalCapitalSlots;
const exceptionalPatchCenter = patchMode && geoTierScore > topCenterGeoThreshold && (city.capacity || 0) > 360000;
const isTopCenter = (!patchMode && slotTopCenter) || exceptionalPatchCenter || (!patchMode && geoTierScore > topCenterGeoThreshold);
const regionalCapacityThreshold = patchMode ? 300000 : 210000;
const regionalCentralityThreshold = patchMode ? 340 : 220;
const isRegionalCapital = isFirstInRegion && (isTopCenter || (city.capacity || 0) > regionalCapacityThreshold || (st?.highCentralityCells || 0) > regionalCentralityThreshold);
const u = rand(st.seed, city.x, city.y, 9101);
const v = rand(st.seed, city.x, city.y, 9102);
const w = rand(st.seed, city.x, city.y, 9103);
@ -524,17 +534,20 @@ let rawPop;
if (isRegionalCapital) {
if (isTopCenter) {
// largest 3M - 11M
// largest 3M - 11M in full generation; patch candidates are suppressed
// unless they are exceptionally strong geographic centers.
rawPop =
3000000 +
Math.pow(u, 0.42) * 5200000 +
Math.pow(v, 3.2) * 2800000;
if (patchMode) rawPop *= (0.42 + (1 - topCenterSuppression) * 0.28);
} else {
// larger 0.25M - 2.5M
rawPop =
250000 +
Math.pow(u, 0.55) * 1450000 +
Math.pow(v, 2.4) * 900000;
if (patchMode) rawPop *= 0.72;
}
} else {
// normal 5k - 0.75k
@ -543,9 +556,9 @@ if (isRegionalCapital) {
Math.pow(u, 0.72) * 520000 +
Math.pow(v, 3.0) * 320000;
}
const capMultiplier = isRegionalCapital ? (isTopCenter ? 1.66 : 1.42) : 1.20;
const capMultiplier = isRegionalCapital ? (isTopCenter ? (patchMode ? 1.22 : 1.66) : (patchMode ? 1.08 : 1.42)) : 1.20;
const population = Math.round(Math.min(rawPop, city.capacity * capMultiplier) / 1000) * 1000;
const floor = isRegionalCapital ? (isTopCenter ? 210000 : 120000) : 42000;
const floor = isRegionalCapital ? (isTopCenter ? (patchMode ? 150000 : 210000) : (patchMode ? 90000 : 120000)) : 42000;
city.population = Math.max(floor, population);
city.isPrefecturalCapital = isPrefecturalCapital;
city.isRegionalCapital = isRegionalCapital;