Files
2026-05-20 10:09:16 +07:00

176 lines
6.8 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
// Filename : CMoveMap.cs
// Creator : ported from C++ (AutoPFImp/AutoMoveImp/MoveMap.*)
// Date : 2026/01/09
namespace AutoMove
{
/// <summary>
/// Pathfinding map container (loads .cfg and its referenced .prmap/.pdhmap/.mlu).
/// 寻路地图容器(加载 .cfg 及其引用的 .prmap/.pdhmap/.mlu)。
/// </summary>
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<CLayerMap> m_aLayers = new List<CLayerMap>(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);
}
/// <summary>
/// Load a movemap from cfg bytes. The resolver must return bytes for referenced files.
/// 从 cfg 字节加载 movemap。resolver 必须返回引用文件的字节。
/// </summary>
public bool Load(byte[] cfgBytes, Func<string, byte[]> 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;
}
}
}
}