Partially implemented BotLootGenerator

This commit is contained in:
Chomp
2025-01-15 12:06:41 +00:00
parent f9f108448a
commit f2c03d9e59
6 changed files with 351 additions and 21 deletions
+2 -2
View File
@@ -435,8 +435,8 @@ public class BotGenerator
{
// Adjust pocket loot weights to allow for 5 or 6 items
var pocketWeights = botJsonTemplate.BotGeneration.Items["pocketLoot"].Weights;
pocketWeights["5"] = 1;
pocketWeights["6"] = 1;
pocketWeights[5] = 1;
pocketWeights[6] = 1;
}
/// <summary>
+333 -8
View File
@@ -1,19 +1,74 @@
using Core.Annotations;
using Core.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Models.Spt.Bots;
using Core.Models.Spt.Config;
using Core.Utils;
using Core.Helpers;
using Core.Services;
using Core.Servers;
using Core.Utils.Cloners;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Generators;
[Injectable]
public class BotLootGenerator
{
private readonly ILogger _logger;
private readonly HashUtil _hashUtil;
private readonly RandomUtil _randomUtil;
private readonly ItemHelper _itemHelper;
private readonly InventoryHelper _inventoryHelper;
private readonly DatabaseService _databaseService;
private readonly HandbookHelper _handbookHelper;
private readonly BotGeneratorHelper _botGeneratorHelper;
private readonly BotWeaponGenerator _botWeaponGenerator;
private readonly WeightedRandomHelper _weightedRandomHelper;
private readonly BotHelper _botHelper;
private readonly BotLootCacheService _botLootCacheService;
private readonly LocalisationService _localisationService;
private readonly ConfigServer _configServer;
private readonly ICloner _cloner;
private BotConfig _botConfig;
private PmcConfig _pmcConfig;
public BotLootGenerator()
public BotLootGenerator(
ILogger logger,
HashUtil hashUtil,
RandomUtil randomUtil,
ItemHelper itemHelper,
InventoryHelper inventoryHelper,
DatabaseService databaseService,
HandbookHelper handbookHelper,
BotGeneratorHelper botGeneratorHelper,
BotWeaponGenerator botWeaponGenerator,
WeightedRandomHelper weightedRandomHelper,
BotHelper botHelper,
BotLootCacheService botLootCacheService,
LocalisationService localisationService,
ConfigServer configServer,
ICloner cloner)
{
_logger = logger;
_hashUtil = hashUtil;
_randomUtil = randomUtil;
_itemHelper = itemHelper;
_inventoryHelper = inventoryHelper;
_databaseService = databaseService;
_handbookHelper = handbookHelper;
_botGeneratorHelper = botGeneratorHelper;
_botWeaponGenerator = botWeaponGenerator;
_weightedRandomHelper = weightedRandomHelper;
_botHelper = botHelper;
_botLootCacheService = botLootCacheService;
_localisationService = localisationService;
_configServer = configServer;
_cloner = cloner;
_botConfig = _configServer.GetConfig<BotConfig>();
_pmcConfig = _configServer.GetConfig<PmcConfig>();
}
/// <summary>
@@ -38,7 +93,264 @@ public class BotLootGenerator
/// <param name="botLevel">Level of bot</param>
public void GenerateLoot(string sessionId, BotType botJsonTemplate, bool isPmc, string botRole, BotBaseInventory botInventory, int botLevel)
{
throw new NotImplementedException();
// Limits on item types to be added as loot
var itemCounts = botJsonTemplate.BotGeneration.Items;
if (
itemCounts["backpackLoot"].Weights is null ||
itemCounts["pocketLoot"].Weights is null ||
itemCounts["vestLoot"].Weights is null ||
itemCounts["specialItems"].Weights is null ||
itemCounts["healing"].Weights is null ||
itemCounts["drugs"].Weights is null ||
itemCounts["food"].Weights is null ||
itemCounts["drink"].Weights is null ||
itemCounts["currency"].Weights is null ||
itemCounts["stims"].Weights is null ||
itemCounts["grenades"].Weights is null
)
{
_logger.Warning(_localisationService.GetText("bot-unable_to_generate_bot_loot", botRole));
return;
}
var backpackLootCount = _weightedRandomHelper.GetWeightedValue(itemCounts["backpackLoot"].Weights);
var pocketLootCount = _weightedRandomHelper.GetWeightedValue(itemCounts["pocketLoot"].Weights);
var vestLootCount = _weightedRandomHelper.GetWeightedValue(itemCounts["vestLoot"].Weights);
var specialLootItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["specialItems"].Weights);
var healingItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["healing"].Weights);
var drugItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["drugs"].Weights);
var foodItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["food"].Weights);
var drinkItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["drink"].Weights);
var currencyItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["currency"].Weights);
var stimItemCount = _weightedRandomHelper.GetWeightedValue(itemCounts["stims"].Weights);
var grenadeCount = _weightedRandomHelper.GetWeightedValue(itemCounts["grenades"].Weights);
// If bot has been flagged as not having loot, set below counts to 0
if (_botConfig.DisableLootOnBotTypes.Contains(botRole.ToLower()))
{
backpackLootCount = 0;
pocketLootCount = 0;
vestLootCount = 0;
currencyItemCount = 0;
}
// Forced pmc healing loot into secure container
if (isPmc && _pmcConfig.ForceHealingItemsIntoSecure)
{
AddForcedMedicalItemsToPmcSecure(botInventory, botRole);
}
var botItemLimits = GetItemSpawnLimitsForBot(botRole);
var containersBotHasAvailable = GetAvailableContainersBotCanStoreItemsIn(botInventory);
// This set is passed as a reference to fill up the containers that are already full, this alleviates
// generation of the bots by avoiding checking the slots of containers we already know are full
var containersIdFull = new List<string>();
// Special items
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Special, botJsonTemplate),
containersBotHasAvailable,
specialLootItemCount,
botInventory,
botRole,
botItemLimits,
containersIdFull);
// Healing items / Meds
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.HealingItems, botJsonTemplate),
containersBotHasAvailable,
healingItemCount,
botInventory,
botRole,
null,
containersIdFull,
0,
isPmc);
// Drugs
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrugItems, botJsonTemplate),
containersBotHasAvailable,
drugItemCount,
botInventory,
botRole,
null,
containersIdFull,
0,
isPmc);
// Food
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.FoodItems, botJsonTemplate),
containersBotHasAvailable,
foodItemCount,
botInventory,
botRole,
null,
containersIdFull,
0,
isPmc);
// Drink
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrinkItems, botJsonTemplate),
containersBotHasAvailable,
drinkItemCount,
botInventory,
botRole,
null,
containersIdFull,
0,
isPmc);
// Currency
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.CurrencyItems, botJsonTemplate),
containersBotHasAvailable,
currencyItemCount,
botInventory,
botRole,
null,
containersIdFull,
0,
isPmc);
// Stims
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.StimItems, botJsonTemplate),
containersBotHasAvailable,
stimItemCount,
botInventory,
botRole,
botItemLimits,
containersIdFull,
0,
isPmc);
// Grenades
AddLootFromPool(
_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,
botInventory,
botRole,
null,
containersIdFull,
0,
isPmc);
var itemPriceLimits = GetSingleItemLootPriceLimits(botLevel, isPmc);
// Backpack - generate loot if they have one
if (containersBotHasAvailable.Contains(EquipmentSlots.Backpack))
{
// Add randomly generated weapon to PMC backpacks
if (isPmc && _randomUtil.GetChance100(_pmcConfig.LooseWeaponInBackpackChancePercent))
{
AddLooseWeaponsToInventorySlot(
sessionId,
botInventory,
EquipmentSlots.Backpack,
botJsonTemplate.BotInventory,
botJsonTemplate.BotChances.WeaponModsChances,
botRole,
isPmc,
botLevel,
containersIdFull);
}
var backpackLootRoubleTotal = GetBackpackRoubleTotalByLevel(botLevel, isPmc);
AddLootFromPool(
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.Backpack,
botJsonTemplate,
itemPriceLimits?.Backpack),
[EquipmentSlots.Backpack],
backpackLootCount,
botInventory,
botRole,
botItemLimits,
containersIdFull,
backpackLootRoubleTotal,
isPmc);
}
// TacticalVest - generate loot if they have one
if (containersBotHasAvailable.Contains(EquipmentSlots.TacticalVest))
{
// Vest
AddLootFromPool(
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.Vest,
botJsonTemplate,
itemPriceLimits?.Vest),
[EquipmentSlots.TacticalVest],
vestLootCount,
botInventory,
botRole,
botItemLimits,
containersIdFull,
_pmcConfig.MaxVestLootTotalRub,
isPmc);
}
// Pockets
AddLootFromPool(
_botLootCacheService.GetLootFromCache(
botRole,
isPmc,
LootCacheType.Pocket,
botJsonTemplate,
itemPriceLimits?.Pocket),
[EquipmentSlots.Pockets],
pocketLootCount,
botInventory,
botRole,
botItemLimits,
containersIdFull,
_pmcConfig.MaxPocketLootTotalRub,
isPmc);
// Secure
// only add if not a pmc or is pmc and flag is true
if (!isPmc || (isPmc && _pmcConfig.AddSecureContainerLootFromBotConfig))
{
AddLootFromPool(
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Secure, botJsonTemplate),
[EquipmentSlots.SecuredContainer],
50,
botInventory,
botRole,
null,
containersIdFull,
- 1,
isPmc);
}
}
private MinMaxLootItemValue? GetSingleItemLootPriceLimits(int botLevel, bool isPmc)
{
// TODO - extend to other bot types
if (isPmc)
{
var matchingValue = _pmcConfig.LootItemLimitsRub.FirstOrDefault(
(minMaxValue) => botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max);
return matchingValue;
}
return null;
}
/// <summary>
@@ -87,9 +399,16 @@ 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>
/// <exception cref="NotImplementedException"></exception>
public void AddLootFromPool(Dictionary<string, int> pool, List<string> equipmentSlots, int totalItemCount,
public void AddLootFromPool(
Dictionary<string, int> pool,
List<EquipmentSlots> equipmentSlots,
int totalItemCount,
BotBaseInventory inventoryToAddItemsTo, // TODO: type for containersIdFull was Set<string>
string botRole, ItemSpawnLimitSettings? itemSpawnLimits, List<string> containersIdFull, int totalValueLimitRub = 0, bool isPmc = false)
string botRole,
ItemSpawnLimitSettings itemSpawnLimits,
List<string> containersIdFull,
int totalValueLimitRub = 0,
bool isPmc = false)
{
throw new NotImplementedException();
}
@@ -129,9 +448,15 @@ public class BotLootGenerator
/// <param name="isPmc">are we generating for a pmc</param>
/// <param name="botLevel"></param>
/// <param name="containersIdFull"></param>
public void AddLooseWeaponsToInventorySlot(string sessionId, BotBaseInventory botInventory, string equipmentSlot,
BotBaseInventory templateInventory, // TODO: type for containersIdFull was Set<string>
Dictionary<string, double> modsChances, string botRole, bool isPmc, int botLevel, List<string>? containersIdFull)
public void AddLooseWeaponsToInventorySlot(string sessionId,
BotBaseInventory botInventory,
EquipmentSlots equipmentSlot,
BotTypeInventory templateInventory,
Dictionary<string, double> modsChances,
string botRole,
bool isPmc,
int botLevel,
List<string>? containersIdFull) // TODO: type for containersIdFull was Set<string>
{
throw new NotImplementedException();
}
+1 -1
View File
@@ -303,7 +303,7 @@ public class GenerationData
{
/** key: number of items, value: weighting */
[JsonPropertyName("weights")]
public Dictionary<string, double>? Weights { get; set; }
public Dictionary<int, int>? Weights { get; set; }
/** Array of item tpls */
[JsonPropertyName("whitelist")]
+2 -2
View File
@@ -44,7 +44,7 @@ public class BotLootCache
public Dictionary<string, int>? GrenadeItems { get; set; }
}
public static class LootCacheType
public class LootCacheType
{
public const string Special = "Special";
public const string Backpack = "Backpack";
@@ -59,4 +59,4 @@ public static class LootCacheType
public const string FoodItems = "FoodItems";
public const string DrinkItems = "DrinkItems";
public const string CurrencyItems = "CurrencyItems";
}
}
+10 -7
View File
@@ -1,4 +1,4 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
using Core.Models.Common;
using Core.Models.Eft.Common;
using Core.Models.Enums;
@@ -79,10 +79,10 @@ public class PmcConfig : BaseConfig
public List<MinMaxLootValue> MaxBackpackLootTotalRub { get; set; }
[JsonPropertyName("maxPocketLootTotalRub")]
public double MaxPocketLootTotalRub { get; set; }
public int MaxPocketLootTotalRub { get; set; }
[JsonPropertyName("maxVestLootTotalRub")]
public double MaxVestLootTotalRub { get; set; }
public int MaxVestLootTotalRub { get; set; }
/** Percentage chance a bot from a wave is converted into a PMC, first key = map, second key = bot wildspawn type (assault/exusec), value: min+max chance to be converted */
[JsonPropertyName("convertIntoPmcChance")]
@@ -117,7 +117,7 @@ public class PmcConfig : BaseConfig
public int? AddPrefixToSameNamePMCAsPlayerChance { get; set; }
[JsonPropertyName("lootItemLimitsRub")]
public List<MinMaxLootValue>? LootItemLimitsRub { get; set; }
public List<MinMaxLootItemValue>? LootItemLimitsRub { get; set; }
}
public class HostilitySettings
@@ -171,13 +171,16 @@ public class MinMaxLootValue : MinMax
{
[JsonPropertyName("value")]
public double Value { get; set; }
}
public class MinMaxLootItemValue : MinMax
{
[JsonPropertyName("backpack")]
public MinMax Backpack { get; set; }
[JsonPropertyName("pocket")]
public MinMax Pocket { get; set; }
[JsonPropertyName("vest")]
public MinMax Vest { get; set; }
}
+3 -1
View File
@@ -1,6 +1,7 @@
using Core.Annotations;
using Core.Generators;
using Core.Helpers;
using Core.Models.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Spt.Bots;
using Core.Utils.Cloners;
@@ -54,7 +55,8 @@ public class BotLootCacheService
string botRole,
bool isPmc,
string lootType,
BotType botJsonTemplate)
BotType botJsonTemplate,
MinMax? itemPriceMinMax = null)
{
throw new NotImplementedException();
}