Refactor of how bot loot has its position picked to improve performance (#548)
* Initial work on moving container space checks into new class * Updated tests Wired up service to save item into inventory when space is found Updated `FillContainerMapWithItem` to return outcome and not throw exception on failure Add containers to bot when generating bot equipment Clean bot cache after completion of loot generation Removed redundant code from `AddItemWithChildrenToEquipmentSlot` Removed unnecessary Singleton status from `BotInventoryContainerService` * Clean-up of service * Add botId xml docs * Updated documentation for `FillContainerMapWithItem` * Code review fixes and improvements * Remove TODO --------- Co-authored-by: Chomp <dev@dev.sp-tarkov.com>
This commit is contained in:
@@ -24,7 +24,7 @@ public static class ContainerExtensions
|
||||
var limitX = containerX - minVolume;
|
||||
|
||||
// Every x+y slot taken up in container, exit
|
||||
if (ContainerIsFull(container2D))
|
||||
if (container2D.ContainerIsFull())
|
||||
{
|
||||
return new FindSlotResult(false);
|
||||
}
|
||||
@@ -84,7 +84,8 @@ public static class ContainerExtensions
|
||||
/// <param name="itemXWidth">Items width</param>
|
||||
/// <param name="itemYHeight">Items height</param>
|
||||
/// <param name="isRotated">is item rotated</param>
|
||||
public static void FillContainerMapWithItem(
|
||||
/// <returns>bool = true when successful, string = error message if failed</returns>
|
||||
public static (bool, string) FillContainerMapWithItem(
|
||||
this int[,] container2D,
|
||||
int columnStartPositionX,
|
||||
int rowStartPositionY,
|
||||
@@ -108,7 +109,7 @@ public static class ContainerExtensions
|
||||
{
|
||||
container2D[rowStartPositionY, columnStartPositionX] = 1;
|
||||
|
||||
return;
|
||||
return (true, string.Empty);
|
||||
}
|
||||
|
||||
// Loop over rows and columns and flag each as taken by item
|
||||
@@ -123,12 +124,15 @@ public static class ContainerExtensions
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(
|
||||
return (
|
||||
false,
|
||||
$"Slot at: ({containerX}, {containerY}) is already filled. Cannot fit: {itemXWidth} by {itemYHeight} item"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -158,7 +162,7 @@ public static class ContainerExtensions
|
||||
/// </summary>
|
||||
/// <param name="container2D">Container to check</param>
|
||||
/// <returns>True = full</returns>
|
||||
private static bool ContainerIsFull(int[,] container2D)
|
||||
public static bool ContainerIsFull(this int[,] container2D)
|
||||
{
|
||||
var containerY = container2D.GetLength(0); // rows
|
||||
var containerX = container2D.GetLength(1); // columns
|
||||
|
||||
@@ -173,6 +173,9 @@ public class BotGenerator(
|
||||
var botRoleLowercase = botGenerationDetails.Role.ToLowerInvariant();
|
||||
var botLevel = botLevelGenerator.GenerateBotLevel(botJsonTemplate.BotExperience.Level, botGenerationDetails, bot);
|
||||
|
||||
// Generate new bot ID
|
||||
AddIdsToBot(bot, botGenerationDetails);
|
||||
|
||||
// Only filter bot equipment, never players
|
||||
if (!botGenerationDetails.IsPlayerScav)
|
||||
{
|
||||
@@ -254,6 +257,7 @@ public class BotGenerator(
|
||||
SetBotAppearance(bot, botJsonTemplate.BotAppearance, botGenerationDetails);
|
||||
|
||||
bot.Inventory = botInventoryGenerator.GenerateInventory(
|
||||
bot.Id.Value,
|
||||
sessionId,
|
||||
botJsonTemplate,
|
||||
botRoleLowercase,
|
||||
@@ -267,9 +271,6 @@ public class BotGenerator(
|
||||
AddDogtagToBot(bot);
|
||||
}
|
||||
|
||||
// Generate new bot ID
|
||||
AddIdsToBot(bot, botGenerationDetails);
|
||||
|
||||
// Generate new inventory ID
|
||||
GenerateInventoryId(bot);
|
||||
|
||||
|
||||
@@ -32,9 +32,19 @@ public class BotInventoryGenerator(
|
||||
BotEquipmentFilterService botEquipmentFilterService,
|
||||
BotEquipmentModPoolService botEquipmentModPoolService,
|
||||
BotEquipmentModGenerator botEquipmentModGenerator,
|
||||
BotInventoryContainerService botInventoryContainerService,
|
||||
ConfigServer configServer
|
||||
)
|
||||
{
|
||||
// Slots handled individually inside `GenerateAndAddEquipmentToBot`
|
||||
private static readonly FrozenSet<EquipmentSlots> _equipmentSlotsWithInventory =
|
||||
[
|
||||
EquipmentSlots.Pockets,
|
||||
EquipmentSlots.TacticalVest,
|
||||
EquipmentSlots.Backpack,
|
||||
EquipmentSlots.SecuredContainer,
|
||||
];
|
||||
|
||||
// Slots handled individually inside `GenerateAndAddEquipmentToBot`
|
||||
private static readonly FrozenSet<EquipmentSlots> _excludedEquipmentSlots =
|
||||
[
|
||||
@@ -56,6 +66,7 @@ public class BotInventoryGenerator(
|
||||
/// <summary>
|
||||
/// Add equipment/weapons/loot to bot
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="botJsonTemplate">Base json db file for the bot having its loot generated</param>
|
||||
/// <param name="botRole">Role bot has (assault/pmcBot)</param>
|
||||
@@ -64,6 +75,7 @@ public class BotInventoryGenerator(
|
||||
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
|
||||
/// <returns>PmcInventory object with equipment/weapons/loot</returns>
|
||||
public BotBaseInventory GenerateInventory(
|
||||
MongoId botId,
|
||||
MongoId sessionId,
|
||||
BotType botJsonTemplate,
|
||||
string botRole,
|
||||
@@ -85,6 +97,7 @@ public class BotInventoryGenerator(
|
||||
var raidConfig = profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
|
||||
|
||||
GenerateAndAddEquipmentToBot(
|
||||
botId,
|
||||
sessionId,
|
||||
templateInventory,
|
||||
wornItemChances,
|
||||
@@ -98,6 +111,7 @@ public class BotInventoryGenerator(
|
||||
|
||||
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
|
||||
GenerateAndAddWeaponsToBot(
|
||||
botId,
|
||||
templateInventory,
|
||||
wornItemChances,
|
||||
sessionId,
|
||||
@@ -109,7 +123,10 @@ public class BotInventoryGenerator(
|
||||
);
|
||||
|
||||
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
|
||||
botLootGenerator.GenerateLoot(sessionId, botJsonTemplate, botGenerationDetails, isPmc, botRole, botInventory, botLevel);
|
||||
botLootGenerator.GenerateLoot(botId, sessionId, botJsonTemplate, botGenerationDetails, isPmc, botRole, botInventory, botLevel);
|
||||
|
||||
// Inventory cache isn't needed, clear to save memory
|
||||
botInventoryContainerService.ClearCache(botId);
|
||||
|
||||
return botInventory;
|
||||
}
|
||||
@@ -153,6 +170,7 @@ public class BotInventoryGenerator(
|
||||
/// <summary>
|
||||
/// Add equipment to a bot
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="templateInventory">bot/x.json data from db</param>
|
||||
/// <param name="wornItemChances">Chances items will be added to bot</param>
|
||||
@@ -163,6 +181,7 @@ public class BotInventoryGenerator(
|
||||
/// <param name="isPmc">Is the generated bot a PMC</param>
|
||||
/// <param name="raidConfig">RadiConfig</param>
|
||||
public void GenerateAndAddEquipmentToBot(
|
||||
MongoId botId,
|
||||
MongoId sessionId,
|
||||
BotTypeInventory templateInventory,
|
||||
Chances wornItemChances,
|
||||
@@ -211,6 +230,7 @@ public class BotInventoryGenerator(
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = equipmentSlot,
|
||||
RootEquipmentPool = value,
|
||||
ModPool = templateInventory.Mods,
|
||||
@@ -233,6 +253,7 @@ public class BotInventoryGenerator(
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = EquipmentSlots.Pockets,
|
||||
// Unheard profiles have unique sized pockets
|
||||
RootEquipmentPool = GetPocketPoolByGameEdition(chosenGameVersion, templateInventory, isPmc),
|
||||
@@ -255,6 +276,7 @@ public class BotInventoryGenerator(
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = EquipmentSlots.FaceCover,
|
||||
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.FaceCover],
|
||||
ModPool = templateInventory.Mods,
|
||||
@@ -275,6 +297,7 @@ public class BotInventoryGenerator(
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = EquipmentSlots.Headwear,
|
||||
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Headwear],
|
||||
ModPool = templateInventory.Mods,
|
||||
@@ -295,6 +318,7 @@ public class BotInventoryGenerator(
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = EquipmentSlots.Earpiece,
|
||||
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Earpiece],
|
||||
ModPool = templateInventory.Mods,
|
||||
@@ -315,6 +339,7 @@ public class BotInventoryGenerator(
|
||||
var hasArmorVest = GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = EquipmentSlots.ArmorVest,
|
||||
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.ArmorVest],
|
||||
ModPool = templateInventory.Mods,
|
||||
@@ -355,6 +380,7 @@ public class BotInventoryGenerator(
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
{
|
||||
BotId = botId,
|
||||
RootEquipmentSlot = EquipmentSlots.TacticalVest,
|
||||
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.TacticalVest],
|
||||
ModPool = templateInventory.Mods,
|
||||
@@ -573,6 +599,12 @@ public class BotInventoryGenerator(
|
||||
settings.Inventory.Items.Add(item);
|
||||
}
|
||||
|
||||
// Cache container ready for items to be added in
|
||||
if (_equipmentSlotsWithInventory.Contains(settings.RootEquipmentSlot))
|
||||
{
|
||||
botInventoryContainerService.AddEmptyContainerToBot(settings.BotId, settings.RootEquipmentSlot, item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -622,6 +654,7 @@ public class BotInventoryGenerator(
|
||||
/// <summary>
|
||||
/// Work out what weapons bot should have equipped and add them to bot inventory
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="templateInventory">bot/x.json data from db</param>
|
||||
/// <param name="equipmentChances">Chances bot can have equipment equipped</param>
|
||||
/// <param name="sessionId">Session id</param>
|
||||
@@ -631,6 +664,7 @@ public class BotInventoryGenerator(
|
||||
/// <param name="itemGenerationLimitsMinMax">Limits for items the bot can have</param>
|
||||
/// <param name="botLevel">level of bot having weapon generated</param>
|
||||
public void GenerateAndAddWeaponsToBot(
|
||||
MongoId botId,
|
||||
BotTypeInventory templateInventory,
|
||||
Chances equipmentChances,
|
||||
MongoId sessionId,
|
||||
@@ -648,6 +682,7 @@ public class BotInventoryGenerator(
|
||||
if (desiredWeapons.ShouldSpawn && templateInventory.Equipment[desiredWeapons.Slot].Any())
|
||||
{
|
||||
AddWeaponAndMagazinesToInventory(
|
||||
botId,
|
||||
sessionId,
|
||||
desiredWeapons,
|
||||
templateInventory,
|
||||
@@ -689,6 +724,7 @@ public class BotInventoryGenerator(
|
||||
/// <summary>
|
||||
/// Add weapon + spare mags/ammo to bots inventory
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="weaponSlot">Weapon slot being generated</param>
|
||||
/// <param name="templateInventory">bot/x.json data from db</param>
|
||||
@@ -699,6 +735,7 @@ public class BotInventoryGenerator(
|
||||
/// <param name="itemGenerationWeights"></param>
|
||||
/// <param name="botLevel"></param>
|
||||
public void AddWeaponAndMagazinesToInventory(
|
||||
MongoId botId,
|
||||
MongoId sessionId,
|
||||
DesiredWeapons weaponSlot,
|
||||
BotTypeInventory templateInventory,
|
||||
@@ -723,7 +760,13 @@ public class BotInventoryGenerator(
|
||||
|
||||
botInventory.Items.AddRange(generatedWeapon.Weapon);
|
||||
|
||||
botWeaponGenerator.AddExtraMagazinesToInventory(generatedWeapon, itemGenerationWeights.Items.Magazines, botInventory, botRole);
|
||||
botWeaponGenerator.AddExtraMagazinesToInventory(
|
||||
botId,
|
||||
generatedWeapon,
|
||||
itemGenerationWeights.Items.Magazines,
|
||||
botInventory,
|
||||
botRole
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ public class BotLootGenerator(
|
||||
/// <summary>
|
||||
/// Add loot to bots containers
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="botJsonTemplate">Clone of Base JSON db file for the bot having its loot generated</param>
|
||||
/// <param name="botGenerationDetails">Details relating to generating a bot</param>
|
||||
@@ -65,6 +66,7 @@ public class BotLootGenerator(
|
||||
/// <param name="botInventory">Inventory to add loot to</param>
|
||||
/// <param name="botLevel">Level of bot</param>
|
||||
public void GenerateLoot(
|
||||
MongoId botId,
|
||||
MongoId sessionId,
|
||||
BotType botJsonTemplate,
|
||||
BotGenerationDetails botGenerationDetails,
|
||||
@@ -119,7 +121,7 @@ public class BotLootGenerator(
|
||||
// Forced pmc healing loot into secure container
|
||||
if (isPmc && _pmcConfig.ForceHealingItemsIntoSecure)
|
||||
{
|
||||
AddForcedMedicalItemsToPmcSecure(botInventory, botRole);
|
||||
AddForcedMedicalItemsToPmcSecure(botInventory, botRole, botId);
|
||||
}
|
||||
|
||||
var botItemLimits = GetItemSpawnLimitsForBot(botRole);
|
||||
@@ -132,6 +134,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Special items
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Special, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
specialLootItemCount,
|
||||
@@ -143,6 +146,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Healing items / Meds
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.HealingItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
healingItemCount,
|
||||
@@ -156,6 +160,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Drugs
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrugItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
drugItemCount,
|
||||
@@ -169,6 +174,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Food
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.FoodItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
foodItemCount,
|
||||
@@ -182,6 +188,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Drink
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrinkItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
drinkItemCount,
|
||||
@@ -195,6 +202,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Currency
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.CurrencyItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
currencyItemCount,
|
||||
@@ -208,6 +216,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Stims
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.StimItems, botJsonTemplate),
|
||||
containersBotHasAvailable,
|
||||
stimItemCount,
|
||||
@@ -221,6 +230,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Grenades
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.GrenadeItems, botJsonTemplate),
|
||||
[EquipmentSlots.Pockets, EquipmentSlots.TacticalVest], // Can't use containersBotHasEquipped as we don't want grenades added to backpack
|
||||
grenadeCount,
|
||||
@@ -241,6 +251,7 @@ public class BotLootGenerator(
|
||||
if (isPmc && randomUtil.GetChance100(_pmcConfig.LooseWeaponInBackpackChancePercent))
|
||||
{
|
||||
AddLooseWeaponsToInventorySlot(
|
||||
botId,
|
||||
sessionId,
|
||||
botInventory,
|
||||
EquipmentSlots.Backpack,
|
||||
@@ -258,6 +269,7 @@ public class BotLootGenerator(
|
||||
: 0;
|
||||
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Backpack, botJsonTemplate, itemPriceLimits?.Backpack),
|
||||
[EquipmentSlots.Backpack],
|
||||
backpackLootCount,
|
||||
@@ -277,6 +289,7 @@ public class BotLootGenerator(
|
||||
// Vest
|
||||
{
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Vest, botJsonTemplate, itemPriceLimits?.Vest),
|
||||
[EquipmentSlots.TacticalVest],
|
||||
vestLootCount,
|
||||
@@ -293,6 +306,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Pockets
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Pocket, botJsonTemplate, itemPriceLimits?.Pocket),
|
||||
[EquipmentSlots.Pockets],
|
||||
pocketLootCount,
|
||||
@@ -310,6 +324,7 @@ public class BotLootGenerator(
|
||||
if (!isPmc || (isPmc && _pmcConfig.AddSecureContainerLootFromBotConfig))
|
||||
{
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Secure, botJsonTemplate),
|
||||
[EquipmentSlots.SecuredContainer],
|
||||
50,
|
||||
@@ -365,10 +380,12 @@ public class BotLootGenerator(
|
||||
/// </summary>
|
||||
/// <param name="botInventory">Inventory to add items to</param>
|
||||
/// <param name="botRole">Role of bot (pmcBEAR/pmcUSEC)</param>
|
||||
protected void AddForcedMedicalItemsToPmcSecure(BotBaseInventory botInventory, string botRole)
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
protected void AddForcedMedicalItemsToPmcSecure(BotBaseInventory botInventory, string botRole, MongoId botId)
|
||||
{
|
||||
// surv12
|
||||
AddLootFromPool(
|
||||
botId,
|
||||
new Dictionary<MongoId, double> { { ItemTpl.MEDICAL_SURV12_FIELD_SURGICAL_KIT, 1 } },
|
||||
[EquipmentSlots.SecuredContainer],
|
||||
1,
|
||||
@@ -380,21 +397,14 @@ public class BotLootGenerator(
|
||||
);
|
||||
|
||||
// AFAK
|
||||
AddLootFromPool(
|
||||
new Dictionary<MongoId, double> { { ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT, 1 } },
|
||||
[EquipmentSlots.SecuredContainer],
|
||||
10,
|
||||
botInventory,
|
||||
botRole,
|
||||
null,
|
||||
0,
|
||||
true
|
||||
);
|
||||
var afaks = new Dictionary<MongoId, double> { { ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT, 1 } };
|
||||
AddLootFromPool(botId, afaks, [EquipmentSlots.SecuredContainer], 10, botInventory, botRole, null, 0, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take random items from a pool and add to an inventory until totalItemCount or totalValueLimit or space limit is reached
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="pool">Pool of items to pick from with weight</param>
|
||||
/// <param name="equipmentSlots">What equipment slot will the loot items be added to</param>
|
||||
/// <param name="totalItemCount">Max count of items to add</param>
|
||||
@@ -405,6 +415,7 @@ public class BotLootGenerator(
|
||||
/// <param name="totalValueLimitRub">Total value of loot allowed in roubles</param>
|
||||
/// <param name="isPmc">Is bot being generated for a pmc</param>
|
||||
protected internal void AddLootFromPool(
|
||||
MongoId botId,
|
||||
Dictionary<MongoId, double> pool,
|
||||
HashSet<EquipmentSlots> equipmentSlots,
|
||||
double totalItemCount,
|
||||
@@ -498,6 +509,7 @@ public class BotLootGenerator(
|
||||
|
||||
// Attempt to add item to container(s)
|
||||
var itemAddedResult = botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
equipmentSlots,
|
||||
newRootItemId,
|
||||
itemToAddTemplate.Id,
|
||||
@@ -619,6 +631,7 @@ public class BotLootGenerator(
|
||||
/// <summary>
|
||||
/// Add generated weapons to inventory as loot
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="sessionId">Session/Player id</param>
|
||||
/// <param name="botInventory">Inventory to add preset to</param>
|
||||
/// <param name="equipmentSlot">Slot to place the preset in (backpack)</param>
|
||||
@@ -629,6 +642,7 @@ public class BotLootGenerator(
|
||||
/// <param name="botLevel"></param>
|
||||
/// <param name="containersIdFull"></param>
|
||||
public void AddLooseWeaponsToInventorySlot(
|
||||
MongoId botId,
|
||||
MongoId sessionId,
|
||||
BotBaseInventory botInventory,
|
||||
EquipmentSlots equipmentSlot,
|
||||
@@ -679,6 +693,7 @@ public class BotLootGenerator(
|
||||
continue;
|
||||
}
|
||||
var result = botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[equipmentSlot],
|
||||
weaponRootItem.Id,
|
||||
weaponRootItem.Template,
|
||||
|
||||
@@ -413,11 +413,13 @@ public class BotWeaponGenerator(
|
||||
/// Generates extra magazines or bullets (if magazine is internal) and adds them to TacticalVest and Pockets.
|
||||
/// Additionally, adds extra bullets to SecuredContainer
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="generatedWeaponResult">Object with properties for generated weapon (weapon mods pool / weapon template / ammo tpl)</param>
|
||||
/// <param name="magWeights">Magazine weights for count to add to inventory</param>
|
||||
/// <param name="inventory">Inventory to add magazines to</param>
|
||||
/// <param name="botRole">The bot type we're generating extra mags for</param>
|
||||
public void AddExtraMagazinesToInventory(
|
||||
MongoId botId,
|
||||
GenerateWeaponResult generatedWeaponResult,
|
||||
GenerationData magWeights,
|
||||
BotBaseInventory inventory,
|
||||
@@ -448,15 +450,16 @@ public class BotWeaponGenerator(
|
||||
// Has an UBGL
|
||||
if (generatedWeaponResult.ChosenUbglAmmoTemplate is not null && !generatedWeaponResult.ChosenUbglAmmoTemplate.Value.IsEmpty)
|
||||
{
|
||||
AddUbglGrenadesToBotInventory(weaponAndMods, generatedWeaponResult, inventory);
|
||||
AddUbglGrenadesToBotInventory(botId, weaponAndMods, generatedWeaponResult, inventory);
|
||||
}
|
||||
|
||||
var inventoryMagGenModel = new InventoryMagGen(magWeights, magTemplate, weaponTemplate, ammoTemplate.Value, inventory);
|
||||
var inventoryMagGenModel = new InventoryMagGen(magWeights, magTemplate, weaponTemplate, ammoTemplate.Value, inventory, botId);
|
||||
|
||||
_inventoryMagGenComponents.FirstOrDefault(v => v.CanHandleInventoryMagGen(inventoryMagGenModel)).Process(inventoryMagGenModel);
|
||||
|
||||
// Add x stacks of bullets to SecuredContainer (bots use a magic mag packing skill to reload instantly)
|
||||
AddAmmoToSecureContainer(
|
||||
botId,
|
||||
_botConfig.SecureContainerAmmoStackCount,
|
||||
generatedWeaponResult.ChosenAmmoTemplate,
|
||||
ammoTemplate.Value.Properties.StackMaxSize ?? 0,
|
||||
@@ -467,10 +470,12 @@ public class BotWeaponGenerator(
|
||||
/// <summary>
|
||||
/// Add Grenades for UBGL to bot's vest and secure container
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="weaponMods">Weapon list with mods</param>
|
||||
/// <param name="generatedWeaponResult">Result of weapon generation</param>
|
||||
/// <param name="inventory">Bot inventory to add grenades to</param>
|
||||
protected void AddUbglGrenadesToBotInventory(
|
||||
MongoId botId,
|
||||
List<Item> weaponMods,
|
||||
GenerateWeaponResult generatedWeaponResult,
|
||||
BotBaseInventory inventory
|
||||
@@ -491,11 +496,11 @@ public class BotWeaponGenerator(
|
||||
var ubglAmmoDbTemplate = itemHelper.GetItem(generatedWeaponResult.ChosenUbglAmmoTemplate.Value).Value;
|
||||
|
||||
// Add greandes to bot inventory
|
||||
var ubglAmmoGenModel = new InventoryMagGen(ubglMinMax, ubglDbTemplate, ubglDbTemplate, ubglAmmoDbTemplate, inventory);
|
||||
var ubglAmmoGenModel = new InventoryMagGen(ubglMinMax, ubglDbTemplate, ubglDbTemplate, ubglAmmoDbTemplate, inventory, botId);
|
||||
_inventoryMagGenComponents.FirstOrDefault(v => v.CanHandleInventoryMagGen(ubglAmmoGenModel)).Process(ubglAmmoGenModel);
|
||||
|
||||
// Store extra grenades in secure container
|
||||
AddAmmoToSecureContainer(5, generatedWeaponResult.ChosenUbglAmmoTemplate.Value, 20, inventory);
|
||||
AddAmmoToSecureContainer(botId, 5, generatedWeaponResult.ChosenUbglAmmoTemplate.Value, 20, inventory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -505,13 +510,14 @@ public class BotWeaponGenerator(
|
||||
/// <param name="ammoTpl">Ammo type to add.</param>
|
||||
/// <param name="stackSize">Size of the ammo stack to add.</param>
|
||||
/// <param name="inventory">Player inventory.</param>
|
||||
protected void AddAmmoToSecureContainer(int stackCount, MongoId ammoTpl, int stackSize, BotBaseInventory inventory)
|
||||
protected void AddAmmoToSecureContainer(MongoId botId, int stackCount, MongoId ammoTpl, int stackSize, BotBaseInventory inventory)
|
||||
{
|
||||
var container = new HashSet<EquipmentSlots> { EquipmentSlots.SecuredContainer };
|
||||
for (var i = 0; i < stackCount; i++)
|
||||
{
|
||||
var id = new MongoId();
|
||||
botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
container,
|
||||
id,
|
||||
ammoTpl,
|
||||
|
||||
@@ -113,6 +113,7 @@ public class PlayerScavGenerator(
|
||||
|
||||
// Add additional items to player scav as loot
|
||||
AddAdditionalLootToPlayerScavContainers(
|
||||
scavData.Id.Value,
|
||||
playerScavKarmaSettings.LootItemsToAddChancePercent,
|
||||
scavData,
|
||||
[EquipmentSlots.TacticalVest, EquipmentSlots.Pockets, EquipmentSlots.Backpack]
|
||||
@@ -133,10 +134,12 @@ public class PlayerScavGenerator(
|
||||
/// <summary>
|
||||
/// Add items picked from `playerscav.lootItemsToAddChancePercent`
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="possibleItemsToAdd">dict of tpl + % chance to be added</param>
|
||||
/// <param name="scavData"></param>
|
||||
/// <param name="containersToAddTo">Possible slotIds to add loot to</param>
|
||||
protected void AddAdditionalLootToPlayerScavContainers(
|
||||
MongoId botId,
|
||||
Dictionary<MongoId, double> possibleItemsToAdd,
|
||||
BotBase scavData,
|
||||
HashSet<EquipmentSlots> containersToAddTo
|
||||
@@ -169,6 +172,7 @@ public class PlayerScavGenerator(
|
||||
};
|
||||
|
||||
var result = botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
containersToAddTo,
|
||||
itemsToAdd[0].Id,
|
||||
itemTemplate.Id,
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ public class BarrelInventoryMagGen(RandomUtil randomUtil, BotWeaponGeneratorHelp
|
||||
}
|
||||
|
||||
botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
|
||||
inventoryMagGen.GetBotId(),
|
||||
inventoryMagGen.GetAmmoTemplate().Id,
|
||||
(int)randomisedAmmoStackSize,
|
||||
inventoryMagGen.GetPmcInventory(),
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ public class ExternalInventoryMagGen(
|
||||
);
|
||||
|
||||
var fitsIntoInventory = botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
inventoryMagGen.GetBotId(),
|
||||
[EquipmentSlots.TacticalVest, EquipmentSlots.Pockets],
|
||||
magazineWithAmmo[0].Id,
|
||||
magazineTpl,
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ public class InternalMagazineInventoryMagGen(BotWeaponGeneratorHelper botWeaponG
|
||||
inventoryMagGen.GetMagazineTemplate()
|
||||
);
|
||||
botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
|
||||
inventoryMagGen.GetBotId(),
|
||||
inventoryMagGen.GetAmmoTemplate().Id,
|
||||
(int)bulletCount,
|
||||
inventoryMagGen.GetPmcInventory(),
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ public class UbglExternalMagGen(BotWeaponGeneratorHelper botWeaponGeneratorHelpe
|
||||
inventoryMagGen.GetMagazineTemplate()
|
||||
);
|
||||
botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
|
||||
inventoryMagGen.GetBotId(),
|
||||
inventoryMagGen.GetAmmoTemplate().Id,
|
||||
(int)bulletCount,
|
||||
inventoryMagGen.GetPmcInventory(),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
|
||||
namespace SPTarkov.Server.Core.Generators.WeaponGen;
|
||||
@@ -10,6 +11,7 @@ public class InventoryMagGen()
|
||||
private readonly TemplateItem? _magazineTemplate;
|
||||
private readonly GenerationData? _magCounts;
|
||||
private readonly BotBaseInventory? _pmcInventory;
|
||||
private readonly MongoId _botId;
|
||||
private readonly TemplateItem? _weaponTemplate;
|
||||
|
||||
public InventoryMagGen(
|
||||
@@ -17,7 +19,8 @@ public class InventoryMagGen()
|
||||
TemplateItem magazineTemplate,
|
||||
TemplateItem weaponTemplate,
|
||||
TemplateItem ammoTemplate,
|
||||
BotBaseInventory pmcInventory
|
||||
BotBaseInventory pmcInventory,
|
||||
MongoId botId
|
||||
)
|
||||
: this()
|
||||
{
|
||||
@@ -26,6 +29,7 @@ public class InventoryMagGen()
|
||||
_weaponTemplate = weaponTemplate;
|
||||
_ammoTemplate = ammoTemplate;
|
||||
_pmcInventory = pmcInventory;
|
||||
_botId = botId;
|
||||
}
|
||||
|
||||
public GenerationData GetMagCount()
|
||||
@@ -52,4 +56,9 @@ public class InventoryMagGen()
|
||||
{
|
||||
return _pmcInventory!;
|
||||
}
|
||||
|
||||
public MongoId GetBotId()
|
||||
{
|
||||
return _botId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Frozen;
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Constants;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
@@ -24,6 +23,7 @@ public class BotGeneratorHelper(
|
||||
InventoryHelper inventoryHelper,
|
||||
ProfileActivityService profileActivityService,
|
||||
ServerLocalisationService serverLocalisationService,
|
||||
BotInventoryContainerService botInventoryContainerService,
|
||||
ConfigServer configServer
|
||||
)
|
||||
{
|
||||
@@ -428,9 +428,10 @@ public class BotGeneratorHelper(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item with all its children into specified equipmentSlots, wherever it fits.
|
||||
/// Adds an item with all its children into specified equipmentSlots, wherever it fits
|
||||
/// </summary>
|
||||
/// <param name="equipmentSlots">Slot to add item+children into</param>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="equipmentSlots">Slot to try and add item+children into</param>
|
||||
/// <param name="rootItemId">Root item id to use as mod items parentId</param>
|
||||
/// <param name="rootItemTplId">Root items tpl id</param>
|
||||
/// <param name="itemWithChildren">Item to add</param>
|
||||
@@ -438,6 +439,7 @@ public class BotGeneratorHelper(
|
||||
/// <param name="containersIdFull">Container Ids with no space for more items</param>
|
||||
/// <returns>ItemAddedResult result object</returns>
|
||||
public ItemAddedResult AddItemWithChildrenToEquipmentSlot(
|
||||
MongoId botId,
|
||||
HashSet<EquipmentSlots> equipmentSlots,
|
||||
MongoId rootItemId,
|
||||
MongoId rootItemTplId,
|
||||
@@ -481,7 +483,7 @@ public class BotGeneratorHelper(
|
||||
}
|
||||
|
||||
// Get container details from db
|
||||
var (isValidItem, itemDbDetails) = itemHelper.GetItem(container.Template);
|
||||
var (isValidItem, containerDbDetails) = itemHelper.GetItem(container.Template);
|
||||
if (!isValidItem)
|
||||
{
|
||||
logger.Warning(serverLocalisationService.GetText("bot-missing_container_with_tpl", container.Template));
|
||||
@@ -490,7 +492,7 @@ public class BotGeneratorHelper(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (itemDbDetails?.Properties?.Grids is null || !itemDbDetails.Properties.Grids.Any())
|
||||
if (containerDbDetails?.Properties?.Grids is null || !containerDbDetails.Properties.Grids.Any())
|
||||
{
|
||||
// Container has no slots to hold items, skip to next container
|
||||
continue;
|
||||
@@ -499,171 +501,23 @@ public class BotGeneratorHelper(
|
||||
// Get x/y grid size of item
|
||||
var (itemWidth, itemHeight) = inventoryHelper.GetItemSize(rootItemTplId, rootItemId, itemWithChildrenList);
|
||||
|
||||
// Iterate over each grid in the container and look for a big enough space for the item to be placed in
|
||||
var currentGridCount = 1;
|
||||
var totalSlotGridCount = itemDbDetails?.Properties?.Grids?.Count();
|
||||
foreach (var slotGrid in itemDbDetails?.Properties?.Grids ?? [])
|
||||
{
|
||||
// Grid is empty, skip or item size is bigger than grid
|
||||
if (IsGridSmallerThanItem(slotGrid, itemWidth, itemHeight))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Can't put item type in grid, skip all grids as we're assuming they have the same rules
|
||||
if (!ItemAllowedInContainer(slotGrid, rootItemTplId))
|
||||
// Multiple containers, maybe next one allows item, only break out of loop for the containers grids
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Get all root items in container
|
||||
var rootItemsInContainer = inventory.Items is null
|
||||
? []
|
||||
: inventory.Items.Where(item => item.SlotId == slotGrid.Name && item.ParentId == container.Id);
|
||||
|
||||
// Get each root item + children
|
||||
var containerItemsWithChildren = GetContainerItemsWithChildren(rootItemsInContainer, inventory.Items);
|
||||
|
||||
if (slotGrid.Props is not null)
|
||||
{
|
||||
// Get rid of an items free/used spots in current grid
|
||||
var slotGridMap = inventoryHelper.GetContainerMap(
|
||||
slotGrid.Props.CellsH.GetValueOrDefault(),
|
||||
slotGrid.Props.CellsV.GetValueOrDefault(),
|
||||
containerItemsWithChildren,
|
||||
container.Id
|
||||
);
|
||||
|
||||
// Try to fit item into grid
|
||||
var findSlotResult = slotGridMap.FindSlotForItem(itemWidth, itemHeight);
|
||||
|
||||
// Free slot found, add item
|
||||
if (findSlotResult.Success ?? false)
|
||||
{
|
||||
var parentItem = itemWithChildrenList.FirstOrDefault(i => i.Id == rootItemId);
|
||||
|
||||
// Set items parent to container id
|
||||
if (parentItem is not null)
|
||||
{
|
||||
parentItem.ParentId = container.Id;
|
||||
parentItem.SlotId = slotGrid.Name;
|
||||
parentItem.Location = new ItemLocation
|
||||
{
|
||||
X = findSlotResult.X,
|
||||
Y = findSlotResult.Y,
|
||||
R = findSlotResult.Rotation ?? false ? ItemRotation.Vertical : ItemRotation.Horizontal,
|
||||
};
|
||||
}
|
||||
|
||||
(inventory.Items ?? []).AddRange(itemWithChildrenList);
|
||||
|
||||
return ItemAddedResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've checked all grids in container and reached this point, there's no space for item
|
||||
if (currentGridCount >= totalSlotGridCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentGridCount++;
|
||||
// No space in this grid, move to next container grid and try again
|
||||
}
|
||||
|
||||
// If we got to this point, the item couldn't be placed on the container
|
||||
if (containersIdFull is null)
|
||||
var result = botInventoryContainerService.AddItemToBotContainer(
|
||||
botId,
|
||||
equipmentSlotId,
|
||||
itemWithChildrenList,
|
||||
inventory,
|
||||
itemWidth,
|
||||
itemHeight
|
||||
);
|
||||
if (result != ItemAddedResult.SUCCESS)
|
||||
{
|
||||
// Failed to add to container, try next
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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 (itemWidth == 1 && itemHeight == 1)
|
||||
{
|
||||
containersIdFull.Add(equipmentSlotId.ToString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return ItemAddedResult.NO_SPACE;
|
||||
}
|
||||
|
||||
protected static bool IsGridSmallerThanItem(Grid slotGrid, int itemWidth, int itemHeight)
|
||||
{
|
||||
return slotGrid.Props?.CellsH == 0
|
||||
|| slotGrid.Props?.CellsV == 0
|
||||
|| itemWidth * itemHeight > slotGrid.Props?.CellsV * slotGrid.Props?.CellsH;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take a list of items and check if they need children + add them
|
||||
/// </summary>
|
||||
/// <param name="containerRootItems"></param>
|
||||
/// <param name="inventoryItems"></param>
|
||||
/// <returns></returns>
|
||||
protected List<Item> GetContainerItemsWithChildren(IEnumerable<Item> containerRootItems, IEnumerable<Item> inventoryItems)
|
||||
{
|
||||
var result = new List<Item>();
|
||||
if (!containerRootItems.Any())
|
||||
{
|
||||
// Container has no root items
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get collection of items likely to be children of root items
|
||||
var itemsWithoutLocation = inventoryItems.Where(item => item.Location is null && item.ParentId is not null).ToList();
|
||||
foreach (var rootItem in containerRootItems)
|
||||
{
|
||||
// Check item in container for children, store for later insertion into `containerItemsToCheck`
|
||||
// (used later when figuring out how much space weapon takes up)
|
||||
itemsWithoutLocation.Insert(0, rootItem);
|
||||
var itemWithChildItems = itemsWithoutLocation.GetItemWithChildren(rootItem.Id);
|
||||
|
||||
// Item had children, replace existing data with item + its children
|
||||
result.AddRange(itemWithChildItems);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the provided item allowed inside a container
|
||||
/// </summary>
|
||||
/// <param name="slotGrid">Items sub-grid we want to place item inside</param>
|
||||
/// <param name="itemTpl">Item tpl being placed</param>
|
||||
/// <returns>True if allowed</returns>
|
||||
protected bool ItemAllowedInContainer(Grid? slotGrid, MongoId itemTpl)
|
||||
{
|
||||
var propFilters = slotGrid?.Props?.Filters;
|
||||
if (propFilters is null || !propFilters.Any())
|
||||
// no filters, item is fine to add
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if item base type is excluded
|
||||
var itemDetails = itemHelper.GetItem(itemTpl).Value;
|
||||
|
||||
// if item to add is found in exclude filter, not allowed
|
||||
var excludedFilter = propFilters.FirstOrDefault()?.ExcludedFilter ?? [];
|
||||
if (excludedFilter.Contains(itemDetails?.Parent ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If Filter array only contains 1 filter and it is for basetype 'item', allow it
|
||||
var filter = propFilters.FirstOrDefault()?.Filter ?? [];
|
||||
if (filter.Count == 1 && filter.Contains(BaseClasses.ITEM))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If allowed filter has something in it + filter doesn't have basetype 'item', not allowed
|
||||
if (filter.Count > 0 && !filter.Contains(itemDetails?.Parent ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,11 +99,13 @@ public class BotWeaponGeneratorHelper(
|
||||
/// <summary>
|
||||
/// Add a specific number of cartridges to a bots inventory (defaults to vest and pockets)
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique identifier</param>
|
||||
/// <param name="ammoTpl">Ammo tpl to add to vest/pockets</param>
|
||||
/// <param name="cartridgeCount">Number of cartridges to add to vest/pockets</param>
|
||||
/// <param name="inventory">Bot inventory to add cartridges to</param>
|
||||
/// <param name="equipmentSlotsToAddTo">What equipment slots should bullets be added into</param>
|
||||
public void AddAmmoIntoEquipmentSlots(
|
||||
MongoId botId,
|
||||
MongoId ammoTpl,
|
||||
int cartridgeCount,
|
||||
BotBaseInventory inventory,
|
||||
@@ -125,6 +127,7 @@ public class BotWeaponGeneratorHelper(
|
||||
foreach (var ammoItem in ammoItems)
|
||||
{
|
||||
var result = botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
equipmentSlotsToAddTo,
|
||||
ammoItem.Id,
|
||||
ammoItem.Template,
|
||||
|
||||
@@ -1720,10 +1720,10 @@ public record GridFilter
|
||||
public Dictionary<string, object>? ExtensionData { get; set; }
|
||||
|
||||
[JsonPropertyName("Filter")]
|
||||
public HashSet<string>? Filter { get; set; }
|
||||
public HashSet<MongoId>? Filter { get; set; }
|
||||
|
||||
[JsonPropertyName("ExcludedFilter")]
|
||||
public HashSet<string>? ExcludedFilter { get; set; }
|
||||
public HashSet<MongoId>? ExcludedFilter { get; set; }
|
||||
|
||||
[JsonPropertyName("locked")]
|
||||
public bool? Locked { get; set; }
|
||||
|
||||
@@ -11,6 +11,8 @@ public record GenerateEquipmentProperties
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, object>? ExtensionData { get; set; }
|
||||
|
||||
public MongoId BotId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Root Slot being generated
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
using System.Collections.Concurrent;
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Models.Utils;
|
||||
|
||||
namespace SPTarkov.Server.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for keeping track of items and their exact position inside a bots container
|
||||
/// </summary>
|
||||
[Injectable]
|
||||
public class BotInventoryContainerService(ISptLogger<BotGeneratorHelper> logger, ItemHelper itemHelper)
|
||||
{
|
||||
// botId/containerName
|
||||
private readonly ConcurrentDictionary<MongoId, Dictionary<EquipmentSlots, ContainerDetails>> _botContainers = new();
|
||||
|
||||
/// <summary>
|
||||
/// Add a container + details to a bots cache ready to accept loot
|
||||
/// </summary>
|
||||
/// <param name="botId">Unique identifier of bot</param>
|
||||
/// <param name="containerName">name of container e.g. "Backpack"</param>
|
||||
/// <param name="containerInventoryItem">Inventory item loot will be linked to in bots inventory</param>
|
||||
public void AddEmptyContainerToBot(MongoId botId, EquipmentSlots containerName, Item containerInventoryItem)
|
||||
{
|
||||
// Add bot to dict if it doesn't exist
|
||||
_botContainers.TryAdd(botId, new());
|
||||
|
||||
// Get the bots' currently cached containers
|
||||
var containers = GetOrCreateBotContainerDictionary(botId);
|
||||
|
||||
// Add container to bot
|
||||
if (!containers.ContainsKey(containerName))
|
||||
{
|
||||
var containerDbItem = itemHelper.GetItem(containerInventoryItem.Template);
|
||||
containers.Add(containerName, new ContainerDetails(containerDbItem.Value, containerInventoryItem));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to add an item + children to a container
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique id</param>
|
||||
/// <param name="containerName">Name of container to add to e.g. "Backpack"</param>
|
||||
/// <param name="itemAndChildren">Item and its children to add to container</param>
|
||||
/// <param name="botInventory">Inventory to add Item+children to</param>
|
||||
/// <param name="itemWidth">Width of item with its children</param>
|
||||
/// <param name="itemHeight">Height of item with its children</param>
|
||||
/// <returns>ItemAddedResult</returns>
|
||||
public ItemAddedResult AddItemToBotContainer(
|
||||
MongoId botId,
|
||||
EquipmentSlots containerName,
|
||||
List<Item> itemAndChildren,
|
||||
BotBaseInventory botInventory,
|
||||
int itemWidth,
|
||||
int itemHeight
|
||||
)
|
||||
{
|
||||
var addResult = ItemAddedResult.UNKNOWN;
|
||||
|
||||
// Find bot and the container we will attempt to add into
|
||||
var botContainers = GetOrCreateBotContainerDictionary(botId);
|
||||
|
||||
botContainers.TryGetValue(containerName, out var containerDetails);
|
||||
|
||||
if (containerDetails.ContainerGridDetails.Count == 0)
|
||||
{
|
||||
// No grids, cannot add item
|
||||
return ItemAddedResult.NO_CONTAINERS;
|
||||
}
|
||||
|
||||
if (!ItemAllowedInContainer(containerDetails, itemAndChildren))
|
||||
// Multiple containers, maybe next one allows item, only break out of loop for the containers grids
|
||||
{
|
||||
return ItemAddedResult.INCOMPATIBLE_ITEM;
|
||||
}
|
||||
|
||||
// Try to fit item into one of the containers' grids
|
||||
var rootItem = itemAndChildren.FirstOrDefault();
|
||||
var gridIndex = 0;
|
||||
foreach (var gridDb in containerDetails.ContainerDbItem.Properties.Grids)
|
||||
{
|
||||
var gridDetails = containerDetails.ContainerGridDetails[gridIndex];
|
||||
if (gridDetails.GridFull)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsItemBiggerThanGrid(gridDetails.GridMap, itemWidth, itemHeight))
|
||||
{
|
||||
// Skip to next grid
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for a slot in the grid to place item
|
||||
var findSlotResult = gridDetails.GridMap.FindSlotForItem(itemWidth, itemHeight);
|
||||
if (findSlotResult.Success.GetValueOrDefault(false))
|
||||
{
|
||||
// It Fits!
|
||||
|
||||
// Set items parent to Id of container
|
||||
if (rootItem is not null)
|
||||
{
|
||||
rootItem.ParentId = containerDetails.ContainerInventoryItem.Id;
|
||||
rootItem.SlotId = gridDb.Name; // Can be name of container e.g. "Backpack" OR "2/3/4/5" depending on which grid of a container item is added to
|
||||
rootItem.Location = new ItemLocation
|
||||
{
|
||||
X = findSlotResult.X,
|
||||
Y = findSlotResult.Y,
|
||||
R = findSlotResult.Rotation ?? false ? ItemRotation.Vertical : ItemRotation.Horizontal,
|
||||
};
|
||||
}
|
||||
|
||||
// Flag result as success to report to caller
|
||||
addResult = ItemAddedResult.SUCCESS;
|
||||
|
||||
// Update grid with slots taken up by above item
|
||||
FillGridRegion(
|
||||
gridDetails.GridMap,
|
||||
findSlotResult.X.Value,
|
||||
findSlotResult.Y.Value,
|
||||
findSlotResult.Rotation.GetValueOrDefault() ? itemHeight : itemWidth,
|
||||
findSlotResult.Rotation.GetValueOrDefault() ? itemWidth : itemHeight
|
||||
);
|
||||
|
||||
// Add item into bots inventory
|
||||
botInventory.Items.AddRange(itemAndChildren);
|
||||
|
||||
// Exit loop, we've found a slot for item
|
||||
break;
|
||||
}
|
||||
|
||||
gridIndex++;
|
||||
|
||||
// Didn't fit, flag as no space, hopefully next grid has space
|
||||
addResult = ItemAddedResult.NO_SPACE;
|
||||
|
||||
// If the item is 1x1 and it failed to fit, grid must be full
|
||||
if (itemHeight == 1 && itemWidth == 1)
|
||||
{
|
||||
gridDetails.GridFull = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if grid is full and flag
|
||||
if (gridDetails.GridMap.ContainerIsFull())
|
||||
{
|
||||
gridDetails.GridFull = true;
|
||||
}
|
||||
}
|
||||
|
||||
return addResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to add an item + children to a container at a specific x/y grid position
|
||||
/// </summary>
|
||||
/// <param name="botId">Bots unique id</param>
|
||||
/// <param name="containerName">Name of container to add to e.g. "Backpack"</param>
|
||||
/// <param name="itemAndChildren">Item and its children to add to container</param>
|
||||
/// <param name="botInventory">Inventory to add Item+children to</param>
|
||||
/// <param name="itemWidth">Width of item with its children</param>
|
||||
/// <param name="itemHeight">Height of item with its children</param>
|
||||
/// <param name="fixedLocation">Details for where to place item in container grid</param>
|
||||
/// <returns>ItemAddedResult</returns>
|
||||
public ItemAddedResult AddItemToBotContainerFixedPosition(
|
||||
MongoId botId,
|
||||
EquipmentSlots containerName,
|
||||
List<Item> itemAndChildren,
|
||||
BotBaseInventory botInventory,
|
||||
int itemWidth,
|
||||
int itemHeight,
|
||||
ItemLocation fixedLocation
|
||||
)
|
||||
{
|
||||
// Default result
|
||||
var addResult = ItemAddedResult.UNKNOWN;
|
||||
|
||||
// Find bot and the container we are attempting to store item in
|
||||
var botContainers = GetOrCreateBotContainerDictionary(botId);
|
||||
|
||||
botContainers.TryGetValue(containerName, out var containerDetails);
|
||||
|
||||
if (containerDetails.ContainerGridDetails.Count == 0)
|
||||
{
|
||||
// No grids, cannot add item
|
||||
return ItemAddedResult.NO_CONTAINERS;
|
||||
}
|
||||
|
||||
if (!ItemAllowedInContainer(containerDetails, itemAndChildren))
|
||||
// Multiple containers, maybe next one allows item, only break out of loop for the containers grids
|
||||
{
|
||||
return ItemAddedResult.INCOMPATIBLE_ITEM;
|
||||
}
|
||||
|
||||
// Try to fit item into one of the containers' grids
|
||||
var rootItem = itemAndChildren.FirstOrDefault();
|
||||
if (rootItem is null)
|
||||
{
|
||||
return ItemAddedResult.UNKNOWN;
|
||||
}
|
||||
foreach (var gridDetails in containerDetails.ContainerGridDetails)
|
||||
{
|
||||
if (gridDetails.GridFull)
|
||||
{
|
||||
// No space, skip early
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsItemBiggerThanGrid(gridDetails.GridMap, itemWidth, itemHeight))
|
||||
{
|
||||
// Skip early
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for a slot in the grid to place item
|
||||
var result = gridDetails.GridMap.FillContainerMapWithItem(
|
||||
fixedLocation.X.Value,
|
||||
fixedLocation.Y.Value,
|
||||
itemWidth,
|
||||
itemHeight,
|
||||
fixedLocation.R == ItemRotation.Vertical
|
||||
);
|
||||
if (result.Item1)
|
||||
{
|
||||
// It Fits!
|
||||
|
||||
// Parent root item to container
|
||||
rootItem.ParentId = containerDetails.ContainerInventoryItem.Id;
|
||||
rootItem.SlotId = containerName.ToString();
|
||||
rootItem.Location = new ItemLocation
|
||||
{
|
||||
X = fixedLocation.X.Value,
|
||||
Y = fixedLocation.Y.Value,
|
||||
R = fixedLocation.R,
|
||||
};
|
||||
|
||||
// Flag result as success to report to caller
|
||||
addResult = ItemAddedResult.SUCCESS;
|
||||
|
||||
// Update internal grid with slots taken up by above item
|
||||
FillGridRegion(
|
||||
gridDetails.GridMap,
|
||||
fixedLocation.X.Value,
|
||||
fixedLocation.Y.Value,
|
||||
fixedLocation.R == ItemRotation.Vertical ? itemHeight : itemWidth,
|
||||
fixedLocation.R == ItemRotation.Vertical ? itemWidth : itemHeight
|
||||
);
|
||||
|
||||
// Item fits + Added to layout grid, add item and children
|
||||
//containerDetails.ItemsAndChildrenInContainer.AddRange(itemAndChildren);
|
||||
|
||||
// Add item into bots inventory
|
||||
botInventory.Items.AddRange(itemAndChildren);
|
||||
|
||||
// Exit loop, we've found a position for item and can stop
|
||||
break;
|
||||
}
|
||||
|
||||
// Didn't fit, flag as no space, hopefully next grid has space
|
||||
addResult = ItemAddedResult.NO_SPACE;
|
||||
|
||||
// If the item is 1x1 and it failed to fit, grid must be full
|
||||
if (itemHeight == 1 && itemWidth == 1)
|
||||
{
|
||||
gridDetails.GridFull = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if grid is full and flag
|
||||
if (gridDetails.GridMap.ContainerIsFull())
|
||||
{
|
||||
gridDetails.GridFull = true;
|
||||
}
|
||||
}
|
||||
|
||||
return addResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper - Get the bot-specific container details, create if data doesn't exist
|
||||
/// </summary>
|
||||
/// <param name="botId">Bot unique identifier</param>
|
||||
/// <returns>Dictionary</returns>
|
||||
protected Dictionary<EquipmentSlots, ContainerDetails> GetOrCreateBotContainerDictionary(MongoId botId)
|
||||
{
|
||||
if (!_botContainers.TryGetValue(botId, out var botContainers))
|
||||
{
|
||||
// Create blank dict ready for containers to be added
|
||||
botContainers = new();
|
||||
}
|
||||
|
||||
return botContainers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill region of a 2D array
|
||||
/// </summary>
|
||||
/// <param name="grid">The 2D grid array to modify</param>
|
||||
/// <param name="x">The starting column index (left)</param>
|
||||
/// <param name="y">The starting row index (top)</param>
|
||||
/// <param name="itemWidth">The number of cells to update horizontally</param>
|
||||
/// <param name="itemHeight">The number of cells to update vertically</param>
|
||||
private void FillGridRegion(int[,] grid, int x, int y, int itemWidth, int itemHeight)
|
||||
{
|
||||
// Outer loop iterates through rows (from starting y position)
|
||||
for (var row = y; row < y + itemHeight; row++)
|
||||
{
|
||||
// Inner loop iterates through columns (from starting x position)
|
||||
for (var col = x; col < x + itemWidth; col++)
|
||||
{
|
||||
grid[row, col] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the items subtype allowed inside this container / is it excluded from this container
|
||||
/// </summary>
|
||||
/// <param name="containerDetails">Details on the container we want to add item into</param>
|
||||
/// <param name="itemAndChildren">Item+children we want to add into container</param>
|
||||
/// <returns>true = item is allowed</returns>
|
||||
private bool ItemAllowedInContainer(ContainerDetails containerDetails, List<Item>? itemAndChildren)
|
||||
{
|
||||
// Assume all grids have same limitations
|
||||
var firstSlotGrid = containerDetails.ContainerDbItem.Properties.Grids.FirstOrDefault();
|
||||
var propFilters = firstSlotGrid?.Props?.Filters;
|
||||
if (propFilters is null || !propFilters.Any())
|
||||
// No filters, item is fine to add
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if item base type is excluded
|
||||
var itemDetails = itemHelper.GetItem(itemAndChildren.FirstOrDefault().Template).Value;
|
||||
|
||||
// if item to add is found in exclude filter, not allowed
|
||||
var excludedFilter = propFilters.FirstOrDefault()?.ExcludedFilter ?? [];
|
||||
if (excludedFilter.Contains(itemDetails?.Parent ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If Filter array only contains 1 filter and it is for basetype 'item', allow it
|
||||
var filter = propFilters.FirstOrDefault()?.Filter ?? [];
|
||||
if (filter.Count == 1 && filter.Contains(BaseClasses.ITEM))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If allowed filter has something in it + filter doesn't have basetype 'item', not allowed
|
||||
if (filter.Count > 0 && !filter.Contains(itemDetails?.Parent ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the items edge length bigger than the grid trying to hold it
|
||||
/// </summary>
|
||||
/// <param name="grid">Container grid</param>
|
||||
/// <param name="itemWidth">Width of item</param>
|
||||
/// <param name="itemHeight">Height of item</param>
|
||||
/// <returns>true = item bigger than grid</returns>
|
||||
private bool IsItemBiggerThanGrid(int[,] grid, int itemWidth, int itemHeight)
|
||||
{
|
||||
var gridHeight = grid.GetLength(0);
|
||||
var gridWidth = grid.GetLength(1);
|
||||
|
||||
// Check if it can fit in either orientation
|
||||
var fitsNormally = itemWidth <= gridWidth && itemHeight <= gridHeight;
|
||||
var fitsRotated = itemHeight <= gridWidth && itemWidth <= gridHeight;
|
||||
|
||||
// Fails both checks
|
||||
return !fitsNormally && !fitsRotated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a bots container details from cache by its id
|
||||
/// </summary>
|
||||
/// <param name="botId">Identifier of bot to get details of</param>
|
||||
/// <returns>Dictionary of containers and their details</returns>
|
||||
public Dictionary<EquipmentSlots, ContainerDetails>? GetBotContainer(MongoId botId)
|
||||
{
|
||||
return GetOrCreateBotContainerDictionary(botId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the cache of all bot containers
|
||||
/// </summary>
|
||||
public void ClearCache()
|
||||
{
|
||||
_botContainers.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear specific bot container details from cache
|
||||
/// </summary>
|
||||
/// <param name="botId">Bot identifier</param>
|
||||
public void ClearCache(MongoId botId)
|
||||
{
|
||||
_botContainers.Remove(botId, out _);
|
||||
}
|
||||
|
||||
public record ContainerDetails
|
||||
{
|
||||
public ContainerDetails(TemplateItem containerDbItem, Item containerInventoryItem)
|
||||
{
|
||||
ContainerDbItem = containerDbItem;
|
||||
ContainerInventoryItem = containerInventoryItem;
|
||||
// Add all grids for this container
|
||||
foreach (var grid in containerDbItem.Properties.Grids)
|
||||
{
|
||||
ContainerGridDetails.Add(
|
||||
new ContainerMapDetails
|
||||
{
|
||||
GridMap = new int[grid.Props.CellsV.GetValueOrDefault(), grid.Props.CellsH.GetValueOrDefault()],
|
||||
GridFull = false,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grid layout and flag if grid is full
|
||||
/// </summary>
|
||||
public List<ContainerMapDetails> ContainerGridDetails { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Db record for the container holding items
|
||||
/// </summary>
|
||||
public TemplateItem ContainerDbItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Inventory item representing the container
|
||||
/// </summary>
|
||||
public Item ContainerInventoryItem { get; set; }
|
||||
|
||||
// TODO: implement this + add checks inside AddItemToBotContainer for perf improvement
|
||||
public bool ContainerFull { get; set; } = false;
|
||||
}
|
||||
|
||||
public record ContainerMapDetails
|
||||
{
|
||||
public int[,] GridMap { get; init; }
|
||||
public bool GridFull { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Enums;
|
||||
using SPTarkov.Server.Core.Services;
|
||||
|
||||
namespace UnitTests.Tests.Helpers;
|
||||
|
||||
@@ -13,12 +14,16 @@ public class BotGeneratorHelperTests
|
||||
{
|
||||
private BotGeneratorHelper _botGeneratorHelper;
|
||||
private BotLootGenerator _botLootGenerator;
|
||||
private BotInventoryContainerService _botInventoryContainerService;
|
||||
private ItemHelper _itemHelper;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Initialize()
|
||||
{
|
||||
_botGeneratorHelper = DI.GetInstance().GetService<BotGeneratorHelper>();
|
||||
_itemHelper = DI.GetInstance().GetService<ItemHelper>();
|
||||
_botLootGenerator = DI.GetInstance().GetService<BotLootGenerator>();
|
||||
_botInventoryContainerService = DI.GetInstance().GetService<BotInventoryContainerService>();
|
||||
}
|
||||
|
||||
#region AddItemWithChildrenToEquipmentSlot
|
||||
@@ -26,6 +31,7 @@ public class BotGeneratorHelperTests
|
||||
[Test]
|
||||
public void AddItemWithChildrenToEquipmentSlot_fit_vertical()
|
||||
{
|
||||
var botId = new MongoId();
|
||||
var stashId = new MongoId();
|
||||
var equipmentId = new MongoId();
|
||||
var botInventory = new BotBaseInventory
|
||||
@@ -42,14 +48,16 @@ public class BotGeneratorHelperTests
|
||||
// Has a 3grids, first is a 3hx5v grid
|
||||
Template = ItemTpl.BACKPACK_EBERLESTOCK_G2_GUNSLINGER_II_BACKPACK_DRY_EARTH,
|
||||
ParentId = equipmentId,
|
||||
SlotId = "Backpack",
|
||||
SlotId = nameof(EquipmentSlots.Backpack),
|
||||
};
|
||||
botInventory.Items.Add(backpack);
|
||||
_botInventoryContainerService.AddEmptyContainerToBot(botId, EquipmentSlots.Backpack, backpack);
|
||||
|
||||
var rootWeaponId = new MongoId();
|
||||
var weaponWithChildren = CreateMp18(rootWeaponId);
|
||||
|
||||
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[EquipmentSlots.Backpack],
|
||||
rootWeaponId,
|
||||
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
|
||||
@@ -93,6 +101,7 @@ public class BotGeneratorHelperTests
|
||||
[Test]
|
||||
public void AddItemWithChildrenToEquipmentSlot_fit_horizontal()
|
||||
{
|
||||
var botId = new MongoId();
|
||||
var stashId = new MongoId();
|
||||
var equipmentId = new MongoId();
|
||||
var botInventory = new BotBaseInventory
|
||||
@@ -108,14 +117,16 @@ public class BotGeneratorHelperTests
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.BACKPACK_ANA_TACTICAL_BETA_2_BATTLE_BACKPACK_OLIVE_DRAB,
|
||||
ParentId = equipmentId,
|
||||
SlotId = "Backpack",
|
||||
SlotId = nameof(EquipmentSlots.Backpack),
|
||||
};
|
||||
botInventory.Items.Add(backpack);
|
||||
_botInventoryContainerService.AddEmptyContainerToBot(botId, EquipmentSlots.Backpack, backpack);
|
||||
|
||||
var rootWeaponId = new MongoId();
|
||||
var weaponWithChildren = CreateMp18(rootWeaponId);
|
||||
|
||||
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[EquipmentSlots.Backpack],
|
||||
rootWeaponId,
|
||||
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
|
||||
@@ -130,7 +141,7 @@ public class BotGeneratorHelperTests
|
||||
{ ItemTpl.BARTER_GOLD_SKULL_RING, 1 },
|
||||
{ ItemTpl.BARTER_PACK_OF_NAILS, 1 },
|
||||
};
|
||||
_botLootGenerator.AddLootFromPool(tplsToAdd, [EquipmentSlots.Backpack], 4, botInventory, "assault", null);
|
||||
_botLootGenerator.AddLootFromPool(botId, tplsToAdd, [EquipmentSlots.Backpack], 4, botInventory, "assault", null);
|
||||
|
||||
Assert.AreEqual(ItemAddedResult.SUCCESS, result);
|
||||
|
||||
@@ -152,37 +163,40 @@ public class BotGeneratorHelperTests
|
||||
[Test]
|
||||
public void AddItemWithChildrenToEquipmentSlot_fit_vertical_with_items_in_backpack()
|
||||
{
|
||||
var botId = new MongoId();
|
||||
var botInventory = new BotBaseInventory { Items = [] };
|
||||
var backpack = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
// Has a 3hx5v grid first
|
||||
Template = ItemTpl.BACKPACK_EBERLESTOCK_G2_GUNSLINGER_II_BACKPACK_DRY_EARTH,
|
||||
SlotId = "Backpack",
|
||||
SlotId = nameof(EquipmentSlots.Backpack),
|
||||
};
|
||||
botInventory.Items.Add(backpack);
|
||||
|
||||
botInventory.Items.Add(
|
||||
new Item
|
||||
_botInventoryContainerService.AddEmptyContainerToBot(botId, EquipmentSlots.Backpack, backpack);
|
||||
|
||||
var akbsCartridge = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
}
|
||||
);
|
||||
X = 0,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
};
|
||||
_botInventoryContainerService.AddItemToBotContainer(botId, EquipmentSlots.Backpack, [akbsCartridge], botInventory, 1, 1);
|
||||
|
||||
var rootWeaponId = new MongoId();
|
||||
var weaponWithChildren = CreateMp18(rootWeaponId);
|
||||
|
||||
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[EquipmentSlots.Backpack],
|
||||
rootWeaponId,
|
||||
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
|
||||
@@ -204,18 +218,28 @@ public class BotGeneratorHelperTests
|
||||
[Test]
|
||||
public void AddItemWithChildrenToEquipmentSlot_no_space_in_first_grid_choose_second_grid()
|
||||
{
|
||||
var botId = new MongoId();
|
||||
var botInventory = new BotBaseInventory { Items = [] };
|
||||
var backpack = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
// Has a 3hx5v grid first
|
||||
Template = ItemTpl.BACKPACK_EBERLESTOCK_G2_GUNSLINGER_II_BACKPACK_DRY_EARTH,
|
||||
SlotId = "Backpack",
|
||||
SlotId = nameof(EquipmentSlots.Backpack),
|
||||
};
|
||||
botInventory.Items.Add(backpack);
|
||||
_botInventoryContainerService.AddEmptyContainerToBot(botId, EquipmentSlots.Backpack, backpack);
|
||||
|
||||
botInventory.Items.AddRange(
|
||||
new Item
|
||||
// Insert items at specific locations
|
||||
var takenSlots = new List<XY>
|
||||
{
|
||||
new() { X = 0, Y = 0 },
|
||||
new() { X = 1, Y = 0 },
|
||||
new() { X = 2, Y = 0 },
|
||||
};
|
||||
foreach (var takenSlot in takenSlots)
|
||||
{
|
||||
var itemToAdd = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
@@ -223,46 +247,29 @@ public class BotGeneratorHelperTests
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
X = (int)takenSlot.X.Value,
|
||||
Y = (int)takenSlot.Y.Value,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 1,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 2,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_botInventoryContainerService.AddItemToBotContainerFixedPosition(
|
||||
botId,
|
||||
EquipmentSlots.Backpack,
|
||||
[itemToAdd],
|
||||
botInventory,
|
||||
1,
|
||||
1,
|
||||
(ItemLocation)itemToAdd.Location
|
||||
);
|
||||
}
|
||||
|
||||
var rootWeaponId = new MongoId();
|
||||
var weaponWithChildren = CreateMp18(rootWeaponId);
|
||||
|
||||
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[EquipmentSlots.Backpack],
|
||||
rootWeaponId,
|
||||
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
|
||||
@@ -271,8 +278,10 @@ public class BotGeneratorHelperTests
|
||||
);
|
||||
|
||||
Assert.AreEqual(ItemAddedResult.SUCCESS, result);
|
||||
|
||||
var weaponRoot = weaponWithChildren.FirstOrDefault(item => item.Id == rootWeaponId);
|
||||
Assert.AreEqual("1", weaponRoot.SlotId);
|
||||
|
||||
Assert.AreEqual((weaponRoot.Location as ItemLocation).X, 0);
|
||||
Assert.AreEqual((weaponRoot.Location as ItemLocation).Y, 0);
|
||||
Assert.AreEqual((weaponRoot.Location as ItemLocation).R, ItemRotation.Vertical);
|
||||
@@ -284,18 +293,29 @@ public class BotGeneratorHelperTests
|
||||
[Test]
|
||||
public void AddItemWithChildrenToEquipmentSlot_no_space()
|
||||
{
|
||||
var botId = new MongoId();
|
||||
var botInventory = new BotBaseInventory { Items = [] };
|
||||
var backpack = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
// Has a 4hx5v grid first
|
||||
Template = ItemTpl.BACKPACK_WARTECH_BERKUT_BB102_BACKPACK_ATACS_FG,
|
||||
SlotId = "Backpack",
|
||||
SlotId = nameof(EquipmentSlots.Backpack),
|
||||
};
|
||||
botInventory.Items.Add(backpack);
|
||||
_botInventoryContainerService.AddEmptyContainerToBot(botId, EquipmentSlots.Backpack, backpack);
|
||||
|
||||
botInventory.Items.AddRange(
|
||||
new Item
|
||||
// Insert items at specific locations
|
||||
var takenSlots = new List<XY>
|
||||
{
|
||||
new() { X = 0, Y = 0 },
|
||||
new() { X = 1, Y = 0 },
|
||||
new() { X = 2, Y = 0 },
|
||||
new() { X = 3, Y = 0 },
|
||||
};
|
||||
foreach (var takenSlot in takenSlots)
|
||||
{
|
||||
var itemToAdd = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
@@ -303,60 +323,29 @@ public class BotGeneratorHelperTests
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
X = (int)takenSlot.X.Value,
|
||||
Y = (int)takenSlot.Y.Value,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 1,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 2,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = 3,
|
||||
Y = 0,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_botInventoryContainerService.AddItemToBotContainerFixedPosition(
|
||||
botId,
|
||||
EquipmentSlots.Backpack,
|
||||
[itemToAdd],
|
||||
botInventory,
|
||||
1,
|
||||
1,
|
||||
(ItemLocation)itemToAdd.Location
|
||||
);
|
||||
}
|
||||
|
||||
var rootWeaponId = new MongoId();
|
||||
var weaponWithChildren = CreateMp18(rootWeaponId);
|
||||
|
||||
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[EquipmentSlots.Backpack],
|
||||
rootWeaponId,
|
||||
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
|
||||
@@ -373,16 +362,20 @@ public class BotGeneratorHelperTests
|
||||
[Test]
|
||||
public void AddItemWithChildrenToEquipmentSlot_custom_gun_no_space()
|
||||
{
|
||||
var botId = new MongoId();
|
||||
var botInventory = new BotBaseInventory { Items = [] };
|
||||
var backpack = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
// Has a 4hx5v grid first
|
||||
Template = ItemTpl.BACKPACK_GRUPPA_99_T30_BACKPACK_BLACK,
|
||||
SlotId = "Backpack",
|
||||
SlotId = nameof(EquipmentSlots.Backpack),
|
||||
};
|
||||
botInventory.Items.Add(backpack);
|
||||
|
||||
botInventory.Items.Add(backpack);
|
||||
_botInventoryContainerService.AddEmptyContainerToBot(botId, EquipmentSlots.Backpack, backpack);
|
||||
|
||||
// Insert items at specific locations to ensure there's no space for adding the weapon
|
||||
var takenSlots = new List<XY>
|
||||
{
|
||||
new() { X = 1, Y = 0 },
|
||||
@@ -407,27 +400,40 @@ public class BotGeneratorHelperTests
|
||||
};
|
||||
foreach (var takenSlot in takenSlots)
|
||||
{
|
||||
botInventory.Items.Add(
|
||||
new Item
|
||||
var itemToAdd = new Item
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
Id = new MongoId(),
|
||||
Template = ItemTpl.AMMO_762X25TT_AKBS,
|
||||
ParentId = backpack.Id,
|
||||
SlotId = "main",
|
||||
Location = new ItemLocation
|
||||
{
|
||||
X = (int)takenSlot.X.Value,
|
||||
Y = (int)takenSlot.Y.Value,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
}
|
||||
X = (int)takenSlot.X.Value,
|
||||
Y = (int)takenSlot.Y.Value,
|
||||
R = ItemRotation.Horizontal,
|
||||
},
|
||||
Upd = new Upd { StackObjectsCount = 1 },
|
||||
};
|
||||
|
||||
_botInventoryContainerService.AddItemToBotContainerFixedPosition(
|
||||
botId,
|
||||
EquipmentSlots.Backpack,
|
||||
[itemToAdd],
|
||||
botInventory,
|
||||
1,
|
||||
1,
|
||||
(ItemLocation)itemToAdd.Location
|
||||
);
|
||||
}
|
||||
|
||||
var rootWeaponId = new MongoId();
|
||||
var weaponWithChildren = new List<Item>();
|
||||
var root = new Item { Id = rootWeaponId, Template = ItemTpl.ASSAULTRIFLE_MOLOT_ARMS_VPO136_VEPRKM_762X39_CARBINE };
|
||||
var root = new Item
|
||||
{
|
||||
Id = rootWeaponId,
|
||||
Template = ItemTpl.ASSAULTRIFLE_MOLOT_ARMS_VPO136_VEPRKM_762X39_CARBINE,
|
||||
ParentId = backpack.Id,
|
||||
};
|
||||
weaponWithChildren.Add(root);
|
||||
|
||||
var stock = new Item
|
||||
@@ -458,6 +464,7 @@ public class BotGeneratorHelperTests
|
||||
weaponWithChildren.Add(muzzle);
|
||||
|
||||
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
botId,
|
||||
[EquipmentSlots.Backpack],
|
||||
rootWeaponId,
|
||||
root.Template,
|
||||
|
||||
Reference in New Issue
Block a user