border tweak

This commit is contained in:
33333-33333 2026-05-26 16:56:18 +09:00
commit 71b58cf033
7 changed files with 514 additions and 68 deletions

View file

@ -132,23 +132,24 @@ export function generateMapFeatures(seed, terrain) {
spine * 0.12
);
const clusterNoise = 0.72 + fbm(x * 0.34 + 13, y * 0.34 - 31, seed + 7001) * 0.46 + valueNoise(x, y, seed + 7002, 8) * 0.16;
settlementCluster[i] = clamp((developable[i] * 0.52 + valleySettlement[i] * 0.28 + coastalSettlement[i] * 0.18 + agriculture[i] * 0.22) * clusterNoise);
settlementCluster[i] = clamp((developable[i] * 0.50 + valleySettlement[i] * 0.26 + coastalSettlement[i] * 0.18 + agriculture[i] * 0.34 + plain[i] * 0.12) * clusterNoise);
ruralSuitability[i] = clamp(
agriculture[i] * 0.42 +
developable[i] * 0.28 +
agriculture[i] * 0.54 +
developable[i] * 0.30 +
valleySettlement[i] * 0.24 +
coastalSettlement[i] * 0.15 +
settlementCluster[i] * 0.24 -
settlementCluster[i] * 0.30 -
Math.max(0, elevation[i] - 0.64) * 0.56
);
townSuitability[i] = clamp(
developable[i] * 0.40 +
valleySettlement[i] * 0.26 +
developable[i] * 0.38 +
agriculture[i] * 0.18 +
valleySettlement[i] * 0.24 +
coastalSettlement[i] * 0.20 +
confluence * 0.34 +
basinField[i] * 0.16 +
plain[i] * 0.12 +
settlementCluster[i] * 0.16 -
plain[i] * 0.18 +
settlementCluster[i] * 0.22 -
slope[i] * 0.34 -
ridgeField[i] * 0.17 -
spine * 0.10
@ -303,21 +304,21 @@ export function generateMapFeatures(seed, terrain) {
const villageScore = new Float32Array(SIZE);
for (let i = 0; i < SIZE; i++) {
if (sea[i]) continue;
villageScore[i] = clamp(ruralSuitability[i] * 0.68 + valleySettlement[i] * 0.26 + coastalSettlement[i] * 0.18 + settlementCluster[i] * 0.08);
villageScore[i] = clamp(ruralSuitability[i] * 0.60 + agriculture[i] * 0.30 + plain[i] * 0.15 + valleySettlement[i] * 0.24 + coastalSettlement[i] * 0.16 + settlementCluster[i] * 0.16);
}
const villages = pickRegionalPoints(villageScore, {
stride: 2,
threshold: 0.25 + rand(seed, 1031) * 0.04,
totalMax: 140,
minDistance: 5,
threshold: 0.18 + rand(seed, 1031) * 0.030,
totalMax: 280,
minDistance: 4,
seedOffset: 1030,
kind: "Village",
quotaForRegion: (regionId, st) => {
if (!st || st.developableCells < 10) return 0;
const vf = visibilityFactor(regionId, st);
const raw = (st.developableCells / 65 + st.valleyCells / 44 + st.coastCells / 55 + 1.2) * vf;
const min = st.area > 2600 ? 7 : st.area > 1400 ? 4 : st.area > 520 ? 2 : st.area > 220 ? 1 : 0;
const max = st.area > 3600 ? 24 : st.area > 2200 ? 17 : st.area > 900 ? 9 : 4;
const raw = (st.developableCells / 28 + st.plainCells / 42 + st.valleyCells / 34 + st.coastCells / 46 + 3.2) * vf;
const min = st.area > 2600 ? 20 : st.area > 1400 ? 12 : st.area > 520 ? 5 : st.area > 220 ? 2 : 0;
const max = st.area > 3600 ? 72 : st.area > 2200 ? 50 : st.area > 900 ? 25 : 10;
return Math.round(clamp(raw + rand(seed, 1033 + regionId * 19) * 1.5, min, max));
},
}).map((p, n) => {
@ -341,8 +342,10 @@ export function generateMapFeatures(seed, terrain) {
);
const valleyMouth = valleyField[i] > 0.22 && (plain[i] > 0.22 || basinField[i] > 0.18 || coastalLowland[i] > 0.18) ? 0.14 : 0;
marketScore[i] = clamp(
townSuitability[i] * 0.62 +
villageInfluence[i] * 0.38 +
townSuitability[i] * 0.58 +
agriculture[i] * 0.16 +
plain[i] * 0.14 +
villageInfluence[i] * 0.40 +
featurePull +
valleyMouth +
basinField[i] * 0.12 +
@ -356,17 +359,17 @@ export function generateMapFeatures(seed, terrain) {
const markets = pickRegionalPoints(marketScore, {
stride: 2,
threshold: 0.31 + rand(seed, 1041) * 0.045,
totalMax: 52,
minDistance: 9,
threshold: 0.245 + rand(seed, 1041) * 0.035,
totalMax: 110,
minDistance: 6,
seedOffset: 1040,
kind: "Market Town",
quotaForRegion: (regionId, st) => {
if (!st || st.townCells < 8) return 0;
const vf = visibilityFactor(regionId, st);
const raw = (st.developableCells / 260 + st.valleyCells / 150 + st.coastCells / 160 + 0.8) * vf;
const min = st.area > 2600 ? 3 : st.area > 1200 ? 2 : st.area > 520 ? 1 : 0;
const max = st.area > 3600 ? 9 : st.area > 2200 ? 7 : st.area > 800 ? 4 : 2;
const raw = (st.developableCells / 105 + st.plainCells / 125 + st.valleyCells / 105 + st.coastCells / 130 + 2.1) * vf;
const min = st.area > 2600 ? 9 : st.area > 1200 ? 5 : st.area > 520 ? 2 : 0;
const max = st.area > 3600 ? 30 : st.area > 2200 ? 22 : st.area > 800 ? 10 : 5;
return Math.round(clamp(raw + rand(seed, 1043 + regionId * 23) * 0.8, min, max));
},
extraScore: (x, y, i) => (distanceToNearest(commercialPorts, x, y) < 8 ? 0.07 : 0) + confluenceField[i] * 0.08,
@ -779,8 +782,36 @@ export function generateMapFeatures(seed, terrain) {
const satelliteCities = [];
const newTowns = [];
const logisticsParks = [];
const interchanges = [];
const logisticsScore = new Float32Array(SIZE);
for (let y = 2; y < MAP_H - 2; y++) {
for (let x = 2; x < MAP_W - 2; x++) {
const i = indexOf(x, y);
if (sea[i]) continue;
const flatAgriculturalCorridor = clamp(
agriculture[i] * 0.34 +
plain[i] * 0.24 +
developable[i] * 0.20 +
basinField[i] * 0.12 +
coastalLowland[i] * 0.08 +
roadInfluence[i] * 0.26 +
railInfluence2[i] * 0.18 +
stationInfluence[i] * 0.10 -
slope[i] * 0.44 -
ridgeField[i] * 0.32
);
const nearMajorCity = modernCities.some((c) => Math.hypot(c.x - x, c.y - y) < 4);
logisticsScore[i] = nearMajorCity ? 0 : flatAgriculturalCorridor;
}
}
const logisticsParks = pickGlobalPoints(logisticsScore, {
threshold: 0.34,
max: 18,
minDistance: 12,
seedOffset: 1450,
predicate: (x, y, i) => logisticsScore[i] > 0.30 && (roadInfluence[i] > 0.10 || railInfluence2[i] > 0.08 || stationInfluence[i] > 0.08),
}).map((p) => ({ ...p, kind: "Logistics Park", score: logisticsScore[indexOf(p.x, p.y)], population: 0 }));
const logisticsInfluence = influenceFromPoints(logisticsParks, 4.8, () => 1.0);
var landuse = new Uint8Array(SIZE);
// Re-run land-use classification after landuse allocation. The loop above is
@ -856,6 +887,10 @@ export function generateMapFeatures(seed, terrain) {
landuse[i] = LANDUSE.INDUSTRIAL;
continue;
}
if (logisticsInfluence[i] > 0.24 && urbanCapacity[i] > 0.08 && (roadInfluence[i] > 0.08 || railInfluence2[i] > 0.06)) {
landuse[i] = LANDUSE.LOGISTICS;
continue;
}
if (core > 0.38 && urbanCapacity[i] > 0.10) {
landuse[i] = LANDUSE.CBD;
continue;
@ -873,10 +908,11 @@ export function generateMapFeatures(seed, terrain) {
landuse[i] = LANDUSE.SUBURB;
} else if (roadLanduseInfluence[i] > 0.30 && urbanCapacity[i] > 0.10 && (townInfluence[i] > 0.05 || cityInfluence[i] > 0.11)) {
landuse[i] = LANDUSE.SUBURB;
} else if (agriculture[i] > 0.24 || rural > 0.22 || (developable[i] > 0.18 && plain[i] > 0.18)) {
} else if (agriculture[i] > 0.16 || rural > 0.18 || (developable[i] > 0.13 && plain[i] > 0.13) || (basinField[i] > 0.18 && slope[i] < 0.34) || (coastalLowland[i] > 0.16 && slope[i] < 0.32)) {
landuse[i] = LANDUSE.FARMLAND;
} else {
landuse[i] = elevation[i] > 0.52 || slope[i] > 0.34 ? LANDUSE.FOREST : LANDUSE.RURAL;
const usablePlain = slope[i] < 0.30 && (plain[i] > 0.18 || developable[i] > 0.20 || basinField[i] > 0.20 || coastalLowland[i] > 0.18);
landuse[i] = elevation[i] > 0.58 || slope[i] > 0.38 ? LANDUSE.FOREST : usablePlain ? LANDUSE.FARMLAND : LANDUSE.RURAL;
}
}
}
@ -916,6 +952,22 @@ export function generateMapFeatures(seed, terrain) {
}
}
for (const park of logisticsParks) {
const r = 3;
for (let dy = -r; dy <= r; dy++) {
for (let dx = -r; dx <= r; dx++) {
const x = park.x + dx;
const y = park.y + dy;
if (!inside(x, y)) continue;
const i = indexOf(x, y);
if (sea[i] || landuse[i] === LANDUSE.CBD || landuse[i] === LANDUSE.FOREST) continue;
if (Math.hypot(dx, dy) <= r && (agriculture[i] > 0.18 || plain[i] > 0.15 || roadInfluence[i] > 0.06 || railInfluence2[i] > 0.05)) {
landuse[i] = LANDUSE.LOGISTICS;
}
}
}
}
if (maxDensity > 0) {
for (let i = 0; i < SIZE; i++) {
if (sea[i]) continue;
@ -923,6 +975,8 @@ export function generateMapFeatures(seed, terrain) {
let floor = ruralDensityFloor[i];
if (lu === LANDUSE.FARMLAND) {
floor = Math.max(floor, clamp(0.024 + agriculture[i] * 0.044 + ruralSuitability[i] * 0.024 + roadDensityInfluence[i] * 0.030 + stationDensityInfluence[i] * 0.026 + villageInfluence[i] * 0.018, 0, 0.110));
} else if (lu === LANDUSE.LOGISTICS) {
floor = Math.max(floor, clamp(0.018 + roadDensityInfluence[i] * 0.026 + railInfluence2[i] * 0.014 + logisticsInfluence[i] * 0.012, 0, 0.060));
} else if (lu === LANDUSE.RURAL) {
floor = Math.max(floor, clamp(0.012 + ruralSuitability[i] * 0.020 + developable[i] * 0.014 + roadDensityInfluence[i] * 0.024 + stationDensityInfluence[i] * 0.020, 0, 0.070));
} else if (lu === LANDUSE.FOREST) {