This commit is contained in:
CWX
2025-01-20 17:40:34 +00:00
7 changed files with 301 additions and 30 deletions
@@ -61,7 +61,7 @@ public class BotEquipmentModGenerator(
}
// Iterate over mod pool and choose mods to add to item
foreach (var (modSlotName, modPool) in compatibleModsPool)
foreach (var (modSlotName, modPool) in compatibleModsPool ?? [])
{
// Get the templates slot object from db
var itemSlotTemplate = GetModItemSlotFromDb(modSlotName, parentTemplate);
@@ -393,7 +393,7 @@ public class BotInventoryGenerator(
// Roll dice on equipment item
var shouldSpawn = _randomUtil.GetChance100(spawnChance ?? 0);
if (shouldSpawn && !settings.RootEquipmentPool.Any())
if (shouldSpawn && settings.RootEquipmentPool.Any())
{
var pickedItemDb = new TemplateItem();
var found = false;
@@ -469,7 +469,8 @@ public class BotInventoryGenerator(
);
// Edge case: Filter the armor items mod pool if bot exists in config dict + config has armor slot
if (_botConfig.Equipment[settings.BotData.EquipmentRole] is not null &&
if (_botConfig.Equipment.ContainsKey(settings.BotData.EquipmentRole) &&
settings.RandomisationDetails is not null &&
settings.RandomisationDetails.RandomisedArmorSlots.Contains(settings.RootEquipmentSlot.ToString()))
{
// Filter out mods from relevant blacklist
@@ -480,7 +481,8 @@ public class BotInventoryGenerator(
}
// Does item have slots for sub-mods to be inserted into
if (pickedItemDb.Properties.Slots.Any() && settings.GenerateModsBlacklist.Contains(pickedItemDb.Id))
if (pickedItemDb.Properties?.Slots?.Count > 0
&& settings.GenerateModsBlacklist.Contains(pickedItemDb.Id))
{
var childItemsToAdd = _botEquipmentModGenerator.GenerateModsForEquipment(
[item],
+2 -2
View File
@@ -474,13 +474,13 @@ public class BotGeneratorHelper(
}
// Get container to put item into
var container = (inventory.Items ?? []).FirstOrDefault(item => item.SlotId == equipmentSlotId.ToString());
var container = (inventory.Items).FirstOrDefault(item => item.SlotId == equipmentSlotId.ToString());
if (container is null)
{
missingContainerCount++;
if (missingContainerCount == equipmentSlots.Count)
{
// Bot doesnt have any containers we want to add item to
// Bot doesn't have any containers we want to add item to
_logger.Debug(
$"Unable to add item: {itemWithChildren.FirstOrDefault()?.Template} to bot as it lacks the following containers: {string.Join(",", equipmentSlots)}"
);
+74 -7
View File
@@ -1,4 +1,4 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using SptCommon.Annotations;
namespace Core.Helpers;
@@ -13,9 +13,59 @@ public class ContainerHelper
/// <param name="itemWidth">Width of item</param>
/// <param name="itemHeight">Height of item</param>
/// <returns>Location to place item in container</returns>
public FindSlotResult FindSlotForItem(List<List<int>> container2D, int itemWidth, int itemHeight)
public FindSlotResult FindSlotForItem(int[][] container2D, int itemWidth, int itemHeight)
{
throw new NotImplementedException();
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
for (var y = 0; y < limitY; y++)
{
// Across
if (container2D[y].All((x) => x == 1))
{
// Every item in row is full, skip row
continue;
}
for (var x = 0; x < limitX; x++)
{
var foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemWidth, itemHeight);
// Failed to find slot, rotate item and try again
if (!foundSlot && itemWidth * itemHeight > 1)
{
// Bigger than 1x1
foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemHeight, itemWidth); // Height/Width swapped
if (foundSlot)
{
// Found a slot for it when rotated
rotation = true;
}
}
if (!foundSlot)
{
// Didn't fit this hole, try again
continue;
}
return new FindSlotResult(true, x, y, rotation);
}
}
// Tried all possible holes, nothing big enough for the item
return new FindSlotResult(false);
}
/// <summary>
@@ -30,7 +80,7 @@ public class ContainerHelper
/// <param name="itemH">Items height</param>
/// <returns>True - slot found</returns>
protected bool LocateSlot(
List<List<int>> container2D,
int[][] container2D,
int containerX,
int containerY,
int x,
@@ -51,7 +101,7 @@ public class ContainerHelper
/// <param name="itemH">Items height</param>
/// <param name="rotate">is item rotated</param>
public void FillContainerMapWithItem(
List<List<int>> container2D,
int[][] container2D,
int x,
int y,
int itemW,
@@ -64,14 +114,31 @@ public class ContainerHelper
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 double? X { get; set; }
public int? X { get; set; }
[JsonPropertyName("y")]
public double? Y { get; set; }
public int? Y { get; set; }
[JsonPropertyName("rotation")]
public bool? Rotation { get; set; }
+210 -8
View File
@@ -8,6 +8,12 @@ using Core.Models.Spt.Config;
using Core.Models.Spt.Inventory;
using Core.Models.Utils;
using Core.Services;
using Core.Models.Eft.Player;
using System.ComponentModel;
using Core.Models.Eft.Hideout;
using Core.Models.Enums;
using Core.Models.Spt.Bots;
using Core.Models.Spt.Services;
namespace Core.Helpers;
@@ -16,6 +22,7 @@ public class InventoryHelper(
ISptLogger<InventoryHelper> _logger,
ProfileHelper _profileHelper,
DialogueHelper _dialogueHelper,
ItemHelper _itemHelper,
LocalisationService _localisationService
)
{
@@ -187,7 +194,8 @@ public class InventoryHelper(
/// <returns>[width, height]</returns>
public List<int> GetItemSize(string? itemTpl, string itemId, List<Item> inventoryItems)
{
throw new NotImplementedException();
// -> Prepares item Width and height returns [sizeX, sizeY]
return GetSizeByInventoryItemHash(itemTpl, itemId, GetInventoryItemHash(inventoryItems));
}
/// <summary>
@@ -198,9 +206,126 @@ public class InventoryHelper(
/// <param name="itemId">Items id</param>
/// <param name="inventoryItemHash">Hashmap of inventory items</param>
/// <returns>An array representing the [width, height] of the item</returns>
protected List<int> GetSizeByInventoryItemHash(string itemTpl, string itemId, InventoryItemHash inventoryItemHash)
protected List<int> GetSizeByInventoryItemHash(string itemTpl, string itemID, InventoryItemHash inventoryItemHash)
{
throw new NotImplementedException();
var toDo = new List<string> { itemID };
var result = _itemHelper.GetItem(itemTpl);
var tmpItem = result.Value;
// Invalid item
if (!result.Key)
{
_logger.Error(_localisationService.GetText("inventory-invalid_item_missing_from_db", itemTpl));
}
// Item found but no _props property
if (tmpItem is not null && tmpItem.Properties is null)
{
_localisationService.GetText("inventory-item_missing_props_property", new {
itemTpl = itemTpl,
itemName = tmpItem?.Name
});
}
// No item object or getItem() returned false
if (tmpItem is null && result.Value is null)
{
// return default size of 1x1
_logger.Error(_localisationService.GetText("inventory-return_default_size", itemTpl));
return [1, 1]; // Invalid input data, return defaults
}
var rootItem = inventoryItemHash.ByItemId[itemID];
var foldableWeapon = tmpItem.Properties.Foldable;
var foldedSlot = tmpItem.Properties.FoldedSlot;
var sizeUp = 0;
var sizeDown = 0;
var sizeLeft = 0;
var sizeRight = 0;
var forcedUp = 0;
var forcedDown = 0;
var forcedLeft = 0;
var forcedRight = 0;
var outX = (int)tmpItem.Properties.Width;
var outY = (int)tmpItem.Properties.Height;
// Item types to ignore
var skipThisItems = new List<string> { BaseClasses.BACKPACK, BaseClasses.SEARCHABLE_ITEM, BaseClasses.SIMPLE_CONTAINER };
var rootFolded = rootItem?.Upd?.Foldable?.Folded == true;
// The item itself is collapsible
if (foldableWeapon is not null && string.IsNullOrEmpty(foldedSlot) && rootFolded)
{
outX -= tmpItem.Properties.SizeReduceRight.Value;
}
// Calculate size contribution from child items/attachments
if (!skipThisItems.Contains(tmpItem.Parent))
{
while (toDo.Count > 0)
{
if (inventoryItemHash.ByParentId.ContainsKey(toDo[0])) {
foreach (var item in inventoryItemHash.ByParentId[toDo[0]]) {
// Filtering child items outside of mod slots, such as those inside containers, without counting their ExtraSize attribute
if (item.SlotId.IndexOf("mod_") < 0)
{
continue;
}
toDo.Add(item.Id);
// If the barrel is folded the space in the barrel is not counted
var itemResult = _itemHelper.GetItem(item.Template);
if (!itemResult.Key)
{
_logger.Error(
_localisationService.GetText("inventory-get_item_size_item_not_found_by_tpl", item.Template));
}
var itm = itemResult.Value;
var childFoldable = itm.Properties.Foldable.GetValueOrDefault(false);
var childFolded = item.Upd?.Foldable is not null && item.Upd.Foldable.Folded == true;
if (foldableWeapon is true && foldedSlot == item.SlotId && (rootFolded || childFolded))
{
continue;
}
if (childFoldable && rootFolded && childFolded)
{
continue;
}
// Calculating child ExtraSize
if (itm.Properties.ExtraSizeForceAdd == true)
{
forcedUp += itm.Properties.ExtraSizeUp.Value;
forcedDown += itm.Properties.ExtraSizeDown.Value;
forcedLeft += itm.Properties.ExtraSizeLeft.Value;
forcedRight += itm.Properties.ExtraSizeRight.Value;
}
else
{
sizeUp = sizeUp < itm.Properties.ExtraSizeUp ? itm.Properties.ExtraSizeUp.Value : sizeUp;
sizeDown = sizeDown < itm.Properties.ExtraSizeDown ? itm.Properties.ExtraSizeDown.Value : sizeDown;
sizeLeft = sizeLeft < itm.Properties.ExtraSizeLeft ? itm.Properties.ExtraSizeLeft.Value : sizeLeft;
sizeRight = sizeRight < itm.Properties.ExtraSizeRight ? itm.Properties.ExtraSizeRight.Value : sizeRight;
}
}
}
toDo.RemoveAt(0);
}
}
return [
outX + sizeLeft + sizeRight + forcedLeft + forcedRight,
outY + sizeUp + sizeDown + forcedUp + forcedDown,
];
}
/// <summary>
@@ -209,9 +334,9 @@ public class InventoryHelper(
/// <param name="containerH">Horizontal size of container</param>
/// <param name="containerY">Vertical size of container</param>
/// <returns>Two-dimensional representation of container</returns>
protected List<List<int>> GetBlankContainerMap(int containerH, int containerY)
protected int[][] GetBlankContainerMap(int containerH, int containerY)
{
throw new NotImplementedException();
return Enumerable.Repeat(Enumerable.Repeat(0, containerH).ToArray(), containerY).ToArray();
}
/// <summary>
@@ -222,9 +347,68 @@ public class InventoryHelper(
/// <param name="itemList">Players inventory items</param>
/// <param name="containerId">Id of the container</param>
/// <returns>Two-dimensional representation of container</returns>
public List<List<int>> GetContainerMap(double containerH, double containerV, List<Item> itemList, string containerId)
public int[][] GetContainerMap(int containerH, int containerV, List<Item> itemList, string containerId)
{
throw new NotImplementedException();
// Create blank 2d map of container
var container2D = GetBlankContainerMap(containerH, containerV);
// Get all items in players inventory keyed by their parentId and by ItemId
var inventoryItemHash = GetInventoryItemHash(itemList);
// Get subset of items that belong to the desired container
if (!inventoryItemHash.ByParentId.TryGetValue(containerId, out List<Item> containerItemHash))
{
// No items in container, exit early
return container2D;
}
// Check each item in container
foreach (var item in containerItemHash) {
var itemLocation = item?.Location as ItemLocation;
if (itemLocation is null)
{
// item has no location property
_logger.Error("Unable to find 'location' property on item with id: ${ item._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 = IsVertical(itemLocation) ? iW : iH;
var fW = IsVertical(itemLocation) ? iH : iW;
// Find the ending x coord of container
var fillTo = itemLocation.X + fW;
for (var y = 0; y < fH; y++)
{
try
{
var rowIndex = itemLocation.Y + y;
var containerRow = container2D[rowIndex.Value];
if (containerRow is null)
{
_logger.Error("Unable to find container: { containerId} row line: { itemLocation.y + y}");
}
// Fill the corresponding cells in the container map to show the slot is taken
Array.Fill(containerRow, 1, itemLocation.X.Value, fillTo.Value);
} catch (Exception ex) {
_logger.Error(
_localisationService.GetText("inventory-unable_to_fill_container", new {
id = item.Id,
error = ex.Message
})
);
}
}
}
return container2D;
}
protected bool IsVertical(ItemLocation itemLocation)
@@ -234,7 +418,25 @@ public class InventoryHelper(
protected InventoryItemHash GetInventoryItemHash(List<Item> inventoryItems)
{
throw new NotImplementedException();
var inventoryItemHash = new InventoryItemHash
{
ByItemId = new(),
ByParentId = new()
};
foreach (var item in inventoryItems)
{
inventoryItemHash.ByItemId.TryAdd(item.Id, item);
if (item.ParentId is null) {
continue;
}
if (!inventoryItemHash.ByParentId.ContainsKey(item.ParentId)) {
inventoryItemHash.ByParentId[item.ParentId] = [];
}
inventoryItemHash.ByParentId[item.ParentId].Add(item);
}
return inventoryItemHash;
}
/// <summary>
@@ -29,8 +29,8 @@ public record Item
public record ItemLocation
{
public double? X { get; set; }
public double? Y { get; set; }
public int? X { get; set; }
public int? Y { get; set; }
public object? R { get; set; } // TODO: Can be string or number
public bool? IsSearched { get; set; }
@@ -153,19 +153,19 @@ public record Props
public double? RepairSpeed { get; set; }
[JsonPropertyName("ExtraSizeLeft")]
public double? ExtraSizeLeft { get; set; }
public int? ExtraSizeLeft { get; set; }
[JsonPropertyName("ExtraSizeRight")]
public double? ExtraSizeRight { get; set; }
public int? ExtraSizeRight { get; set; }
[JsonPropertyName("ExtraSizeUp")]
public double? ExtraSizeUp { get; set; }
public int? ExtraSizeUp { get; set; }
[JsonPropertyName("FlareTypes")]
public List<string>? FlareTypes { get; set; }
[JsonPropertyName("ExtraSizeDown")]
public double? ExtraSizeDown { get; set; }
public int? ExtraSizeDown { get; set; }
[JsonPropertyName("ExtraSizeForceAdd")]
public bool? ExtraSizeForceAdd { get; set; }
@@ -474,7 +474,7 @@ public record Props
public bool? Retractable { get; set; }
[JsonPropertyName("SizeReduceRight")]
public double? SizeReduceRight { get; set; }
public int? SizeReduceRight { get; set; }
[JsonPropertyName("CenterOfImpact")]
public double? CenterOfImpact { get; set; }
@@ -1512,10 +1512,10 @@ public record GridProps
public List<GridFilter>? Filters { get; set; }
[JsonPropertyName("cellsH")]
public double? CellsH { get; set; }
public int? CellsH { get; set; }
[JsonPropertyName("cellsV")]
public double? CellsV { get; set; }
public int? CellsV { get; set; }
[JsonPropertyName("minCount")]
public double? MinCount { get; set; }