using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace JXHelper.Core; /// /// Điểm mốc bản đồ (Map Waypoint) trích xuất từ file .map chuẩn của Võ Lâm 1 / AutoVLBS. /// public record MapWaypoint(int Index, int X, int Y, int Type) { /// /// Tọa độ hiển thị ước tính trên HUD góc phải màn hình game (X / Y). /// public int HudX => (int)Math.Round(X / 256.0); public int HudY => (int)Math.Round(Y / 512.0); public string TypeDescription => Type switch { 500 => "Xa Phu / Dịch chuyển", 501 => "Tiệm Thuốc (Dược điếm)", 502 => "Tiệm Tạp Hóa", 503 => "Thợ Rèn (Tiệm Vũ khí / Sửa đồ)", 504 => "Tiệm Trang Sức", 505 => "Võ Quán", 506 => "Tửu Quán", 507 => "Tiền Trang", 511 => "Cổng Thành / Chuyển Map", _ => "Đường đi (Waypath)" }; public override string ToString() => $"Node {Index}: ({X}, {Y}) [HUD: ~{HudX}/{HudY}] - {TypeDescription}"; } /// /// Đồ thị bản đồ chứa các điểm mốc và các cạnh nối (Topological Navigation Graph). /// Hỗ trợ tìm đường ngắn nhất (A* / Dijkstra) giữa 2 điểm bất kỳ trong thành hoặc hang động. /// public class MapGraph { public int MapId { get; set; } public string MapName { get; set; } = string.Empty; public string MapFileName { get; set; } = string.Empty; public List Waypoints { get; } = new(); public Dictionary> Edges { get; } = new(); /// /// Tìm điểm mốc theo loại chức năng (VD: 500 = Xa Phu, 501 = Tiệm Thuốc, 503 = Thợ Rèn). /// public MapWaypoint? FindWaypointByType(int type) => Waypoints.FirstOrDefault(w => w.Type == type); /// /// Tìm điểm mốc gần nhất với tọa độ engine (X, Y). /// public MapWaypoint? FindNearestWaypoint(int x, int y) { if (Waypoints.Count == 0) return null; MapWaypoint? best = null; double minDistanceSq = double.MaxValue; foreach (var wp in Waypoints) { double dx = wp.X - x; double dy = wp.Y - y; double distSq = dx * dx + dy * dy; if (distSq < minDistanceSq) { minDistanceSq = distSq; best = wp; } } return best; } /// /// Tìm đường đi ngắn nhất giữa hai điểm mốc dựa trên thuật toán BFS / Dijkstra trên đồ thị. /// public List FindPath(int startNodeIndex, int targetNodeIndex) { var result = new List(); if (startNodeIndex == targetNodeIndex) { var single = Waypoints.FirstOrDefault(w => w.Index == startNodeIndex); if (single != null) result.Add(single); return result; } var queue = new Queue(); var visited = new HashSet(); var parent = new Dictionary(); queue.Enqueue(startNodeIndex); visited.Add(startNodeIndex); bool found = false; while (queue.Count > 0) { int current = queue.Dequeue(); if (current == targetNodeIndex) { found = true; break; } if (Edges.TryGetValue(current, out var neighbors)) { foreach (int next in neighbors) { if (!visited.Contains(next)) { visited.Add(next); parent[next] = current; queue.Enqueue(next); } } } } if (found) { int curr = targetNodeIndex; var pathIndices = new List(); while (curr != startNodeIndex) { pathIndices.Add(curr); curr = parent[curr]; } pathIndices.Add(startNodeIndex); pathIndices.Reverse(); var wpDict = Waypoints.ToDictionary(w => w.Index); foreach (var idx in pathIndices) { if (wpDict.TryGetValue(idx, out var wp)) { result.Add(wp); } } } return result; } } /// /// Kho dữ liệu bản đồ toàn diện (Map Database SSoT): /// Quản lý danh mục tên bản đồ, file bản đồ (.map), phân giải Map ID tĩnh và nạp đồ thị điều hướng. /// public static class MapDatabase { // Bảng danh mục bản đồ cốt lõi (SSoT) private static readonly Dictionary KnownMapNames = new() { { 1, "Phượng Tường" }, { 2, "Hoa Sơn" }, { 9, "Trường Giang Nguyên Đầu" }, { 11, "Thành Đô" }, { 13, "Nga My Phái" }, { 20, "Giang Tân Thôn" }, { 25, "Đường Môn" }, { 37, "Biện Kinh" }, { 38, "Thiết Tháp tầng 1" }, { 39, "Thiết Tháp tầng 2" }, { 40, "Thiết Tháp tầng 3" }, { 53, "Ba Lăng Huyện" }, { 54, "Chu Tiên Trấn" }, { 55, "Đào Hoa Nguyên" }, { 78, "Tương Dương" }, { 80, "Dương Châu" }, { 114, "108 La Hán Trận" }, { 116, "Tường Vân Động 1" }, { 117, "Tường Vân Động 2" }, { 118, "Tường Vân Động 3" }, { 121, "Long Môn Trấn" }, { 123, "Lão Hổ Động" }, { 124, "Cán Viên Động" }, { 132, "Băng Huyết Động" }, { 142, "Dược Vương Động 2" }, { 144, "Dược Vương Động 4" }, { 162, "Đại Lý" }, { 163, "Ác Bá Địa Đạo" }, { 164, "Thiên Tầm Tháp 1" }, { 165, "Thiên Tầm Tháp 2" }, { 166, "Thiên Tầm Tháp 3" }, { 176, "Lâm An" }, { 181, "Lưỡng Thủy Động" }, { 182, "Nghiệt Long Động" }, { 198, "Thanh Khê Động" }, { 201, "Băng Hà Động" }, { 202, "Phù Dung Động" }, { 203, "Vô Danh Động" }, { 205, "Đường Trung Động" }, { 224, "Sa Mạc Địa Biểu" }, { 225, "Sa Mạc 1" }, { 226, "Sa Mạc 2" }, { 227, "Sa Mạc 3" }, { 319, "Lâm Du Quan" }, { 320, "Chân Núi Trường Bạch" }, { 321, "Trường Bạch Sơn Nam" }, { 322, "Trường Bạch Sơn Bắc" }, { 342, "Vị Sơn Đảo" }, { 875, "Hắc Sa Động" } }; private static readonly string[] MapDirectories = new[] { @"C:\JXVCTK\JxVoCongTruyenKy\_VLAuto\Maps\CityMaps", @"C:\JXVCTK\JxVoCongTruyenKy\_VLAuto\Maps", @"C:\JXVCTK\JxVoCongTruyenKy\_AutoVLBS\VLBS19\MAPS", @"c:\Projects\JX\config\maps" }; /// /// Lấy tên tiếng Việt của bản đồ theo Map ID. /// public static string GetMapName(int mapId) { if (KnownMapNames.TryGetValue(mapId, out var name)) return name; return $"Bản đồ ID {mapId}"; } /// /// Tìm đường dẫn tới file .map theo Map ID. /// public static string? FindMapFilePath(int mapId) { string[] possibleNames = new[] { $"City{mapId}.map", $"{mapId}.map", GetSpecialMapFileName(mapId) }; foreach (var dir in MapDirectories) { if (!Directory.Exists(dir)) continue; foreach (var name in possibleNames) { if (string.IsNullOrEmpty(name)) continue; string full = Path.Combine(dir, name); if (File.Exists(full)) return full; } } return null; } private static string GetSpecialMapFileName(int mapId) => mapId switch { 123 => "LaoHo.map", 182 => "NghietLong.map", 124 => "CanVien.map", 201 => "BangHa.map", 202 => "PhuDung.map", 203 => "VoDanh.map", 205 => "DuongTrung.map", 319 => "LamDuQuan.map", 224 => "SaMacDiaBieu.map", 225 => "SaMac1.map", 226 => "SaMac2.map", 227 => "SaMac3.map", _ => string.Empty }; /// /// Nạp và phân giải toàn bộ đồ thị điều hướng từ file .map. /// public static MapGraph? LoadMapGraph(int mapId) { string? filePath = FindMapFilePath(mapId); if (filePath == null || !File.Exists(filePath)) return null; var graph = new MapGraph { MapId = mapId, MapName = GetMapName(mapId), MapFileName = Path.GetFileName(filePath) }; var lines = File.ReadAllLines(filePath); int nodeCounter = 1; bool readingGraphEdges = false; foreach (var rawLine in lines) { var line = rawLine.Trim(); if (string.IsNullOrEmpty(line)) continue; // Dấu hiệu bắt đầu danh sách cạnh đồ thị (Bắt đầu bằng $ hoặc số 0 riêng lẻ) if (line.StartsWith("$")) { readingGraphEdges = true; // Định dạng $,Node,Neighbor1,Neighbor2... var parts = line.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (parts.Length >= 2 && int.TryParse(parts[1], out int nodeIdx)) { if (!graph.Edges.ContainsKey(nodeIdx)) graph.Edges[nodeIdx] = new List(); for (int i = 2; i < parts.Length; i++) { if (int.TryParse(parts[i], out int neighbor)) { graph.Edges[nodeIdx].Add(neighbor); } } } continue; } if (line == "0") { readingGraphEdges = true; continue; } if (!readingGraphEdges) { // Đọc Waypoint: X, Y, [Type] var parts = line.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (parts.Length >= 2) { if (int.TryParse(parts[0], out int x) && int.TryParse(parts[1], out int y)) { int type = 0; if (parts.Length >= 3) { int.TryParse(parts[2], out type); } graph.Waypoints.Add(new MapWaypoint(nodeCounter++, x, y, type)); } } } else { // Cạnh đồ thị dạng "A,B" var parts = line.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (parts.Length == 2 && int.TryParse(parts[0], out int from) && int.TryParse(parts[1], out int to)) { if (!graph.Edges.ContainsKey(from)) graph.Edges[from] = new List(); if (!graph.Edges.ContainsKey(to)) graph.Edges[to] = new List(); graph.Edges[from].Add(to); graph.Edges[to].Add(from); } } } return graph; } }