/** * 地图布局系统 - 根据区域连接生成可视化地图 */ import { LOCATION_CONFIG } from '@/config/locations.js' /** * 构建地图图形结构 * @returns {Object} 地图数据 */ export function buildMapGraph() { const nodes = [] const edges = [] // 首先创建所有节点 for (const [id, config] of Object.entries(LOCATION_CONFIG)) { nodes.push({ id, name: config.name, type: config.type, x: 0, y: 0 }) } // 创建连接边 for (const [id, config] of Object.entries(LOCATION_CONFIG)) { if (config.connections) { for (const connId of config.connections) { // 避免重复边 const edgeExists = edges.some(e => (e.from === id && e.to === connId) || (e.from === connId && e.to === id) ) if (!edgeExists) { edges.push({ from: id, to: connId }) } } } } return { nodes, edges } } /** * 简单的力导向布局算法 * 将节点分布在合理的坐标上 * @param {Object} graph - 地图图形结构 * @param {Object} options - 布局选项 * @returns {Object} 带有坐标的节点 */ export function layoutMap(graph, options = {}) { const { nodes, edges } = graph const { width = 300, height = 400 } = options // 创建邻接表 const adj = {} for (const node of nodes) { adj[node.id] = [] } for (const edge of edges) { adj[edge.from].push(edge.to) adj[edge.to].push(edge.from) } // 找到中心节点(连接最多的节点作为起点) let centerNode = nodes[0] for (const node of nodes) { if (adj[node.id].length > adj[centerNode.id].length) { centerNode = node } } // BFS 分层布局 const levels = {} const visited = new Set() const queue = [{ id: centerNode.id, level: 0 }] while (queue.length > 0) { const { id, level } = queue.shift() if (visited.has(id)) continue visited.add(id) if (!levels[level]) levels[level] = [] levels[level].push(id) for (const neighbor of adj[id]) { if (!visited.has(neighbor)) { queue.push({ id: neighbor, level: level + 1 }) } } } // 计算节点位置 const nodePositions = {} const levelHeight = height / (Object.keys(levels).length + 1) for (const [level, nodeIds] of Object.entries(levels)) { const y = (parseInt(level) + 1) * levelHeight const levelWidth = width / (nodeIds.length + 1) nodeIds.forEach((nodeId, index) => { nodePositions[nodeId] = { x: (index + 1) * levelWidth, y } }) } // 更新节点坐标 return nodes.map(node => ({ ...node, x: nodePositions[node.id]?.x || width / 2, y: nodePositions[node.id]?.y || height / 2 })) } /** * 获取从当前位置可达的区域(包括多步可达) * @param {string} currentLocation - 当前位置ID * @param {number} maxDistance - 最大距离(步数) * @returns {Object} 可达区域信息,key为locationId,value为距离 */ export function getReachableLocations(currentLocation, maxDistance = 10) { const distances = {} const queue = [{ id: currentLocation, dist: 0 }] distances[currentLocation] = 0 while (queue.length > 0) { const { id, dist } = queue.shift() if (dist >= maxDistance) continue // 找到所有连接的节点 const connections = LOCATION_CONFIG[id]?.connections || [] for (const connId of connections) { if (distances[connId] === undefined) { distances[connId] = dist + 1 queue.push({ id: connId, dist: dist + 1 }) } } } return distances } /** * 获取地图的路径信息 * @param {string} from - 起点 * @param {string} to - 终点 * @returns {Array} 路径数组 */ export function getPath(from, to) { if (from === to) return [from] const { edges } = buildMapGraph() const adj = {} for (const node of Object.keys(LOCATION_CONFIG)) { adj[node] = LOCATION_CONFIG[node]?.connections || [] } // BFS 寻找最短路径 const queue = [[from]] const visited = new Set([from]) while (queue.length > 0) { const path = queue.shift() const current = path[path.length - 1] if (current === to) { return path } for (const neighbor of adj[current]) { if (!visited.has(neighbor)) { visited.add(neighbor) queue.push([...path, neighbor]) } } } return null // 无路径 } /** * 预定义的地图布局配置 * 为每个区域指定固定的显示位置 */ export const PREDEFINED_LAYOUT = { // 营地为中心 camp: { x: 150, y: 200 }, // 营地周围 market: { x: 50, y: 120 }, blackmarket: { x: 250, y: 120 }, wild1: { x: 150, y: 320 }, // 更远的地方 boss_lair: { x: 150, y: 440 }, basement: { x: 150, y: 540 } } /** * 使用预定义布局获取节点位置 * @param {Object} graph - 地图图形结构 * @returns {Array} 带有坐标的节点 */ export function getPredefinedLayout(graph) { const { nodes } = graph return nodes.map(node => ({ ...node, x: PREDEFINED_LAYOUT[node.id]?.x || 150, y: PREDEFINED_LAYOUT[node.id]?.y || 200 })) } /** * 获取可视化的地图数据 * @param {string} currentLocation - 当前位置 * @returns {Object} 地图数据 */ export function getMapData(currentLocation) { const graph = buildMapGraph() const layoutNodes = getPredefinedLayout(graph) // 计算画布大小 let maxX = 0, maxY = 0 for (const node of layoutNodes) { maxX = Math.max(maxX, node.x) maxY = Math.max(maxY, node.y) } // 获取可达距离 const reachable = getReachableLocations(currentLocation, 10) // 计算到每个区域的路径 const paths = {} for (const node of layoutNodes) { const path = getPath(currentLocation, node.id) if (path) { paths[node.id] = path } } return { nodes: layoutNodes, edges: graph.edges, width: maxX + 50, height: maxY + 50, currentLocation, reachable, paths } }