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;
}
}