using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PSO2SERVER.Models { /// /// Revealed minimap regions /// public class RevealedRegions { // 内部实现: // 该结构体由一个大小为[10]的字节数组组成,表示一个 8x10 的已揭示区域网格 // (即 10 列 8 行)。每一位(bit)表示一个区域是否已揭示。 // 位是从右到左索引的(即从 LSB 到 MSB),字节是从左到右索引的(arr[0]表示区域A1-8)。 // 获取该数组中的某个值可以通过如下方式: // // 示例代码: // bool GetBit(byte[] arr, int row, int col) // { // // 确保行和列的范围有效 // if (row < 0 || row >= 8 || col < 0 || col >= 10) // throw new ArgumentOutOfRangeException(); // // // 计算对应位置的位偏移 // int offset = row * 10 + col; // int byteOffset = offset / 8; // int bitOffset = offset % 8; // // // 获取相应的位值 // return (arr[byteOffset] >> bitOffset & 1) == 1; // } // 使用一个长度为 10 的字节数组,表示 8 行 10 列的区域位图。 // Lsb0 表示最低有效位在字节的最右侧。 public byte[] zones = new byte[10]; // 默认构造函数 public RevealedRegions() { } // 构造函数 - 用指定数据初始化 public RevealedRegions(byte[] data) { // 如果传入的 unk2 长度小于 10,则填充剩余部分为 0 if (data.Length <= 10) { zones = data.Concat(new byte[10 - data.Length]).ToArray(); } else { // 如果传入的 unk2 长度大于 10,则截取前 10 字节 zones = data.Take(10).ToArray(); } } /// /// 获取指定行列位置的位值 /// /// 行索引(0 到 7) /// 列索引(0 到 9) /// 该位置的位值(true = 已揭示,false = 未揭示) public bool GetBit(int row, int col) { // 确保行列值在有效范围内 if (row < 0 || row >= 8 || col < 0 || col >= 10) { throw new ArgumentOutOfRangeException(); } // 计算对应位置的偏移 int offset = row * 10 + col; int byteOffset = offset / 8; int bitOffset = offset % 8; // 获取并返回该位置的位值 return (zones[byteOffset] >> bitOffset & 1) == 1; } /// /// 设置指定行列位置的位值 /// /// 行索引(0 到 7) /// 列索引(0 到 9) /// 设置的值(true = 设置为已揭示,false = 设置为未揭示) public void SetBit(int row, int col, bool value) { // 确保行列值在有效范围内 if (row < 0 || row >= 8 || col < 0 || col >= 10) { throw new ArgumentOutOfRangeException(); } // 计算对应位置的偏移 int offset = row * 10 + col; int byteOffset = offset / 8; int bitOffset = offset % 8; // 设置对应位置的位值 if (value) { // 设置为1 zones[byteOffset] |= (byte)(1 << bitOffset); } else { // 设置为0 zones[byteOffset] &= (byte)~(1 << bitOffset); } } // 读取数据 public static RevealedRegions Read(BinaryReader reader) { try { byte[] data = reader.ReadBytes(10); if (data.Length != 10) throw new InvalidDataException("Failed to read 10 bytes for zones data."); return new RevealedRegions(data); } catch (Exception ex) { throw new PacketError("RevealedRegions", "zone_bits", ex); } } // 写入数据 public void Write(BinaryWriter writer) { try { writer.Write(zones); } catch (Exception ex) { throw new PacketError("RevealedRegions", "zone_bits", ex); } } // 索引操作,获取指定行的 10 位数据 public BitSlice GetRow(int index) { if (index < 0 || index >= 8) throw new ArgumentOutOfRangeException(); byte[] row = new byte[10]; Array.Copy(zones, index * 10, row, 0, 10); return new BitSlice(row); } // 用于调试输出 public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("["); for (int i = 0; i < 8; i++) { sb.Append(GetRow(i).ToString()); if (i < 7) sb.Append(", "); } sb.Append("]"); return sb.ToString(); } // Helper class to represent a slice of bits public class BitSlice { private byte[] data; public BitSlice(byte[] data) { this.data = data; } public override string ToString() { StringBuilder sb = new StringBuilder(); foreach (byte b in data) { sb.Append(Convert.ToString(b, 2).PadLeft(8, '0')); } return sb.ToString(); } } // Error handling class for packet-related errors public class PacketError : Exception { public string PacketName { get; } public string FieldName { get; } public new Exception InnerException { get; } public PacketError(string packetName, string fieldName, Exception innerException) : base($"Error in packet '{packetName}', field '{fieldName}'", innerException) { PacketName = packetName; FieldName = fieldName; InnerException = innerException; } } } }