diff --git a/Libraries/SPTarkov.Server.Core/Extensions/ContainerExtensions.cs b/Libraries/SPTarkov.Server.Core/Extensions/ContainerExtensions.cs
new file mode 100644
index 00000000..14ffd2cb
--- /dev/null
+++ b/Libraries/SPTarkov.Server.Core/Extensions/ContainerExtensions.cs
@@ -0,0 +1,239 @@
+using SPTarkov.Server.Core.Models.Spt.Inventory;
+
+namespace SPTarkov.Server.Core.Extensions
+{
+ public static class ContainerExtensions
+ {
+ ///
+ /// Finds a slot for an item in a given 2D container map
+ ///
+ /// List of container with positions filled/free
+ /// Width of item
+ /// Height of item
+ /// Location to place item in container
+ public static FindSlotResult FindSlotForItem(
+ this int[,] container2D,
+ int? itemX,
+ int? itemY
+ )
+ {
+ // Assume not rotated
+ var rotation = false;
+
+ // Find the min volume the item will take up
+ var minVolume = (itemX < itemY ? itemX : itemY) - 1;
+ var containerY = container2D.GetLength(0); // rows
+ var containerX = container2D.GetLength(1); // columns
+ var limitY = containerY - minVolume;
+ var limitX = containerX - minVolume;
+
+ // Every x+y slot taken up in container, exit
+ if (ContainerIsFull(container2D))
+ {
+ return new FindSlotResult(false);
+ }
+
+ // Down = y, iterate over rows
+ for (var y = 0; y < limitY; y++)
+ {
+ if (RowIsFull(container2D, y))
+ {
+ continue;
+ }
+
+ // Left to right across columns, look for free position
+ for (var x = 0; x < limitX; x++)
+ {
+ // Does item fit
+ if (
+ CanItemBePlacedInContainerAtPosition(
+ container2D,
+ x,
+ y,
+ itemX.Value,
+ itemY.Value
+ )
+ )
+ {
+ // Success, found a spot it fits
+ return new FindSlotResult(true, x, y, rotation);
+ }
+
+ if (!ItemBiggerThan1X1(itemX.Value, itemY.Value))
+ {
+ // Doesn't fit AND rotating won't help
+ continue;
+ }
+
+ // Rotate item by swapping x and y item values
+ if (
+ CanItemBePlacedInContainerAtPosition(
+ container2D,
+ x,
+ y,
+ itemY.Value, // Swapped
+ itemX.Value // Swapped
+ )
+ )
+ {
+ // Found a position for the item when rotated
+ rotation = true;
+ return new FindSlotResult(true, x, y, rotation);
+ }
+ }
+ }
+
+ // Tried all possible positions, nothing big enough for item
+ return new FindSlotResult(false);
+ }
+
+ ///
+ /// 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 static void FillContainerMapWithItem(
+ this int[,] container2D,
+ int x,
+ int y,
+ int? itemXWidth,
+ int? itemYHeight,
+ bool isRotated
+ )
+ {
+ // Swap height/width if item needs to be rotated to fit
+ var itemWidth = isRotated ? itemYHeight : itemXWidth;
+ var itemHeight = isRotated ? itemXWidth : itemYHeight;
+
+ 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 {itemXWidth} by {itemYHeight} item"
+ );
+ }
+ }
+ }
+ }
+
+ ///
+ /// Is the requested row full
+ ///
+ /// Container to check
+ /// Index of row to check
+ /// True = full
+ private static bool RowIsFull(int[,] container2D, int rowIndex)
+ {
+ var rowFull = true;
+ var containerColumnCount = container2D.GetLength(0); // rows
+ for (var col = 0; col < containerColumnCount; col++)
+ {
+ if (container2D[rowIndex, col] == 0)
+ {
+ rowFull = false;
+ break;
+ }
+ }
+
+ return rowFull;
+ }
+
+ ///
+ /// Is every slot in container full
+ ///
+ /// Container to check
+ /// True = full
+ private static bool ContainerIsFull(int[,] container2D)
+ {
+ var containerY = container2D.GetLength(0); // rows
+ var containerX = container2D.GetLength(1); // columns
+ var containerFull = true;
+ for (var y = 0; y < containerY; y++)
+ {
+ for (var x = 0; x < containerX; x++)
+ {
+ if (container2D[y, x] == 0)
+ {
+ containerFull = false;
+ break;
+ }
+ }
+ if (!containerFull)
+ {
+ break;
+ }
+ }
+
+ return containerFull;
+ }
+
+ ///
+ /// Is the item size values passed in bigger than 1x1
+ ///
+ /// Width of item
+ /// Height of item
+ /// True = bigger than 1x1
+ private static bool ItemBiggerThan1X1(int itemWidth, int itemHeight)
+ {
+ return itemWidth + itemHeight > 2;
+ }
+
+ ///
+ /// Can an item of specified size be placed inside a 2d container at a specific position
+ ///
+ /// Container to find space in
+ /// Starting x position for item
+ /// Starting y position for item
+ /// Items width
+ /// Items height
+ /// True - slot found
+ private static bool CanItemBePlacedInContainerAtPosition(
+ int[,] container,
+ int startXPos,
+ int startYPos,
+ int itemXWidth,
+ int itemYHeight
+ )
+ {
+ var containerHeight = container.GetLength(0); // Rows
+ var containerWidth = container.GetLength(1); // Columns
+
+ // Check item isn't bigger than container when at position
+ if (
+ startXPos + itemXWidth > containerWidth
+ || startYPos + itemYHeight > containerHeight
+ )
+ {
+ // Item is bigger than container, will never fit
+ return false;
+ }
+
+ // Check each slot, is any filled
+ for (var checkY = startYPos; checkY < startYPos + itemYHeight; checkY++)
+ {
+ for (var checkX = startXPos; checkX < startXPos + itemXWidth; checkX++)
+ {
+ if (container[checkY, checkX] == 1)
+ {
+ // Occupied by something
+ return false;
+ }
+ }
+ }
+
+ return true; // Slot is free
+ }
+ }
+}
diff --git a/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs b/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs
index f32869be..1c513589 100644
--- a/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs
+++ b/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs
@@ -1,4 +1,6 @@
-using SPTarkov.Server.Core.Models.Common;
+using System.Text.Json;
+using SPTarkov.Common.Extensions;
+using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
@@ -342,5 +344,42 @@ namespace SPTarkov.Server.Core.Extensions
ExtensionData = item.ExtensionData,
};
}
+
+ public static ItemLocation? GetParsedLocation(this Item item)
+ {
+ if (item.Location is null)
+ {
+ return null;
+ }
+
+ if (item.Location is JsonElement element)
+ {
+ // TODO: when is this true
+ return element.ToObject();
+ }
+
+ return (ItemLocation)item.Location;
+ }
+
+ ///
+ /// Get a list of the item IDs (NOT tpls) inside a secure container
+ ///
+ /// Inventory items to look for secure container in
+ /// List of ids
+ public static List GetSecureContainerItems(this List- items)
+ {
+ var secureContainer = items.First(x => x.SlotId == "SecuredContainer");
+
+ // No container found, drop out
+ if (secureContainer is null)
+ {
+ return [];
+ }
+
+ var itemsInSecureContainer = items.FindAndReturnChildrenByItems(secureContainer.Id);
+
+ // Return all items returned and exclude the secure container item itself
+ return itemsInSecureContainer.Where(x => x != secureContainer.Id).ToList();
+ }
}
}
diff --git a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs
index eaae244d..c006184a 100644
--- a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs
+++ b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs
@@ -21,10 +21,8 @@ namespace SPTarkov.Server.Core.Generators;
public class LocationLootGenerator(
ISptLogger _logger,
RandomUtil _randomUtil,
- HashUtil _hashUtil,
ItemHelper _itemHelper,
DatabaseService _databaseService,
- ContainerHelper _containerHelper,
PresetHelper _presetHelper,
ServerLocalisationService _serverLocalisationService,
SeasonalEventService _seasonalEventService,
@@ -600,8 +598,7 @@ public class LocationLootGenerator(
: chosenItemWithChildren.Items;
// look for open slot to put chosen item into
- var result = _containerHelper.FindSlotForItem(
- containerMap,
+ var result = containerMap.FindSlotForItem(
chosenItemWithChildren.Width,
chosenItemWithChildren.Height
);
@@ -620,8 +617,7 @@ public class LocationLootGenerator(
}
// Find somewhere for item inside container
- _containerHelper.FillContainerMapWithItem(
- containerMap,
+ containerMap.FillContainerMapWithItem(
result.X.Value,
result.Y.Value,
chosenItemWithChildren.Width,
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs
index 59f57a90..9f782d85 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs
@@ -21,7 +21,6 @@ public class BotGeneratorHelper(
DurabilityLimitsHelper _durabilityLimitsHelper,
ItemHelper _itemHelper,
InventoryHelper _inventoryHelper,
- ContainerHelper _containerHelper,
ProfileActivityService _profileActivityService,
ServerLocalisationService _serverLocalisationService,
ConfigServer _configServer
@@ -665,7 +664,7 @@ public class BotGeneratorHelper(
}
// Get x/y grid size of item
- var itemSize = _inventoryHelper.GetItemSize(
+ var (width, height) = _inventoryHelper.GetItemSize(
rootItemTplId,
rootItemId,
itemWithChildren
@@ -680,7 +679,7 @@ public class BotGeneratorHelper(
if (
slotGrid.Props?.CellsH == 0
|| slotGrid.Props?.CellsV == 0
- || itemSize[0] * itemSize[1] > slotGrid.Props?.CellsV * slotGrid.Props?.CellsH
+ || width * height > slotGrid.Props?.CellsV * slotGrid.Props?.CellsH
)
{
continue;
@@ -718,11 +717,7 @@ public class BotGeneratorHelper(
);
// Try to fit item into grid
- var findSlotResult = _containerHelper.FindSlotForItem(
- slotGridMap,
- itemSize[0],
- itemSize[1]
- );
+ var findSlotResult = slotGridMap.FindSlotForItem(width, height);
// Free slot found, add item
if (findSlotResult.Success ?? false)
@@ -768,7 +763,7 @@ public class BotGeneratorHelper(
}
// if the item was a one by one, we know it must be full. Or if the maps cant find a slot for a one by one
- if (itemSize[0] == 1 && itemSize[1] == 1)
+ if (width == 1 && height == 1)
{
containersIdFull.Add(equipmentSlotId.ToString());
}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ContainerHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ContainerHelper.cs
deleted file mode 100644
index 7e99615f..00000000
--- a/Libraries/SPTarkov.Server.Core/Helpers/ContainerHelper.cs
+++ /dev/null
@@ -1,211 +0,0 @@
-using System.Text.Json.Serialization;
-using SPTarkov.DI.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 positions filled/free
- /// Width of item
- /// Height of item
- /// Location to place item in container
- public FindSlotResult FindSlotForItem(int[][] container2D, int? itemX, int? itemY)
- {
- // Assume not rotated
- var rotation = false;
-
- var minVolume = (itemX < itemY ? itemX : itemY) - 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;
- }
-
- // Go left to right across x-axis looking for free position
- for (var x = 0; x < limitX; x++)
- {
- if (
- CanItemBePlacedInContainerAtPosition(
- container2D,
- containerX,
- containerY,
- x,
- y,
- itemX!.Value,
- itemY!.Value
- )
- )
- {
- // Success, return result
- return new FindSlotResult(true, x, y, rotation);
- }
-
- if (ItemBiggerThan1X1(itemX!.Value, itemY!.Value))
- {
- // Pointless rotating a 1x1, try next position across
- continue;
- }
-
- // Bigger than 1x1, try rotating by swapping x and y values
- if (
- !CanItemBePlacedInContainerAtPosition(
- container2D,
- containerX,
- containerY,
- x,
- y,
- itemY!.Value,
- itemX!.Value
- )
- )
- {
- continue;
- }
-
- // Found a position for item when rotated
- rotation = true;
-
- return new FindSlotResult(true, x, y, rotation);
- }
- }
-
- // Tried all possible positions, nothing big enough for item
- return new FindSlotResult(false);
- }
-
- protected static bool ItemBiggerThan1X1(int itemWidth, int itemHeight)
- {
- return itemWidth + itemHeight > 2;
- }
-
- ///
- /// Can an item of specified size be placed inside a 2d container at a specific position
- ///
- /// Container to find space in
- /// Container x size
- /// Container y size
- /// Starting x position for item
- /// Starting y position for item
- /// Items width
- /// Items height
- /// True - slot found
- protected bool CanItemBePlacedInContainerAtPosition(
- int[][] container,
- int containerWidth,
- int containerHeight,
- int startXPos,
- int startYPos,
- int itemWidth,
- int itemHeight
- )
- {
- // Check item isn't bigger than container when at position
- if (startXPos + itemWidth > containerWidth || startYPos + itemHeight > containerHeight)
- {
- return false;
- }
-
- // Check each position item will take up in container, go across and then down
- for (var itemY = startYPos; itemY < startYPos + itemHeight; itemY++)
- {
- for (var itemX = startXPos; itemX < startXPos + itemWidth; itemX++)
- {
- // e,g for a 2x2 item; [0,0] then [0,1] then [1,0] then [1,1]
- if (container[itemY][itemX] != 0)
- {
- // x,y Position blocked, can't place
- return false;
- }
- }
- }
-
- return true;
- }
-
- ///
- /// 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; }
-}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs
index c1fe39b2..21b1ea7d 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs
@@ -1,7 +1,5 @@
using System.Collections.Frozen;
-using System.Text.Json;
using System.Text.Json.Serialization;
-using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
@@ -25,10 +23,8 @@ namespace SPTarkov.Server.Core.Helpers;
[Injectable]
public class InventoryHelper(
ISptLogger _logger,
- HashUtil _hashUtil,
HttpResponseUtil _httpResponseUtil,
DialogueHelper _dialogueHelper,
- ContainerHelper _containerHelper,
EventOutputHolder _eventOutputHolder,
ProfileHelper _profileHelper,
ItemHelper _itemHelper,
@@ -146,10 +142,7 @@ public class InventoryHelper(
// Run callback
try
{
- if (request.Callback is not null)
- {
- request.Callback((int)(itemWithModsToAddClone[0].Upd.StackObjectsCount ?? 0));
- }
+ request.Callback?.Invoke((int)(itemWithModsToAddClone[0].Upd.StackObjectsCount ?? 0));
}
catch (Exception ex)
{
@@ -169,7 +162,7 @@ public class InventoryHelper(
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
- $"Added {itemWithModsToAddClone[0].Upd?.StackObjectsCount ?? 1} item: {itemWithModsToAddClone[0].Template} with: {itemWithModsToAddClone.Count - 1} mods to inventory"
+ $"Added: {itemWithModsToAddClone[0].Upd?.StackObjectsCount ?? 1} item: {itemWithModsToAddClone[0].Template} with: {itemWithModsToAddClone.Count - 1} mods to inventory"
);
}
}
@@ -245,7 +238,7 @@ public class InventoryHelper(
/// Container grid to fit items into
/// Items to try and fit into grid
/// True all fit
- public bool CanPlaceItemsInContainer(int[][] containerFS2D, List
> itemsWithChildren)
+ public bool CanPlaceItemsInContainer(int[,] containerFS2D, List> itemsWithChildren)
{
return itemsWithChildren.All(itemWithChildren =>
CanPlaceItemInContainer(containerFS2D, itemWithChildren)
@@ -258,28 +251,23 @@ public class InventoryHelper(
/// Container grid
/// Item to check fits
/// True it fits
- public bool CanPlaceItemInContainer(int[][] containerFS2D, List- itemWithChildren)
+ public bool CanPlaceItemInContainer(int[,] containerFS2D, List
- itemWithChildren)
{
// Get x/y size of item
var rootItem = itemWithChildren[0];
- var itemSize = GetItemSize(rootItem.Template, rootItem.Id, itemWithChildren);
+ var (sizeX, sizeY) = GetItemSize(rootItem.Template, rootItem.Id, itemWithChildren);
// Look for a place to slot item into
- var findSlotResult = _containerHelper.FindSlotForItem(
- containerFS2D,
- itemSize[0],
- itemSize[1]
- );
+ var findSlotResult = containerFS2D.FindSlotForItem(sizeX, sizeY);
if (findSlotResult.Success.GetValueOrDefault(false))
{
try
{
- _containerHelper.FillContainerMapWithItem(
- containerFS2D,
+ containerFS2D.FillContainerMapWithItem(
findSlotResult.X.Value,
findSlotResult.Y.Value,
- itemSize[0],
- itemSize[1],
+ sizeX,
+ sizeY,
findSlotResult.Rotation.Value
);
}
@@ -310,7 +298,7 @@ public class InventoryHelper(
/// Id of the container we're fitting item into
/// Slot id value to use, default is "hideout"
public void PlaceItemInContainer(
- int[][] containerFS2D,
+ int[,] containerFS2D,
List
- itemWithChildren,
string containerId,
string desiredSlotId = "hideout"
@@ -318,24 +306,23 @@ public class InventoryHelper(
{
// Get x/y size of item
var rootItemAdded = itemWithChildren[0];
- var itemSize = GetItemSize(rootItemAdded.Template, rootItemAdded.Id, itemWithChildren);
+ var (sizeX, sizeY) = GetItemSize(
+ rootItemAdded.Template,
+ rootItemAdded.Id,
+ itemWithChildren
+ );
// Look for a place to slot item into
- var findSlotResult = _containerHelper.FindSlotForItem(
- containerFS2D,
- itemSize[0],
- itemSize[1]
- );
+ var findSlotResult = containerFS2D.FindSlotForItem(sizeX, sizeY);
if (findSlotResult.Success.GetValueOrDefault(false))
{
try
{
- _containerHelper.FillContainerMapWithItem(
- containerFS2D,
+ containerFS2D.FillContainerMapWithItem(
findSlotResult.X.Value,
findSlotResult.Y.Value,
- itemSize[0],
- itemSize[1],
+ sizeX,
+ sizeY,
findSlotResult.Rotation.Value
);
}
@@ -378,8 +365,8 @@ public class InventoryHelper(
/// Should sorting table to be used if main stash has no space
/// Output to send back to client
protected void PlaceItemInInventory(
- int[][] stashFS2D,
- int[][] sortingTableFS2D,
+ int[,] stashFS2D,
+ int[,] sortingTableFS2D,
List
- itemWithChildren,
BotBaseInventory playerInventory,
bool useSortingTable,
@@ -388,20 +375,19 @@ public class InventoryHelper(
{
// Get x/y size of item
var rootItem = itemWithChildren[0];
- var itemSize = GetItemSize(rootItem.Template, rootItem.Id, itemWithChildren);
+ var (sizeX, sizeY) = GetItemSize(rootItem.Template, rootItem.Id, itemWithChildren);
// Look for a place to slot item into
- var findSlotResult = _containerHelper.FindSlotForItem(stashFS2D, itemSize[0], itemSize[1]);
+ var findSlotResult = stashFS2D.FindSlotForItem(sizeX, sizeY);
if (findSlotResult.Success.Value)
{
try
{
- _containerHelper.FillContainerMapWithItem(
- stashFS2D,
+ stashFS2D.FillContainerMapWithItem(
findSlotResult.X.Value,
findSlotResult.Y.Value,
- itemSize[0],
- itemSize[1],
+ sizeX,
+ sizeY,
findSlotResult.Rotation.Value
);
}
@@ -412,7 +398,7 @@ public class InventoryHelper(
return;
}
- // Store details for object, incuding container item will be placed in
+ // Store details for object, including container item will be placed in
rootItem.ParentId = playerInventory.Stash;
rootItem.SlotId = "hideout";
rootItem.Location = new ItemLocation
@@ -430,20 +416,15 @@ public class InventoryHelper(
// Space not found in main stash, use sorting table
if (useSortingTable)
{
- var findSortingSlotResult = _containerHelper.FindSlotForItem(
- sortingTableFS2D,
- itemSize[0],
- itemSize[1]
- );
+ var findSortingSlotResult = sortingTableFS2D.FindSlotForItem(sizeX, sizeY);
try
{
- _containerHelper.FillContainerMapWithItem(
- sortingTableFS2D,
+ sortingTableFS2D.FillContainerMapWithItem(
findSortingSlotResult.X.Value,
findSortingSlotResult.Y.Value,
- itemSize[0],
- itemSize[1],
+ sizeX,
+ sizeY,
findSortingSlotResult.Rotation.Value
);
}
@@ -687,7 +668,7 @@ public class InventoryHelper(
/// Items id to get size of
///
/// [width, height]
- public List GetItemSize(string? itemTpl, string itemId, List
- inventoryItems)
+ public (int, int) GetItemSize(string? itemTpl, string itemId, List
- inventoryItems)
{
// -> Prepares item Width and height returns [sizeX, sizeY]
return GetSizeByInventoryItemHash(itemTpl, itemId, GetInventoryItemHash(inventoryItems));
@@ -701,9 +682,9 @@ public class InventoryHelper(
/// Items id
/// Hashmap of inventory items
/// An array representing the [width, height] of the item
- protected List GetSizeByInventoryItemHash(
- string itemTpl,
- string itemId,
+ protected (int, int) GetSizeByInventoryItemHash(
+ MongoId itemTpl,
+ MongoId itemId,
InventoryItemHash inventoryItemHash
)
{
@@ -736,7 +717,7 @@ public class InventoryHelper(
_serverLocalisationService.GetText("inventory-return_default_size", itemTpl)
);
- return [1, 1]; // Invalid input data, return defaults
+ return (1, 1); // Invalid input data, return defaults
}
if (!inventoryItemHash.ByItemId.TryGetValue(itemId, out var rootItem))
@@ -745,7 +726,7 @@ public class InventoryHelper(
$"Unable to get root item with Id: {itemId} from player inventory. Defaulting to 1x1"
);
- return [1, 1]; // Invalid input data, return defaults
+ return (1, 1); // Invalid input data, return defaults
}
// Does root item support being folded
@@ -867,11 +848,10 @@ public class InventoryHelper(
}
}
- return
- [
+ return (
outX.Value + sizeLeft + sizeRight + forcedLeft + forcedRight,
- outY.Value + sizeUp + sizeDown + forcedUp + forcedDown,
- ];
+ outY.Value + sizeUp + sizeDown + forcedUp + forcedDown
+ );
}
///
@@ -882,7 +862,7 @@ public class InventoryHelper(
/// Players inventory items
/// Id of the container
/// Two-dimensional representation of container
- public int[][] GetContainerMap(int sizeX, int sizeY, List
- itemList, string containerId)
+ public int[,] GetContainerMap(int sizeX, int sizeY, List
- itemList, string containerId)
{
// Create blank 2d map of container
var containerYX = _itemHelper.GetBlankContainerMap(sizeY, sizeX);
@@ -892,66 +872,63 @@ public class InventoryHelper(
// Get subset of items that belong to the desired container
if (!inventoryItemHash.ByParentId.TryGetValue(containerId, out var rootItemsInContainer))
- // No items in container, exit early
{
+ // No items in container, exit early and return the blank container map
return containerYX;
}
- // Check each item in container
- foreach (var item in rootItemsInContainer)
+ // Add every root items size (with mods attached) found in container
+ foreach (var rootItem in rootItemsInContainer)
{
- ItemLocation? itemLocation;
- if (item.Location is JsonElement element)
- {
- // TODO: is this ever true?
- itemLocation = element.ToObject();
- }
- else
- {
- itemLocation = (ItemLocation?)item.Location;
- }
-
+ var itemLocation = rootItem.GetParsedLocation();
if (itemLocation is null)
{
// Item has no location property
_logger.Error(
- $"Unable to find 'location' property on item with id: {item.Id}, skipping"
+ $"Unable to find 'location' property on item with id: {rootItem.Id}, skipping"
);
continue;
}
// Get x/y size of item
- var tmpSize = GetSizeByInventoryItemHash(item.Template, item.Id, inventoryItemHash);
- var iW = tmpSize[0]; // x
- var iH = tmpSize[1]; // y
- var fH = itemLocation.IsVertical() ? iW : iH;
- var fW = itemLocation.IsVertical() ? iH : iW;
+ var (xSize, ySize) = GetSizeByInventoryItemHash(
+ rootItem.Template,
+ rootItem.Id,
+ inventoryItemHash
+ );
+ var itemHSize = itemLocation.IsVertical() ? xSize : ySize;
+ var itemWSize = itemLocation.IsVertical() ? ySize : xSize;
- for (var y = 0; y < fH; y++)
+ for (var yOffset = 0; yOffset < itemHSize; yOffset++)
{
- try
+ for (var xOffset = 0; xOffset < itemWSize; xOffset++)
{
- var rowIndex = itemLocation.Y + y;
- var containerX = containerYX.ElementAtOrDefault(rowIndex.Value);
- if (containerX is null)
- {
- _logger.Error(
- $"Unable to find container: {containerId} row line: {itemLocation.Y + y}"
- );
- }
+ var currentY = itemLocation.Y.Value + yOffset;
+ var currentX = itemLocation.X.Value + xOffset;
- // Fill the corresponding cells in the container map to show the slot is taken
- Array.Fill(containerX, 1, itemLocation.X.Value, fW);
- }
- catch (Exception ex)
- {
- _logger.Error(
- _serverLocalisationService.GetText(
- "inventory-unable_to_fill_container",
- new { id = item.Id, error = $"{ex.Message} {ex.StackTrace}" }
- )
- );
+ // Check still in containers bounds
+ if (currentY >= 0 && currentY < sizeY && currentX >= 0 && currentX < sizeX)
+ {
+ // mark slot used
+ containerYX[currentY, currentX] = 1;
+ }
+ else
+ {
+ // Out of bounds
+ var message =
+ $"Item: {rootItem.Id} at: {itemLocation.X}, {itemLocation.Y} size: {itemHSize}x{itemWSize} extends outside the containers bounds";
+
+ _logger.Error(
+ _serverLocalisationService.GetText(
+ "inventory-unable_to_fill_container",
+ new { id = rootItem.Id, error = $"{message}" }
+ )
+ );
+
+ // Stop and try next row
+ break;
+ }
}
}
}
@@ -975,6 +952,11 @@ public class InventoryHelper(
continue;
}
+ if (item.ParentId == "hideout")
+ {
+ continue;
+ }
+
if (!inventoryItemHash.ByParentId.ContainsKey(item.ParentId))
{
inventoryItemHash.ByParentId[item.ParentId] = [];
@@ -1057,7 +1039,7 @@ public class InventoryHelper(
///
/// Player profile
/// 2-dimensional array
- protected int[][] GetStashSlotMap(PmcData pmcData)
+ protected int[,] GetStashSlotMap(PmcData pmcData)
{
var (horizontal, vertical) = GetPlayerStashSize(pmcData);
return GetContainerMap(
@@ -1073,7 +1055,7 @@ public class InventoryHelper(
///
/// Container to get data for
/// blank two-dimensional array
- public int[][] GetContainerSlotMap(string containerTpl)
+ public int[,] GetContainerSlotMap(string containerTpl)
{
var containerTemplate = _itemHelper.GetItem(containerTpl).Value;
@@ -1089,7 +1071,7 @@ public class InventoryHelper(
///
/// Player profile
/// two-dimensional array
- protected int[][] GetSortingTableSlotMap(PmcData pmcData)
+ protected int[,] GetSortingTableSlotMap(PmcData pmcData)
{
return GetContainerMap(10, 45, pmcData.Inventory.Items, pmcData.Inventory.SortingTable);
}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs
index 07e72355..cc8818f1 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs
@@ -2057,7 +2057,7 @@ public class ItemHelper(
/// Get a 2D grid of a container's item slots
///
/// Tpl id of the container
- public int[][] GetContainerMapping(string containerTpl)
+ public int[,] GetContainerMapping(string containerTpl)
{
// Get template from db
var containerTemplate = GetItem(containerTpl).Value;
@@ -2075,16 +2075,8 @@ public class ItemHelper(
/// Horizontal size of container
/// Vertical size of container
/// Two-dimensional representation of container
- public int[][] GetBlankContainerMap(int containerY, int containerX)
+ public int[,] GetBlankContainerMap(int containerY, int containerX)
{
- //var x = new int[containerY][];
- //for (int i = 0; i < containerY; i++)
- //{
- // x[i] = new int[containerH];
- //}
-
- //return x;
-
- return Enumerable.Range(0, containerY).Select(_ => new int[containerX]).ToArray();
+ return new int[containerX, containerY];
}
}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/SecureContainerHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/SecureContainerHelper.cs
deleted file mode 100644
index 661a8d0d..00000000
--- a/Libraries/SPTarkov.Server.Core/Helpers/SecureContainerHelper.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using SPTarkov.DI.Annotations;
-using SPTarkov.Server.Core.Extensions;
-using SPTarkov.Server.Core.Models.Eft.Common.Tables;
-
-namespace SPTarkov.Server.Core.Helpers;
-
-[Injectable]
-public class SecureContainerHelper(ItemHelper _itemHelper)
-{
- ///
- /// Get a list of the item IDs (NOT tpls) inside a secure container
- ///
- /// Inventory items to look for secure container in
- /// List of ids
- public List GetSecureContainerItems(List- items)
- {
- var secureContainer = items.First(x => x.SlotId == "SecuredContainer");
-
- // No container found, drop out
- if (secureContainer is null)
- {
- return [];
- }
-
- var itemsInSecureContainer = items.FindAndReturnChildrenByItems(secureContainer.Id);
-
- // Return all items returned and exclude the secure container item itself
- return itemsInSecureContainer.Where(x => x != secureContainer.Id).ToList();
- }
-}
diff --git a/Libraries/SPTarkov.Server.Core/Models/Common/MongoId.cs b/Libraries/SPTarkov.Server.Core/Models/Common/MongoId.cs
index d8447c63..6f7af548 100644
--- a/Libraries/SPTarkov.Server.Core/Models/Common/MongoId.cs
+++ b/Libraries/SPTarkov.Server.Core/Models/Common/MongoId.cs
@@ -23,11 +23,6 @@ public readonly struct MongoId : IEquatable
return;
}
- if (id == "hideout")
- {
- throw new Exception("wtf");
- }
-
if (id.Length != 24)
{
// TODO: Items.json root item has an empty parentId property
diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Inventory/FindSlotResult.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Inventory/FindSlotResult.cs
new file mode 100644
index 00000000..8aa02646
--- /dev/null
+++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Inventory/FindSlotResult.cs
@@ -0,0 +1,33 @@
+using System.Text.Json.Serialization;
+
+namespace SPTarkov.Server.Core.Models.Spt.Inventory;
+
+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; }
+}
diff --git a/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs b/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs
index e023886c..37996336 100644
--- a/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs
+++ b/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations;
+using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Generators;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
@@ -9,7 +10,6 @@ using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Spt.Services;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
-using SPTarkov.Server.Core.Utils;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Services;
@@ -19,9 +19,7 @@ public class AirdropService(
ConfigServer configServer,
ISptLogger _logger,
LootGenerator _lootGenerator,
- HashUtil _hashUtil,
WeightedRandomHelper _weightedRandomHelper,
- ContainerHelper _containerHelper,
ServerLocalisationService _serverLocalisationService,
ItemFilterService _itemFilterService,
ItemHelper _itemHelper
@@ -135,19 +133,14 @@ public class AirdropService(
var itemSize = _itemHelper.GetItemSize(itemAndChildren, itemAndChildren[0].Id);
// Look for open slot to put chosen item into
- var result = _containerHelper.FindSlotForItem(
- containerMap,
- itemSize.Width,
- itemSize.Height
- );
+ var result = containerMap.FindSlotForItem(itemSize.Width, itemSize.Height);
if (result.Success.GetValueOrDefault(false))
{
// It Fits, add item + children
lootResult.AddRange(itemAndChildren);
// Update container with item we just added
- _containerHelper.FillContainerMapWithItem(
- containerMap,
+ containerMap.FillContainerMapWithItem(
result.X.Value,
result.Y.Value,
itemSize.Width,
diff --git a/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs b/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs
index e0f01859..9f67d6a0 100644
--- a/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs
+++ b/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs
@@ -137,7 +137,7 @@ public class CircleOfCultistService(
_hideoutConfig.CultistCircle
),
rewardAmountRoubles,
- cultistCircleStashId,
+ cultistCircleStashId.Value,
_hideoutConfig.CultistCircle
);
@@ -153,7 +153,7 @@ public class CircleOfCultistService(
pmcData,
rewards,
containerGrid,
- cultistCircleStashId,
+ cultistCircleStashId.Value,
output
);
@@ -352,7 +352,7 @@ public class CircleOfCultistService(
protected List
> GetRewardsWithinBudget(
List rewardItemTplPool,
double rewardBudget,
- string cultistCircleStashId,
+ MongoId cultistCircleStashId,
CultistCircleSettings circleConfig
)
{
@@ -1014,8 +1014,8 @@ public class CircleOfCultistService(
string sessionId,
PmcData pmcData,
List> rewards,
- int[][] containerGrid,
- string cultistCircleStashId,
+ int[,] containerGrid,
+ MongoId cultistCircleStashId,
ItemEventRouterResponse output
)
{