using System.Text.Json.Serialization;
using SPTarkov.Common.Annotations;
namespace SPTarkov.Server.Core.Helpers;
[Injectable]
public class ContainerHelper
{
///
/// Finds a slot for an item in a given 2D container map
///
/// List of container with slots filled/free
/// Width of item
/// Height of item
/// Location to place item in container
public FindSlotResult FindSlotForItem(int[][] container2D, int itemWidth, int itemHeight)
{
var rotation = false;
var minVolume = (itemWidth < itemHeight ? itemWidth : itemHeight) - 1;
var containerY = container2D.Length;
var containerX = container2D[0].Length;
var limitY = containerY - minVolume;
var limitX = containerX - minVolume;
// Every x+y slot taken up in container, exit
if (container2D.All(x => x.All(y => y == 1)))
{
return new FindSlotResult(false);
}
// Down = y
for (var y = 0; y < limitY; y++)
{
if (container2D[y].All(x => x == 1))
// Every item in row is full, skip row
{
continue;
}
// Try each slot on the row (across = x)
for (var x = 0; x < limitX; x++)
{
var foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemWidth, itemHeight);
if (foundSlot)
{
return new FindSlotResult(true, x, y, rotation);
}
// Failed to find slot, rotate item and try again
if (!foundSlot && ItemBiggerThan1X1(itemWidth, itemHeight))
{
// Bigger than 1x1, try rotating
foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemHeight, itemWidth); // Height/Width swapped
if (foundSlot)
{
// Found a slot for it when rotated
rotation = true;
return new FindSlotResult(true, x, y, rotation);
}
}
}
}
// Tried all possible holes, nothing big enough for the item
return new FindSlotResult(false);
}
protected static bool ItemBiggerThan1X1(int itemWidth, int itemHeight)
{
return itemWidth * itemHeight > 1;
}
///
/// Find a slot inside a container an item can be placed in
///
/// Container to find space in
/// Container x size
/// Container y size
/// ???
/// ???
/// Items width
/// Items height
/// True - slot found
protected bool LocateSlot(
int[][] container2D,
int containerX,
int containerY,
int x,
int y,
int itemW,
int itemH)
{
var foundSlot = true;
for (var itemY = 0; itemY < itemH; itemY++)
{
if (foundSlot && y + itemH - 1 > containerY - 1)
{
foundSlot = false;
break;
}
// Does item fit x-ways across
for (var itemX = 0; itemX < itemW; itemX++)
{
if (foundSlot && x + itemW - 1 > containerX - 1)
{
foundSlot = false;
break;
}
if (container2D[y + itemY][x + itemX] != 0)
{
foundSlot = false;
break;
}
}
if (!foundSlot)
{
break;
}
}
return foundSlot;
}
///
/// Find a free slot for an item to be placed at
///
/// Container to place item in
/// Container x size
/// Container y size
/// Items width
/// Items height
/// is item rotated
public void FillContainerMapWithItem(
int[][] container2D,
int x,
int y,
int itemW,
int itemH,
bool rotate)
{
// Swap height/width if we want to fit it in rotated
var itemWidth = rotate ? itemH : itemW;
var itemHeight = rotate ? itemW : itemH;
for (var tmpY = y; tmpY < y + itemHeight; tmpY++)
for (var tmpX = x; tmpX < x + itemWidth; tmpX++)
{
if (container2D[tmpY][tmpX] == 0)
// Flag slot as used
{
container2D[tmpY][tmpX] = 1;
}
else
{
throw new Exception($"Slot at({x}, {y}) is already filled. Cannot fit a {itemW} by {itemH} item");
}
}
}
}
public class FindSlotResult
{
public FindSlotResult(bool success)
{
Success = success;
}
public FindSlotResult(bool success, int x, int y, bool rotation)
{
Success = success;
X = x;
Y = y;
Rotation = rotation;
}
public FindSlotResult()
{
}
[JsonPropertyName("success")]
public bool? Success
{
get;
set;
}
[JsonPropertyName("x")]
public int? X
{
get;
set;
}
[JsonPropertyName("y")]
public int? Y
{
get;
set;
}
[JsonPropertyName("rotation")]
public bool? Rotation
{
get;
set;
}
}