PSO2SERVER/Server/Models/RevealedRegions.cs

208 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PSO2SERVER.Models
{
/// <summary>
/// Revealed minimap regions
/// </summary>
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();
}
}
/// <summary>
/// 获取指定行列位置的位值
/// </summary>
/// <param name="row">行索引0 到 7</param>
/// <param name="col">列索引0 到 9</param>
/// <returns>该位置的位值true = 已揭示false = 未揭示)</returns>
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;
}
/// <summary>
/// 设置指定行列位置的位值
/// </summary>
/// <param name="row">行索引0 到 7</param>
/// <param name="col">列索引0 到 9</param>
/// <param name="value">设置的值true = 设置为已揭示false = 设置为未揭示)</param>
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;
}
}
}
}