/** * 魔力宝贝图档解析脚本 - CGTool * * @Author HonorLee (dev@honorlee.me) * @Version 1.0 (2023-04-15) * @License GPL-3.0 * * Map.cs 服务端地图解析类 */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using UnityEngine; namespace CrossgateToolkit { //地图文件信息 public class MapFileInfo { public uint Serial; public string Name; public string FileName; } //地图块数据 public class MapBlockData { public GraphicInfoData GraphicInfo; // public uint GraphicIndex; public uint MapSerial; public int FixPlayerZ; public int ObjectZIndex = 0; } //地图信息 public class MapInfo { //地图编号 public uint Serial; //地图宽度 public int Width; //地图高度 public int Height; // 地图名称 public string Name; // 调色板号 - 默认 -1 表示自动 public int Palet = -1; //未知数据 public byte[] Unknow; //地面数据 public List GroundDatas = new List(); //地表数据 public List ObjectDatas = new List(); public bool[] BlockedIndexs; public float[] FixPlayerZs; //地图坐标二维数组,用以记录可行走区域并作为自动寻路的数据参考 public bool[,] MapNodes; } public class Map { //Z轴修正值 public static readonly int FixZIndex = 24; //缓存数据 private static Dictionary _cache = new Dictionary(); private static Dictionary _mapIndexFiles = new Dictionary(); //初始化地图文件列表 public static void Init() { DirectoryInfo mapDirectory = new DirectoryInfo(CGTool.PATH.MAP); FileInfo[] mapFiles = mapDirectory.GetFiles(); string match = @"^(\d+)_?(.+)?$"; foreach (var fileInfo in mapFiles) { string filename = fileInfo.Name; Match matchRet = Regex.Match(filename, match); if(!matchRet.Success) continue; MapFileInfo _file = new MapFileInfo(); _file.Serial = uint.Parse(matchRet.Groups[1].Value); if(matchRet.Groups.Count > 1) _file.Name = matchRet.Groups[1].Value; _file.FileName = filename; _mapIndexFiles.Add(_file.Serial, _file); } Debug.Log("[CGTool] 地图列表初始化完成,共" + _mapIndexFiles.Count + "个地图文件"); } //获取全部地图列表 public static List GetMapList() { List _list = new List(); foreach (var mapIndexFile in _mapIndexFiles) { _list.Add(mapIndexFile.Value); } return _list; } //获取地图数据 public static MapInfo GetMap(uint serial) { //返回缓存数据 if (_cache.ContainsKey(serial)) return _cache[serial]; //加载数据 MapInfo mapInfo = _loadMap(serial); return mapInfo; } //加载地图数据 private static MapInfo _loadMap(uint serial) { // CGTool.Logger.Write("开始加载时间:" + DateTime.Now); if (!_mapIndexFiles.ContainsKey(serial)) return null; // print("找到地图文件: " + mapFileInfo.Name); FileStream mapFileStream = new FileStream(CGTool.PATH.MAP + "/" + _mapIndexFiles[serial].FileName, FileMode.Open); BinaryReader mapFileReader = new BinaryReader(mapFileStream); MapInfo mapInfo = new MapInfo(); mapInfo.Serial = serial; bool isClientMapFile = false; //地图文件头 byte[] mapHeader = mapFileReader.ReadBytes( 6); if(mapHeader[0]==0x4C && mapHeader[1]==0x53 && mapHeader[2]==0x32 && mapHeader[3]==0x4D && mapHeader[4]==0x41 && mapHeader[5]==0x50){ isClientMapFile = false; Debug.Log("[CGTool] 地图文件头: 服务端地图"); }else if (mapHeader[0]==0x4D && mapHeader[1]==0x41 && mapHeader[2]==0x50){ isClientMapFile = true; Debug.Log("[CGTool] 地图文件头: 客户端地图"); } else { Debug.LogError("[CGTool] 地图文件头错误: " + _mapIndexFiles[serial].FileName); return null; } byte[] bytes; if (isClientMapFile) { // 无用信息 mapFileReader.ReadBytes(6); //读取地图宽度 bytes = mapFileReader.ReadBytes(4); mapInfo.Width = BitConverter.ToUInt16(bytes,0); //读取地图高度 bytes = mapFileReader.ReadBytes(4); mapInfo.Height = BitConverter.ToUInt16(bytes,0); if (MapExtra.ClientMapExtraDatas.ContainsKey(serial)) { mapInfo.Name = MapExtra.ClientMapExtraDatas[serial].Name; mapInfo.Palet = MapExtra.ClientMapExtraDatas[serial].Palet; } else { mapInfo.Name = "未知领域"; mapInfo.Palet = -1; } } else { // 无用信息 mapFileReader.ReadBytes(2); //地图名称 byte[] mapNameBytes = mapFileReader.ReadBytes(32); string[] mapHead = System.Text.Encoding.GetEncoding("GBK").GetString(mapNameBytes).Split('|'); mapInfo.Name = mapHead[0]; // 调色板 if (mapHead.Length>1){ if(mapHead[1] != null || mapHead[1] != "") mapInfo.Palet = int.Parse(mapHead[1]); } //读取地图宽度 bytes = mapFileReader.ReadBytes(2); Array.Reverse(bytes); mapInfo.Width = BitConverter.ToUInt16(bytes,0); //读取地图高度 bytes = mapFileReader.ReadBytes(2); Array.Reverse(bytes); mapInfo.Height = BitConverter.ToUInt16(bytes,0); } byte[] mapBytes = mapFileReader.ReadBytes((int) (mapInfo.Width * mapInfo.Height * 2)); byte[] mapCoverBytes = mapFileReader.ReadBytes((int) (mapInfo.Width * mapInfo.Height * 2)); mapFileReader.Dispose(); mapFileReader.Close(); mapFileStream.Close(); // print(JsonUtility.ToJson(mapInfo)); BinaryReader mapReader = new BinaryReader(new MemoryStream(mapBytes)); BinaryReader mapCoverReader = new BinaryReader(new MemoryStream(mapCoverBytes)); // BinaryReader mapInfoReader = new BinaryReader(new MemoryStream(mapInfoBytes)); List tempGroundTiles = new List(); List tempObjectTiles = new List(); // CGTool.Logger.Write("开始解析时间:" + DateTime.Now); //原始数据为反转数据,即坐标起点为 1,1 排序方向为 y : 1=>0 x: 1=>0 int len = mapInfo.Width * mapInfo.Height; for (int i = 0; i < len; i++) { //地面数据 MapBlockData mapTile = null; bytes = mapReader.ReadBytes(2); if(!isClientMapFile) Array.Reverse(bytes); uint mapGraphicSerial = BitConverter.ToUInt16(bytes,0); int Version = 0; if (mapGraphicSerial > 20000) { mapGraphicSerial += 200000; Version = 1; } GraphicInfoData graphicInfoData = GraphicInfo.GetGraphicInfoData(mapGraphicSerial); if (graphicInfoData != null) { mapTile = new MapBlockData(); mapTile.GraphicInfo = graphicInfoData; mapTile.MapSerial = mapGraphicSerial; } tempGroundTiles.Add(mapTile); MapBlockData mapCoverTile = null; bytes = mapCoverReader.ReadBytes(2); if(!isClientMapFile) Array.Reverse(bytes); uint mapCoverGraphicSerial = BitConverter.ToUInt16(bytes,0); Version = 0; if (mapCoverGraphicSerial > 30000 || mapCoverGraphicSerial==25290) { mapCoverGraphicSerial += 200000; Version = 1; } graphicInfoData = GraphicInfo.GetGraphicInfoData(mapCoverGraphicSerial); if (graphicInfoData != null) { mapCoverTile = new MapBlockData(); mapCoverTile.GraphicInfo = graphicInfoData; mapCoverTile.MapSerial = mapCoverGraphicSerial; } tempObjectTiles.Add(mapCoverTile); } MapBlockData[] GroundTiles = new MapBlockData[len]; MapBlockData[] ObjectTiles = new MapBlockData[len]; bool[] blockedIndexs = new bool[len]; float[] fixPlayerZs = new float[len]; bool[,] nodes = new bool[mapInfo.Width, mapInfo.Height]; // CGTool.Logger.Write("开始排序时间:" + DateTime.Now); //重新排序 for (int y = 0; y < mapInfo.Height; y++) { for (int x = 0; x < mapInfo.Width; x++) { // int index = i * (int) mapInfo.Width + ((int) mapInfo.Width - j - 1); int _tmpindex = x + (mapInfo.Height - y - 1) * mapInfo.Width; int index = x + y * mapInfo.Width; MapBlockData mapTile = tempGroundTiles[_tmpindex]; MapBlockData ObjectTile = tempObjectTiles[_tmpindex]; GroundTiles[index] = mapTile; ObjectTiles[index] = ObjectTile; if (mapTile==null || mapTile.GraphicInfo.Blocked) blockedIndexs[index] = true; if (ObjectTile!=null && ObjectTile.GraphicInfo !=null && ObjectTile.GraphicInfo.Blocked) blockedIndexs[index] = true; nodes[x, y] = !blockedIndexs[index]; //角色默认层级 // int objectTileZIndex = index * FixZIndex; fixPlayerZs[index] = 1; } } //整理Object Z轴层级遮挡及角色遮挡问题 for (int y = 0; y < mapInfo.Height; y++) { for (int x = 0; x < mapInfo.Width; x++) { int index = x + y * mapInfo.Width; int objectTileZIndex = index * FixZIndex; MapBlockData ObjectTile = ObjectTiles[index]; if(ObjectTile==null || ObjectTile.GraphicInfo==null) continue; //Object默认层级 ObjectTile.ObjectZIndex = objectTileZIndex; //角色Z轴补正 //在自定义排序轴(1,1,-1)情况下,角色Z轴在物件y-1位置,到x+East位置,补正为48*x //在物件South+1位置,到x+East位置,补正为-48*x if (!ObjectTile.GraphicInfo.AsGround) { for(int i = x;i<(x+ObjectTile.GraphicInfo.East);i++) { int fix = 1; int oy = y - 1; int _index = (int) (oy * mapInfo.Width + i); if (fixPlayerZs[_index] == 1) fixPlayerZs[_index] = fix * (i - x + 1) * 240f + 0.1f; // fix = -1; // oy = y + ObjectTile.GraphicInfo.South; // _index = (int) (oy * mapInfo.Width + i); // if (fixPlayerZs[_index] == 0) fixPlayerZs[_index] = fix * (i - x + 1) * 100; } for(int i=y+1;i<(y+ObjectTile.GraphicInfo.South);i++) { int fix = 1; int ox = x - 1; int _index = (int) (i * mapInfo.Width + ox); if (fixPlayerZs[_index] == 1) fixPlayerZs[_index] = fix * (i - y - 1) * 240f + 0.1f; } } else { // ObjectTile.ObjectZIndex = 0; } //如果物件占地范围大于1x1,则需要处理行走限制 if ((ObjectTile.GraphicInfo.East > 1 || ObjectTile.GraphicInfo.South > 1) && ObjectTile.GraphicInfo.Blocked) { //取物件占地中间点位置 // objectTileZIndex = (x + ObjectTile.GraphicInfo.East / 2 + (y + ObjectTile.GraphicInfo.South / 2) * mapInfo.Width) * FixZIndex; // ObjectTile.ObjectZIndex = objectTileZIndex; //取物件左上角位置Z轴复写默认Z轴 // ObjectTile.ObjectZIndex = (x + (y + ObjectTile.GraphicInfo.South) * mapInfo.Width) * FixZIndex; for (int i = x; i < (x + ObjectTile.GraphicInfo.East); i++) { for (int j = y; j < (y+ ObjectTile.GraphicInfo.South); j++) { if(i>=mapInfo.Width || j>=mapInfo.Height) continue; int _index = (int) (j * mapInfo.Width + i); blockedIndexs[_index] = true; nodes[i, j] = false; } } } } } mapInfo.GroundDatas = GroundTiles.ToList(); mapInfo.ObjectDatas = ObjectTiles.ToList(); mapInfo.BlockedIndexs = blockedIndexs; mapInfo.MapNodes = nodes; mapInfo.FixPlayerZs = fixPlayerZs; _cache[serial] = mapInfo; Debug.Log("[CGTool] 读取地图: " + mapInfo.Name); Debug.Log("地图宽度: " + mapInfo.Width + " 地图高度: " + mapInfo.Height); // CGTool.Logger.Write("地图解析完成时间:" + DateTime.Now); return mapInfo; } } }