Files

255 lines
5.9 KiB
JavaScript
Raw Permalink Normal View History

/**
* 地图布局系统 - 根据区域连接生成可视化地图
*/
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为locationIdvalue为距离
*/
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
}
}