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