Files

115 lines
4.0 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.
namespace Ananta.Server.Protocol.Client4229938;
/// <summary>
/// 搜打撤的格子占位模型 —— 逐条镜像客户端 LX6/Manager/ExtractionShooterUtils.lua 的
/// OccupyCells / CheckCanAddItemByCell / TryGetFreeCellByOrder。
///
/// 为什么必须镜像:客户端拖拽落地前会**自己**用这套规则校验一次(CheckShiftItemInBag、
/// CheckCellAllowed、TryGetFreeCellIndexByConfigId),服务端如果只判"原点格空不空",
/// 就会出现「2x3 的物品和 1 格的物品叠在一起」——因为 2x3 的覆盖区里那些非原点格在服务端看来是空的。
///
/// 三条硬约束(照抄客户端,不要"优化"):
/// 1. 坐标是 0 基,合法范围 x∈[0,sizeX-1]、y∈[0,sizeY-1]。
/// 2. 物品占 Volume.x × Volume.y,IsRotated 时两者互换。
/// 3. 找空格是 y 外层、x 内层(先铺满一行再换行),无旋转优先、旋转次之。
/// </summary>
internal sealed class ExtractionGrid4229938
{
private readonly bool[] _cells;
internal ExtractionGrid4229938(int sizeX, int sizeY)
{
SizeX = Math.Max(1, sizeX);
SizeY = Math.Max(1, sizeY);
_cells = new bool[SizeX * SizeY];
}
internal int SizeX { get; }
internal int SizeY { get; }
/// <summary>把 Volume 按旋转换算成实际占格 (宽, 高)。</summary>
internal static (int W, int H) Rotate(int volumeX, int volumeY, bool isRotated)
=> isRotated ? (Math.Max(1, volumeY), Math.Max(1, volumeX))
: (Math.Max(1, volumeX), Math.Max(1, volumeY));
internal bool InBounds(int x, int y)
=> x >= 0 && y >= 0 && x < SizeX && y < SizeY;
internal bool IsFree(int x, int y) => InBounds(x, y) && !_cells[(y * SizeX) + x];
/// <summary>整块 (x,y,w,h) 是否都在界内且没被占。对应客户端 CheckCanAddItemByCell。</summary>
internal bool CanPlace(int x, int y, int w, int h)
{
if (x < 0 || y < 0 || w <= 0 || h <= 0)
return false;
if (x + w > SizeX || y + h > SizeY)
return false;
for (var oy = 0; oy < h; oy++)
for (var ox = 0; ox < w; ox++)
if (_cells[((y + oy) * SizeX) + x + ox])
return false;
return true;
}
internal void Occupy(int x, int y, int w, int h)
{
for (var oy = 0; oy < h; oy++)
for (var ox = 0; ox < w; ox++)
if (InBounds(x + ox, y + oy))
_cells[((y + oy) * SizeX) + x + ox] = true;
}
internal void Release(int x, int y, int w, int h)
{
for (var oy = 0; oy < h; oy++)
for (var ox = 0; ox < w; ox++)
if (InBounds(x + ox, y + oy))
_cells[((y + oy) * SizeX) + x + ox] = false;
}
/// <summary>
/// 按客户端的顺序找第一个能放下 (w,h) 的格子:y 外层、x 内层。
/// 与 TryGetFreeCellByOrder 的扫描顺序一致 —— 顺序不一致会导致服务端认为"放得下"、
/// 客户端认为"放不下",玩家看到的就是拖过去弹回。
/// </summary>
internal bool TryFirstFit(int w, int h, out int x, out int y)
{
x = 0;
y = 0;
if (w > SizeX || h > SizeY)
return false;
for (var cy = 0; cy <= SizeY - h; cy++)
for (var cx = 0; cx <= SizeX - w; cx++)
if (CanPlace(cx, cy, w, h))
{
x = cx;
y = cy;
return true;
}
return false;
}
/// <summary>先试不旋转,再试旋转。对应 TryGetFreeCellByOrderWithRotation。</summary>
internal bool TryFirstFitWithRotation(int volumeX, int volumeY, out int x, out int y, out bool rotated)
{
if (TryFirstFit(volumeX, volumeY, out x, out y))
{
rotated = false;
return true;
}
if (TryFirstFit(volumeY, volumeX, out x, out y))
{
rotated = true;
return true;
}
rotated = false;
return false;
}
}