using System; using System.Collections.Generic; using UnityEngine; // Filename : CMoveMap.cs // Creator : ported from C++ (AutoPFImp/AutoMoveImp/MoveMap.*) // Date : 2026/01/09 namespace AutoMove { /// /// Pathfinding map container (loads .cfg and its referenced .prmap/.pdhmap/.mlu). /// 寻路地图容器(加载 .cfg 及其引用的 .prmap/.pdhmap/.mlu)。 /// public class CMoveMap { // C++: PFMAP_CFG_FILE_MAGIC (('c'<<24)|('f'<<16)|('g'<<8)|('f')) => bytes 'f''g''f''c' in LE. private const uint PFMAP_CFG_FILE_MAGIC = 0x63666766; private const uint PFMAP_CFG_FILE_VERSION = 1; // map origin (world pos of bottom-left corner) // 地图原点(左下角的世界坐标) private Vector3 m_vOrigin; private readonly List m_aLayers = new List(2); private int m_iMapWidth; private int m_iMapLength; private float m_fPixelSize = 1.0f; public int GetLayerCount() => m_aLayers.Count; public CLayerMap GetLayer(int index) => (index >= 0 && index < m_aLayers.Count) ? m_aLayers[index] : null; public int GetMapWidth() => m_iMapWidth; public int GetMapLength() => m_iMapLength; public float GetPixelSize() => m_fPixelSize; public void SetOrigin(Vector3 vOrigin) { // C++ only uses x/z. // C++ 只使用 x/z。 m_vOrigin.x = vOrigin.x; m_vOrigin.z = vOrigin.z; } public void CalcOrigin() { // C++: use first layer rmap size and pixel size, then set origin to map center. // C++:用第一层 rmap 的尺寸与像素大小,然后设置 origin 为地图中心。 if (m_aLayers.Count == 0) return; var rmap = m_aLayers[0].GetRMap(); if (rmap == null) return; rmap.GetImageSize(out m_iMapWidth, out m_iMapLength); m_fPixelSize = rmap.GetPixelSize(); m_vOrigin = Vector3.zero; m_vOrigin.x = -m_iMapWidth * m_fPixelSize * 0.5f; m_vOrigin.z = -m_iMapLength * m_fPixelSize * 0.5f; } public Vector2 TransMap2Wld(int x, int y) { return TransMap2Wld((float)x, (float)y); } public Vector2 TransMap2Wld(float mapX, float mapY) { float wx = m_vOrigin.x + (mapX + 0.5f) * m_fPixelSize; float wz = m_vOrigin.z + (mapY + 0.5f) * m_fPixelSize; return new Vector2(wx, wz); } public Vector2Int TransWld2Map(float wx, float wz) { int x = Mathf.FloorToInt((wx - m_vOrigin.x) / m_fPixelSize); int y = Mathf.FloorToInt((wz - m_vOrigin.z) / m_fPixelSize); return new Vector2Int(x, y); } public float GetDH(int iLayer, int x, int y) { var layer = GetLayer(iLayer); if (layer == null) return 0.0f; return layer.GetDH(x, y); } /// /// Load a movemap from cfg bytes. The resolver must return bytes for referenced files. /// 从 cfg 字节加载 movemap。resolver 必须返回引用文件的字节。 /// public bool Load(byte[] cfgBytes, Func resolver, string debugBasePath) { try { m_aLayers.Clear(); if (cfgBytes == null || cfgBytes.Length < 16) return false; int offset = 0; // Some PW client assets have a leading "MOXB" wrapper. // 一些完美客户端资源有 "MOXB" 前缀封装。 if (cfgBytes.Length >= 8 && cfgBytes[0] == (byte)'M' && cfgBytes[1] == (byte)'O' && cfgBytes[2] == (byte)'X' && cfgBytes[3] == (byte)'B') { offset += 4; } uint magic = BitConverter.ToUInt32(cfgBytes, offset); offset += 4; if (magic != PFMAP_CFG_FILE_MAGIC) { Debug.LogWarning($"[CMoveMap] Invalid cfg magic for {debugBasePath} magic=0x{magic:X8}"); return false; } uint ver = BitConverter.ToUInt32(cfgBytes, offset); offset += 4; if (ver != PFMAP_CFG_FILE_VERSION) { Debug.LogWarning($"[CMoveMap] Invalid cfg version for {debugBasePath} ver={ver}"); return false; } int count = BitConverter.ToInt32(cfgBytes, offset); offset += 4; if (count <= 0) return false; for (int i = 0; i < count; i++) { int len = BitConverter.ToInt32(cfgBytes, offset); offset += 4; string baseName = System.Text.Encoding.ASCII.GetString(cfgBytes, offset, len); offset += len; var layer = new CLayerMap(); m_aLayers.Add(layer); uint flagR = BitConverter.ToUInt32(cfgBytes, offset); offset += 4; if (flagR == 1) { byte[] prmap = resolver(baseName + ".prmap"); if (prmap == null || !layer.LoadRMap(prmap)) return false; } uint flagDH = BitConverter.ToUInt32(cfgBytes, offset); offset += 4; if (flagDH == 1) { byte[] pdhmap = resolver(baseName + ".pdhmap"); if (pdhmap == null || !layer.LoadDHMap(pdhmap)) return false; } uint flagIsl = BitConverter.ToUInt32(cfgBytes, offset); offset += 4; if (flagIsl == 1) { // island list not ported yet (rarely needed for basic route). // island 列表暂未移植(基础寻路通常不依赖)。 _ = resolver(baseName + ".isl"); } } // mlu name (we load bytes to validate presence, but we don't port MultiCluGraph yet). // mlu 名称(我们读取以验证存在,但暂不移植 MultiCluGraph)。 int mluLen = BitConverter.ToInt32(cfgBytes, offset); offset += 4; string mluBase = System.Text.Encoding.ASCII.GetString(cfgBytes, offset, mluLen); offset += mluLen; _ = resolver(mluBase + ".mlu"); // compute dimensions & default origin // 计算尺寸与默认 origin CalcOrigin(); return true; } catch (Exception ex) { Debug.LogWarning($"[CMoveMap] Load failed for {debugBasePath}: {ex.Message}"); return false; } } } }