This commit is contained in:
33333-33333 2026-05-22 02:11:18 +09:00
commit da9d8ef904
11 changed files with 612 additions and 344 deletions

View file

@ -971,6 +971,88 @@ export function generateMapFeatures(seed, terrain) {
}
});
const requiredTransportNodes = [];
function addRequiredTransportNode(node, reason) {
if (!node || !inside(node.x, node.y) || sea[indexOf(node.x, node.y)]) return;
const key = `${node.x},${node.y}`;
if (requiredTransportNodes.some((p) => `${p.x},${p.y}` === key)) return;
requiredTransportNodes.push({ ...node, requiredTransportReason: reason });
}
addRequiredTransportNode(capital, "capital");
for (const gate of externalGateways) addRequiredTransportNode(gate, "externalGateway");
for (const city of modernCities) if ((city.population || 0) >= 120000 || city.isPrefecturalCapital) addRequiredTransportNode(city, "majorCity");
for (const port of majorPorts) addRequiredTransportNode(port, "majorPort");
const backboneAccess = new Map();
function nodeKey(p) {
return `${p.x},${p.y}`;
}
function backbonePoint(node) {
const key = nodeKey(node);
if (!backboneAccess.has(key)) {
const mode = node.requiredTransportReason === "externalGateway" ? "road" : "road";
backboneAccess.set(key, routePoint(node, mode, 18000 + node.x * 97 + node.y * 101));
}
return backboneAccess.get(key);
}
function addBackboneRoad(a, b) {
const start = backbonePoint(a);
const goal = backbonePoint(b);
const existing = [...nationalRoads, ...externalRoads, ...externalExpressways, ...railways, ...branchRailways];
const path = aStar(start, goal, makeTransportCost(roadCost, existing, roadHubs, [start, goal], 2, 4.2, townAvoidNodes, 2.6, 4.2));
if (path.length <= 3 || pathCompactness(path) > 3.8 || path.some(([x, y]) => elevation[indexOf(x, y)] > 0.72)) return false;
nationalRoads.push(path);
incrementDegree(roadDegree, a);
incrementDegree(roadDegree, b);
return true;
}
const connectedBackboneNodes = requiredTransportNodes.length ? [requiredTransportNodes[0]] : [];
const pendingBackboneNodes = requiredTransportNodes.slice(1);
let backboneEdgeCount = 0;
while (pendingBackboneNodes.length && connectedBackboneNodes.length) {
let bestIndex = -1;
let bestAnchor = null;
let bestScore = INF;
for (let i = 0; i < pendingBackboneNodes.length; i++) {
const node = pendingBackboneNodes[i];
for (const anchor of connectedBackboneNodes) {
const d = Math.hypot(node.x - anchor.x, node.y - anchor.y);
const ai = indexOf(anchor.x, anchor.y);
const bi = indexOf(node.x, node.y);
const corridor = sameCorridorAffinity(anchor, node);
const score = d * (1.0 - corridor * 0.22) + Math.max(elevation[ai], elevation[bi]) * 8 - Math.max(passSuitability[ai], passSuitability[bi]) * 4;
if (score < bestScore) {
bestScore = score;
bestIndex = i;
bestAnchor = anchor;
}
}
}
if (bestIndex < 0 || !bestAnchor) break;
const node = pendingBackboneNodes.splice(bestIndex, 1)[0];
if (addBackboneRoad(bestAnchor, node)) backboneEdgeCount++;
connectedBackboneNodes.push(node);
}
function nearestPathCellDistance(node, paths) {
let best = INF;
for (const path of paths) {
for (const [x, y] of path) best = Math.min(best, Math.hypot(node.x - x, node.y - y));
}
return best;
}
const combinedModernBackbone = [...nationalRoads, ...externalRoads, ...externalExpressways, ...railways, ...branchRailways, ...externalRailways, ...expressways];
const connectedRequiredNodeCount = requiredTransportNodes.filter((node) => nearestPathCellDistance(node, combinedModernBackbone) <= 7).length;
const missingRequiredNodes = requiredTransportNodes
.filter((node) => nearestPathCellDistance(node, combinedModernBackbone) > 7)
.map((node) => ({ x: node.x, y: node.y, kind: node.kind, reason: node.requiredTransportReason }));
const transportDebug = {
requiredNodeCount: requiredTransportNodes.length,
connectedRequiredNodeCount,
missingRequiredNodes,
backboneEdgeCount,
};
function pruneHighMountainTransport(paths, threshold = 0.82) {
for (let i = paths.length - 1; i >= 0; i--) {
if (paths[i].some(([x, y]) => elevation[indexOf(x, y)] > threshold)) paths.splice(i, 1);
@ -1327,6 +1409,7 @@ export function generateMapFeatures(seed, terrain) {
railInfluence2,
villageInfluence,
externalGateways,
transportDebug,
cityPopulationCap,
};
}