?
This commit is contained in:
parent
3ac2c116bd
commit
da9d8ef904
11 changed files with 612 additions and 344 deletions
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue