diff --git a/Core/Generators/PMCLootGenerator.cs b/Core/Generators/PMCLootGenerator.cs
index 5443d142..2e0748f6 100644
--- a/Core/Generators/PMCLootGenerator.cs
+++ b/Core/Generators/PMCLootGenerator.cs
@@ -1,4 +1,4 @@
-using Core.Annotations;
+using Core.Annotations;
using Core.Models.Eft.Common.Tables;
namespace Core.Generators;
@@ -15,7 +15,7 @@ public class PMCLootGenerator
///
///
/// Dictionary of string and number
- public Dictionary GeneratePMCPocketLootPool(string botRole)
+ public Dictionary GeneratePMCPocketLootPool(string botRole)
{
throw new NotImplementedException();
}
@@ -25,7 +25,7 @@ public class PMCLootGenerator
///
///
/// Dictionary of string and number
- public Dictionary GeneratePMCVestLootPool(string botRole)
+ public Dictionary GeneratePMCVestLootPool(string botRole)
{
throw new NotImplementedException();
}
@@ -57,7 +57,7 @@ public class PMCLootGenerator
///
///
/// Dictionary of string and number
- public Dictionary GeneratePMCBackpackLootPool(string botRole)
+ public Dictionary GeneratePMCBackpackLootPool(string botRole)
{
throw new NotImplementedException();
}
diff --git a/Core/Helpers/ItemHelper.cs b/Core/Helpers/ItemHelper.cs
index 313af33f..9dbfa05e 100644
--- a/Core/Helpers/ItemHelper.cs
+++ b/Core/Helpers/ItemHelper.cs
@@ -1044,7 +1044,12 @@ public class ItemHelper
throw new NotImplementedException();
}
- public bool IsOfBaseclass(string tpl, List baseClassTpls)
+ public bool IsOfBaseclass(string tpl, string baseClassTpl)
+ {
+ return _itemBaseClassService.ItemHasBaseClass(tpl, [baseClassTpl]);
+ }
+
+ public bool isOfBaseclasses(string tpl, List baseClassTpls)
{
return _itemBaseClassService.ItemHasBaseClass(tpl, baseClassTpls);
}
diff --git a/Core/Models/Spt/Bots/BotLootCache.cs b/Core/Models/Spt/Bots/BotLootCache.cs
index e6e20078..758f573e 100644
--- a/Core/Models/Spt/Bots/BotLootCache.cs
+++ b/Core/Models/Spt/Bots/BotLootCache.cs
@@ -5,43 +5,43 @@ namespace Core.Models.Spt.Bots;
public class BotLootCache
{
[JsonPropertyName("backpackLoot")]
- public Dictionary? BackpackLoot { get; set; }
+ public Dictionary? BackpackLoot { get; set; }
[JsonPropertyName("pocketLoot")]
- public Dictionary? PocketLoot { get; set; }
+ public Dictionary? PocketLoot { get; set; }
[JsonPropertyName("vestLoot")]
- public Dictionary? VestLoot { get; set; }
+ public Dictionary? VestLoot { get; set; }
[JsonPropertyName("secureLoot")]
- public Dictionary? SecureLoot { get; set; }
+ public Dictionary? SecureLoot { get; set; }
[JsonPropertyName("combinedPoolLoot")]
- public Dictionary? CombinedPoolLoot { get; set; }
+ public Dictionary? CombinedPoolLoot { get; set; }
[JsonPropertyName("specialItems")]
- public Dictionary? SpecialItems { get; set; }
+ public Dictionary? SpecialItems { get; set; }
[JsonPropertyName("healingItems")]
- public Dictionary? HealingItems { get; set; }
+ public Dictionary? HealingItems { get; set; }
[JsonPropertyName("drugItems")]
- public Dictionary? DrugItems { get; set; }
+ public Dictionary? DrugItems { get; set; }
[JsonPropertyName("foodItems")]
- public Dictionary? FoodItems { get; set; }
+ public Dictionary? FoodItems { get; set; }
[JsonPropertyName("drinkItems")]
- public Dictionary? DrinkItems { get; set; }
+ public Dictionary? DrinkItems { get; set; }
[JsonPropertyName("currencyItems")]
- public Dictionary? CurrencyItems { get; set; }
+ public Dictionary? CurrencyItems { get; set; }
[JsonPropertyName("stimItems")]
- public Dictionary? StimItems { get; set; }
+ public Dictionary? StimItems { get; set; }
[JsonPropertyName("grenadeItems")]
- public Dictionary? GrenadeItems { get; set; }
+ public Dictionary? GrenadeItems { get; set; }
}
public class LootCacheType
diff --git a/Core/Services/BotLootCacheService.cs b/Core/Services/BotLootCacheService.cs
index c114badb..1f1aaa79 100644
--- a/Core/Services/BotLootCacheService.cs
+++ b/Core/Services/BotLootCacheService.cs
@@ -3,6 +3,7 @@ using Core.Generators;
using Core.Helpers;
using Core.Models.Common;
using Core.Models.Eft.Common.Tables;
+using Core.Models.Enums;
using Core.Models.Spt.Bots;
using Core.Utils.Cloners;
using ILogger = Core.Models.Utils.ILogger;
@@ -69,7 +70,294 @@ public class BotLootCacheService
/// db template for bot having its loot generated
protected void AddLootToCache(string botRole, bool isPmc, BotType botJsonTemplate)
{
- throw new NotImplementedException();
+ // Full pool of loot we use to create the various sub-categories with
+ var lootPool = botJsonTemplate.BotInventory.Items;
+
+ // Flatten all individual slot loot pools into one big pool, while filtering out potentially missing templates
+ Dictionary specialLootPool = new();
+ Dictionary backpackLootPool= new();
+ Dictionary pocketLootPool = new();
+ Dictionary vestLootPool = new();
+ Dictionary secureLootTPool = new();
+ Dictionary combinedLootPool = new();
+
+ if (isPmc)
+ {
+ // Replace lootPool from bot json with our own generated list for PMCs
+ lootPool.Backpack = _cloner.Clone(_pmcLootGenerator.GeneratePMCBackpackLootPool(botRole));
+ lootPool.Pockets = _cloner.Clone(_pmcLootGenerator.GeneratePMCPocketLootPool(botRole));
+ lootPool.TacticalVest = _cloner.Clone(_pmcLootGenerator.GeneratePMCVestLootPool(botRole));
+ }
+
+ // Backpack/Pockets etc
+ var poolsToProcess =
+ new Dictionary>
+ {
+ { "Backpack", lootPool.Backpack },
+ { "Pockets", lootPool.Pockets },
+ { "SecuredContainer", lootPool.SecuredContainer },
+ { "SpecialLoot", lootPool.SpecialLoot },
+ { "TacticalVest", lootPool.TacticalVest }
+ };
+
+
+ foreach (var kvp in poolsToProcess)
+ {
+ // No items to add, skip
+ if (kvp.Value.Count == 0)
+ {
+ continue;
+ }
+
+ // Sort loot pool into separate buckets
+ switch (kvp.Key)
+ {
+ case "specialloot":
+ AddItemsToPool(specialLootPool, kvp.Value);
+ break;
+ case "pockets":
+ AddItemsToPool(pocketLootPool, kvp.Value);
+ break;
+ case "tacticalvest":
+ AddItemsToPool(vestLootPool, kvp.Value);
+ break;
+ case "securedcontainer":
+ AddItemsToPool(secureLootTPool, kvp.Value);
+ break;
+ case "backpack":
+ AddItemsToPool(backpackLootPool, kvp.Value);
+ break;
+ default:
+ _logger.Warning($"How did you get here {kvp.Key}");
+ break;
+ }
+
+ // Add all items (if any) to combined pool (excluding secure)
+ if (kvp.Value.Count > 0 && kvp.Key.ToLower() != "securedcontainer")
+ {
+ AddItemsToPool(combinedLootPool, kvp.Value);
+ }
+ }
+
+ // Assign whitelisted special items to bot if any exist
+ var specialLootItems =
+ botJsonTemplate.BotGeneration.Items.SpecialItems.Whitelist.Count > 0
+ ? botJsonTemplate.BotGeneration.Items.SpecialItems.Whitelist
+ : new Dictionary();
+
+ // no whitelist, find and assign from combined item pool
+ if (!specialLootItems.Any())
+ {
+ // key = tpl, value = weight
+ foreach (var itemKvP in specialLootPool) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (!(IsBulletOrGrenade(itemTemplate.Properties) || IsMagazine(itemTemplate.Properties)))
+ {
+ specialLootItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted healing items to bot if any exist
+ var healingItems =
+ botJsonTemplate.BotGeneration.Items.Healing.Whitelist.Count > 0
+ ? botJsonTemplate.BotGeneration.Items.Healing.Whitelist
+ : new Dictionary();
+
+ // No whitelist, find and assign from combined item pool
+ if (!healingItems.Any())
+ {
+ // key = tpl, value = weight
+ foreach (var itemKvP in combinedLootPool) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (
+ IsMedicalItem(itemTemplate.Properties) &&
+ itemTemplate.Parent != BaseClasses.STIMULATOR &&
+ itemTemplate.Parent != BaseClasses.DRUGS
+ )
+ {
+ healingItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted drugs to bot if any exist
+ var drugItems = botJsonTemplate.BotGeneration.Items.Drugs.Whitelist ?? new Dictionary();
+ // no drugs whitelist, find and assign from combined item pool
+ if (!drugItems.Any())
+ {
+ foreach (var itemKvP in (combinedLootPool)) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (IsMedicalItem(itemTemplate.Properties) && itemTemplate.Parent == BaseClasses.DRUGS)
+ {
+ drugItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted food to bot if any exist
+ var foodItems = botJsonTemplate.BotGeneration.Items.Food.Whitelist ?? new Dictionary();
+ // No food whitelist, find and assign from combined item pool
+ if (!foodItems.Any())
+ {
+ foreach (var itemKvP in (combinedLootPool)) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (_itemHelper.IsOfBaseclass(itemTemplate.Id, BaseClasses.FOOD))
+ {
+ foodItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted drink to bot if any exist
+ var drinkItems = botJsonTemplate.BotGeneration.Items.Food.Whitelist ?? new Dictionary();
+ // No drink whitelist, find and assign from combined item pool
+ if (!drinkItems.Any())
+ {
+ foreach (var itemKvP in combinedLootPool) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (_itemHelper.IsOfBaseclass(itemTemplate.Id, BaseClasses.DRINK))
+ {
+ drinkItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted currency to bot if any exist
+ var currencyItems = botJsonTemplate.BotGeneration.Items.Currency.Whitelist ?? new Dictionary();
+ // No currency whitelist, find and assign from combined item pool
+ if (!currencyItems.Any())
+ {
+ foreach (var itemKvP in combinedLootPool) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (_itemHelper.IsOfBaseclass(itemTemplate.Id, BaseClasses.MONEY))
+ {
+ currencyItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted stims to bot if any exist
+ var stimItems = botJsonTemplate.BotGeneration.Items.Stims.Whitelist ?? new Dictionary();
+ // No whitelist, find and assign from combined item pool
+ if (!stimItems.Any())
+ {
+ foreach (var itemKvP in combinedLootPool) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (IsMedicalItem(itemTemplate.Properties) && itemTemplate.Parent == BaseClasses.STIMULATOR)
+ {
+ stimItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Assign whitelisted grenades to bot if any exist
+ var grenadeItems = botJsonTemplate.BotGeneration.Items.Grenades.Whitelist ?? new Dictionary();
+ // no whitelist, find and assign from combined item pool
+ if (!grenadeItems.Any())
+ {
+ foreach (var itemKvP in combinedLootPool) {
+ var itemTemplate = _itemHelper.GetItem(itemKvP.Key).Value;
+ if (IsGrenade(itemTemplate.Properties))
+ {
+ grenadeItems[itemKvP.Key] = itemKvP.Value;
+ }
+ }
+ }
+
+ // Get backpack loot (excluding magazines, bullets, grenades, drink, food and healing/stim items)
+ var filteredBackpackItems = new Dictionary();
+ foreach (var itemKvP in backpackLootPool) {
+ var itemResult = _itemHelper.GetItem(itemKvP.Key);
+ if (itemResult.Value is null)
+ {
+ continue;
+ }
+ var itemTemplate = itemResult.Value;
+ if (
+ IsBulletOrGrenade(itemTemplate.Properties) ||
+ IsMagazine(itemTemplate.Properties) ||
+ IsMedicalItem(itemTemplate.Properties) ||
+ IsGrenade(itemTemplate.Properties) ||
+ IsFood(itemTemplate.Id) ||
+ IsDrink(itemTemplate.Id) ||
+ IsCurrency(itemTemplate.Id)
+ )
+ {
+ // Is type we don't want as backpack loot, skip
+ continue;
+ }
+
+ filteredBackpackItems[itemKvP.Key] = itemKvP.Value;
+ }
+
+ // Get pocket loot (excluding magazines, bullets, grenades, drink, food medical and healing/stim items)
+ var filteredPocketItems = new Dictionary();
+ foreach (var itemKvP in pocketLootPool) {
+ var itemResult = _itemHelper.GetItem(itemKvP.Key);
+ if (itemResult.Value is null)
+ {
+ continue;
+ }
+ var itemTemplate = itemResult.Value;
+ if (
+ IsBulletOrGrenade(itemTemplate.Properties) ||
+ IsMagazine(itemTemplate.Properties) ||
+ IsMedicalItem(itemTemplate.Properties) ||
+ IsGrenade(itemTemplate.Properties) ||
+ IsFood(itemTemplate.Id) ||
+ IsDrink(itemTemplate.Id) ||
+ IsCurrency(itemTemplate.Id) ||
+ itemTemplate.Properties.Height is null || // lacks height
+ itemTemplate.Properties.Width is null // lacks width
+ ) {
+ continue;
+ }
+
+ filteredPocketItems[itemKvP.Key] = itemKvP.Value;
+ }
+
+ // Get vest loot (excluding magazines, bullets, grenades, medical and healing/stim items)
+ var filteredVestItems = new Dictionary();
+ foreach (var itemKvP in vestLootPool) {
+ var itemResult = _itemHelper.GetItem(itemKvP.Key);
+ if (itemResult.Value is null)
+ {
+ continue;
+ }
+
+ var itemTemplate = itemResult.Value;
+ if (
+ IsBulletOrGrenade(itemTemplate.Properties) ||
+ IsMagazine(itemTemplate.Properties) ||
+ IsMedicalItem(itemTemplate.Properties) ||
+ IsGrenade(itemTemplate.Properties) ||
+ IsFood(itemTemplate.Id) ||
+ IsDrink(itemTemplate.Id) ||
+ IsCurrency(itemTemplate.Id)
+ )
+ {
+ continue;
+ }
+
+ filteredVestItems[itemKvP.Key] = itemKvP.Value;
+ }
+
+ var cacheForRole = _lootCache[botRole];
+
+ cacheForRole.HealingItems = healingItems;
+ cacheForRole.DrugItems = drugItems;
+ cacheForRole.FoodItems = foodItems;
+ cacheForRole.DrinkItems = drinkItems;
+ cacheForRole.CurrencyItems = currencyItems;
+ cacheForRole.StimItems = stimItems;
+ cacheForRole.GrenadeItems = grenadeItems;
+
+ cacheForRole.SpecialItems = specialLootItems;
+ cacheForRole.BackpackLoot = filteredBackpackItems;
+ cacheForRole.PocketLoot = filteredPocketItems;
+ cacheForRole.VestLoot = filteredVestItems;
+ cacheForRole.SecureLoot = secureLootTPool;
}
///
@@ -82,7 +370,7 @@ public class BotLootCacheService
throw new NotImplementedException();
}
- protected void AddItemsToPool(Dictionary poolToAddTo, Dictionary poolOfItemsToAdd)
+ protected void AddItemsToPool(Dictionary poolToAddTo, Dictionary poolOfItemsToAdd)
{
throw new NotImplementedException();
}