diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs index 5b77eba6..38d0238b 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs @@ -171,8 +171,12 @@ public class BotGenerator( /// BotBase object protected BotBase GenerateBot(MongoId sessionId, BotBase bot, BotType botJsonTemplate, BotGenerationDetails botGenerationDetails) { - var botRoleLowercase = botGenerationDetails.Role.ToLowerInvariant(); - var botLevel = botLevelGenerator.GenerateBotLevel(botJsonTemplate.BotExperience.Level, botGenerationDetails, bot); + botGenerationDetails.RoleLowercase = botGenerationDetails.Role.ToLowerInvariant(); + + var botLevelDetails = botLevelGenerator.GenerateBotLevel(botJsonTemplate.BotExperience.Level, botGenerationDetails, bot); + + // Assign value for later use + botGenerationDetails.BotLevel = botLevelDetails.Level.GetValueOrDefault(); // Generate Id/AId for bot AddIdsToBot(bot, botGenerationDetails); @@ -180,13 +184,12 @@ public class BotGenerator( // Only filter bot equipment, never players if (!botGenerationDetails.IsPlayerScav) { - botEquipmentFilterService.FilterBotEquipment(sessionId, botJsonTemplate, botLevel.Level.Value, botGenerationDetails); + botEquipmentFilterService.FilterBotEquipment(sessionId, botJsonTemplate, botGenerationDetails); } bot.Info.Nickname = botNameService.GenerateUniqueBotNickname( botJsonTemplate, botGenerationDetails, - botRoleLowercase, BotConfig.BotRolesThatMustHaveUniqueName ); @@ -194,7 +197,7 @@ public class BotGenerator( bot.Info.LowerNickname = botGenerationDetails.IsPmc ? bot.Info.Nickname.ToLowerInvariant() : string.Empty; // Only run when generating a 'fake' playerscav, not actual player scav - if (!botGenerationDetails.IsPlayerScav && ShouldSimulatePlayerScav(botRoleLowercase)) + if (!botGenerationDetails.IsPlayerScav && ShouldSimulatePlayerScav(botGenerationDetails.RoleLowercase)) { botNameService.AddRandomPmcNameToBotMainProfileNicknameProperty(bot); SetRandomisedGameVersionAndCategory(bot.Info); @@ -217,8 +220,8 @@ public class BotGenerator( bot.Hideout = null; } - bot.Info.Experience = botLevel.Exp; - bot.Info.Level = botLevel.Level; + bot.Info.Experience = botLevelDetails.Exp; + bot.Info.Level = botLevelDetails.Level; bot.Info.Settings.Experience = GetExperienceRewardForKillByDifficulty( botJsonTemplate.BotExperience.Reward, botGenerationDetails.BotDifficulty, @@ -248,22 +251,16 @@ public class BotGenerator( { AddAdditionalPocketLootWeightsForUnheardBot(botJsonTemplate); } + + botGenerationDetails.GameVersion = bot.Info.GameVersion; } // Add drip SetBotAppearance(bot, botJsonTemplate.BotAppearance, botGenerationDetails); - bot.Inventory = botInventoryGenerator.GenerateInventory( - bot.Id.Value, - sessionId, - botJsonTemplate, - botRoleLowercase, - botGenerationDetails, - bot.Info.Level.Value, - bot.Info.GameVersion - ); + bot.Inventory = botInventoryGenerator.GenerateInventory(bot.Id.Value, sessionId, botJsonTemplate, botGenerationDetails); - if (BotConfig.BotRolesWithDogTags.Contains(botRoleLowercase)) + if (BotConfig.BotRolesWithDogTags.Contains(botGenerationDetails.RoleLowercase)) { AddDogtagToBot(bot); } diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs index 49727c7a..cb22c20d 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs @@ -70,45 +70,26 @@ public class BotInventoryGenerator( /// Bots unique identifier /// Session id /// Base json db file for the bot having its loot generated - /// Role bot has (assault/pmcBot) /// Details related to generating a bot - /// Level of bot being generated - /// Game version for bot, only really applies for PMCs /// PmcInventory object with equipment/weapons/loot public BotBaseInventory GenerateInventory( MongoId botId, MongoId sessionId, BotType botJsonTemplate, - string botRole, - BotGenerationDetails botGenerationDetails, - int botLevel, - string chosenGameVersion + BotGenerationDetails botGenerationDetails ) { var templateInventory = botJsonTemplate.BotInventory; var wornItemChances = botJsonTemplate.BotChances; var itemGenerationLimitsMinMax = botJsonTemplate.BotGeneration; - var isPmc = botGenerationDetails.IsPmc; - // Generate base inventory with no items var botInventory = GenerateInventoryBase(); // Get generated raid details bot will be spawned in var raidConfig = profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration; - GenerateAndAddEquipmentToBot( - botId, - sessionId, - templateInventory, - wornItemChances, - botRole, - botInventory, - botLevel, - chosenGameVersion, - isPmc, - raidConfig - ); + GenerateAndAddEquipmentToBot(botId, sessionId, templateInventory, wornItemChances, botInventory, botGenerationDetails, raidConfig); // Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed GenerateAndAddWeaponsToBot( @@ -117,14 +98,12 @@ public class BotInventoryGenerator( wornItemChances, sessionId, botInventory, - botRole, - isPmc, - itemGenerationLimitsMinMax, - botLevel + botGenerationDetails, + itemGenerationLimitsMinMax ); // Pick loot and add to bots containers (rig/backpack/pockets/secure) - botLootGenerator.GenerateLoot(botId, sessionId, botJsonTemplate, botGenerationDetails, isPmc, botRole, botInventory, botLevel); + botLootGenerator.GenerateLoot(botId, sessionId, botJsonTemplate, botGenerationDetails, botInventory); // Inventory cache isn't needed, clear to save memory if (botGenerationDetails.ClearBotContainerCacheAfterGeneration) @@ -178,32 +157,31 @@ public class BotInventoryGenerator( /// Session id /// bot/x.json data from db /// Chances items will be added to bot - /// Role bot has (assault/pmcBot) /// Inventory to add equipment to - /// Level of bot - /// Game version for bot, only really applies for PMCs - /// Is the generated bot a PMC + /// Details related to generating a bot /// RadiConfig public void GenerateAndAddEquipmentToBot( MongoId botId, MongoId sessionId, BotTypeInventory templateInventory, Chances wornItemChances, - string botRole, BotBaseInventory botInventory, - int botLevel, - string chosenGameVersion, - bool isPmc, + BotGenerationDetails botGenerationDetails, GetRaidConfigurationRequestData? raidConfig ) { - if (!BotConfig.Equipment.TryGetValue(botGeneratorHelper.GetBotEquipmentRole(botRole), out var botEquipConfig)) + if ( + !BotConfig.Equipment.TryGetValue( + botGeneratorHelper.GetBotEquipmentRole(botGenerationDetails.RoleLowercase), + out var botEquipConfig + ) + ) { - logger.Error($"Bot Equipment generation failed, unable to find equipment filters for: {botRole}"); + logger.Error($"Bot Equipment generation failed, unable to find equipment filters for: {botGenerationDetails.RoleLowercase}"); return; } - var randomistionDetails = botHelper.GetBotRandomizationDetails(botLevel, botEquipConfig); + var randomistionDetails = botHelper.GetBotRandomizationDetails(botGenerationDetails.BotLevel, botEquipConfig); // Apply nighttime changes if its nighttime + there's changes to make if ( @@ -224,13 +202,14 @@ public class BotInventoryGenerator( } // Is PMC + generating armband + armband forcing is enabled - if (PMCConfig.ForceArmband.Enabled && isPmc) + if (PMCConfig.ForceArmband.Enabled && botGenerationDetails.IsPmc) { // Replace armband pool with single tpl from config if (templateInventory.Equipment.TryGetValue(EquipmentSlots.ArmBand, out var armbands)) { // Get tpl based on pmc side - var armbandTpl = botRole == "pmcusec" ? PMCConfig.ForceArmband.Usec : PMCConfig.ForceArmband.Bear; + var armbandTpl = + botGenerationDetails.RoleLowercase == "pmcusec" ? PMCConfig.ForceArmband.Usec : PMCConfig.ForceArmband.Bear; armbands.Clear(); armbands.Add(armbandTpl, 1); @@ -242,7 +221,7 @@ public class BotInventoryGenerator( // Get profile of player generating bots, we use their level later on var pmcProfile = profileHelper.GetPmcProfile(sessionId); - var botEquipmentRole = botGeneratorHelper.GetBotEquipmentRole(botRole); + var botEquipmentRole = botGeneratorHelper.GetBotEquipmentRole(botGenerationDetails.RoleLowercase); // Iterate over all equipment slots of bot, do it in specific order to reduce conflicts // e.g. ArmorVest should be generated after TacticalVest @@ -266,8 +245,8 @@ public class BotInventoryGenerator( SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -285,13 +264,17 @@ public class BotInventoryGenerator( BotId = botId, RootEquipmentSlot = EquipmentSlots.Pockets, // Unheard profiles have unique sized pockets - RootEquipmentPool = GetPocketPoolByGameEdition(chosenGameVersion, templateInventory, isPmc), + RootEquipmentPool = GetPocketPoolByGameEdition( + botGenerationDetails.GameVersion, + templateInventory, + botGenerationDetails.IsPmc + ), ModPool = templateInventory.Mods, SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -312,8 +295,8 @@ public class BotInventoryGenerator( SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -333,8 +316,8 @@ public class BotInventoryGenerator( SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -354,8 +337,8 @@ public class BotInventoryGenerator( SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -375,8 +358,8 @@ public class BotInventoryGenerator( SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -390,14 +373,14 @@ public class BotInventoryGenerator( if (botEquipConfig.ForceOnlyArmoredRigWhenNoArmor.GetValueOrDefault(false) && !hasArmorVest) // Filter rigs down to only those with armor { - FilterRigsToThoseWithProtection(templateInventory.Equipment, botRole); + FilterRigsToThoseWithProtection(templateInventory.Equipment, botGenerationDetails.RoleLowercase); } // Optimisation - Remove armored rigs from pool if (hasArmorVest) // Filter rigs down to only those with armor { - FilterRigsToThoseWithoutProtection(templateInventory.Equipment, botRole); + FilterRigsToThoseWithoutProtection(templateInventory.Equipment, botGenerationDetails.RoleLowercase); } // Bot is flagged as always needing a vest @@ -416,8 +399,8 @@ public class BotInventoryGenerator( SpawnChances = wornItemChances, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, Inventory = botInventory, @@ -688,20 +671,16 @@ public class BotInventoryGenerator( /// Chances bot can have equipment equipped /// Session id /// Inventory to add weapons to - /// assault/pmcBot/bossTagilla etc - /// Is the bot being generated as a pmc + /// Details related to generating a bot /// Limits for items the bot can have - /// level of bot having weapon generated public void GenerateAndAddWeaponsToBot( MongoId botId, BotTypeInventory templateInventory, Chances equipmentChances, MongoId sessionId, BotBaseInventory botInventory, - string botRole, - bool isPmc, - Generation itemGenerationLimitsMinMax, - int botLevel + BotGenerationDetails botGenerationDetails, + Generation itemGenerationLimitsMinMax ) { var weaponSlotsToFill = GetDesiredWeaponsForBot(equipmentChances); @@ -717,10 +696,8 @@ public class BotInventoryGenerator( templateInventory, botInventory, equipmentChances, - botRole, - isPmc, - itemGenerationLimitsMinMax, - botLevel + botGenerationDetails, + itemGenerationLimitsMinMax ); } } @@ -759,10 +736,8 @@ public class BotInventoryGenerator( /// bot/x.json data from db /// Inventory to add weapon+mags/ammo to /// Chances bot can have equipment equipped - /// assault/pmcBot/bossTagilla etc - /// Is the bot being generated as a pmc + /// Details related to generating a bot /// - /// public void AddWeaponAndMagazinesToInventory( MongoId botId, MongoId sessionId, @@ -770,21 +745,17 @@ public class BotInventoryGenerator( BotTypeInventory templateInventory, BotBaseInventory botInventory, Chances equipmentChances, - string botRole, - bool isPmc, - Generation itemGenerationWeights, - int botLevel + BotGenerationDetails botGenerationDetails, + Generation itemGenerationWeights ) { var generatedWeapon = botWeaponGenerator.GenerateRandomWeapon( sessionId, weaponSlot.Slot.ToString(), templateInventory, + botGenerationDetails, botInventory.Equipment.Value, - equipmentChances.WeaponModsChances, - botRole, - isPmc, - botLevel + equipmentChances.WeaponModsChances ); botInventory.Items.AddRange(generatedWeapon.Weapon); @@ -794,7 +765,7 @@ public class BotInventoryGenerator( generatedWeapon, itemGenerationWeights.Items.Magazines, botInventory, - botRole + botGenerationDetails.RoleLowercase ); } } diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs index 52290ee0..2601fcff 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs @@ -61,19 +61,13 @@ public class BotLootGenerator( /// Session id /// Clone of Base JSON db file for the bot having its loot generated /// Details relating to generating a bot - /// Will bot be a pmc - /// Role of bot, e.g. assault /// Inventory to add loot to - /// Level of bot public void GenerateLoot( MongoId botId, MongoId sessionId, BotType botJsonTemplate, BotGenerationDetails botGenerationDetails, - bool isPmc, - string botRole, - BotBaseInventory botInventory, - int botLevel + BotBaseInventory botInventory ) { // Limits on item types to be added as loot @@ -93,7 +87,7 @@ public class BotLootGenerator( || itemCounts.Grenades.Weights is null ) { - logger.Warning(serverLocalisationService.GetText("bot-unable_to_generate_bot_loot", botRole)); + logger.Warning(serverLocalisationService.GetText("bot-unable_to_generate_bot_loot", botGenerationDetails.RoleLowercase)); return; } @@ -110,7 +104,7 @@ public class BotLootGenerator( 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.ToLowerInvariant())) + if (BotConfig.DisableLootOnBotTypes.Contains(botGenerationDetails.RoleLowercase)) { backpackLootCount = 0; pocketLootCount = 0; @@ -119,156 +113,202 @@ public class BotLootGenerator( } // Forced pmc healing loot into secure container - if (isPmc && PMCConfig.ForceHealingItemsIntoSecure) + if (botGenerationDetails.IsPmc && PMCConfig.ForceHealingItemsIntoSecure) { - AddForcedMedicalItemsToPmcSecure(botInventory, botRole, botId); + AddForcedMedicalItemsToPmcSecure(botInventory, botGenerationDetails.RoleLowercase, botId); } - var botItemLimits = GetItemSpawnLimitsForBot(botRole); + var botItemLimits = GetItemSpawnLimitsForBot(botGenerationDetails.RoleLowercase); var containersBotHasAvailable = GetAvailableContainersBotCanStoreItemsIn(botInventory); // Special items AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Special, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.Special, + botJsonTemplate + ), containersBotHasAvailable, specialLootItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, botItemLimits ); // Healing items / Meds AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.HealingItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.HealingItems, + botJsonTemplate + ), containersBotHasAvailable, healingItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, 0, - isPmc + botGenerationDetails.IsPmc ); // Drugs AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrugItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.DrugItems, + botJsonTemplate + ), containersBotHasAvailable, drugItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, 0, - isPmc + botGenerationDetails.IsPmc ); // Food AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.FoodItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.FoodItems, + botJsonTemplate + ), containersBotHasAvailable, foodItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, 0, - isPmc + botGenerationDetails.IsPmc ); // Drink AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.DrinkItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.DrinkItems, + botJsonTemplate + ), containersBotHasAvailable, drinkItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, 0, - isPmc + botGenerationDetails.IsPmc ); // Currency AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.CurrencyItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.CurrencyItems, + botJsonTemplate + ), containersBotHasAvailable, currencyItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, 0, - isPmc + botGenerationDetails.IsPmc ); // Stims AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.StimItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.StimItems, + botJsonTemplate + ), containersBotHasAvailable, stimItemCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, botItemLimits, 0, - isPmc + botGenerationDetails.IsPmc ); // Grenades AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.GrenadeItems, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.GrenadeItems, + botJsonTemplate + ), [EquipmentSlots.Pockets, EquipmentSlots.TacticalVest], // Can't use containersBotHasEquipped as we don't want grenades added to backpack grenadeCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, 0, - isPmc + botGenerationDetails.IsPmc ); - var itemPriceLimits = GetSingleItemLootPriceLimits(botLevel, isPmc); + var itemPriceLimits = GetSingleItemLootPriceLimits(botGenerationDetails.BotLevel, botGenerationDetails.IsPmc); // Backpack - generate loot if they have one if (containersBotHasAvailable.Contains(EquipmentSlots.Backpack) && backpackLootCount > 0) { // Add randomly generated weapon to PMC backpacks - if (isPmc && randomUtil.GetChance100(PMCConfig.LooseWeaponInBackpackChancePercent)) + if (botGenerationDetails.IsPmc && randomUtil.GetChance100(PMCConfig.LooseWeaponInBackpackChancePercent)) { AddLooseWeaponsToInventorySlot( botId, sessionId, botInventory, EquipmentSlots.Backpack, + botGenerationDetails, botJsonTemplate.BotInventory, - botJsonTemplate.BotChances?.WeaponModsChances, - botRole, - isPmc, - botLevel + botJsonTemplate.BotChances?.WeaponModsChances ); } - var backpackLootRoubleTotal = isPmc - ? PMCConfig.LootSettings.Backpack.GetRoubleValue(botLevel, botGenerationDetails.Location) + var backpackLootRoubleTotal = botGenerationDetails.IsPmc + ? PMCConfig.LootSettings.Backpack.GetRoubleValue(botGenerationDetails.BotLevel, botGenerationDetails.Location) : 0; AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Backpack, botJsonTemplate, itemPriceLimits?.Backpack), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.Backpack, + botJsonTemplate, + itemPriceLimits?.Backpack + ), [EquipmentSlots.Backpack], backpackLootCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, botItemLimits, backpackLootRoubleTotal, - isPmc + botGenerationDetails.IsPmc ); } - var vestLootRoubleTotal = isPmc ? PMCConfig.LootSettings.Vest.GetRoubleValue(botLevel, botGenerationDetails.Location) : 0; + var vestLootRoubleTotal = botGenerationDetails.IsPmc + ? PMCConfig.LootSettings.Vest.GetRoubleValue(botGenerationDetails.BotLevel, botGenerationDetails.Location) + : 0; // TacticalVest - generate loot if they have one if (containersBotHasAvailable.Contains(EquipmentSlots.TacticalVest)) @@ -276,47 +316,66 @@ public class BotLootGenerator( { AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Vest, botJsonTemplate, itemPriceLimits?.Vest), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.Vest, + botJsonTemplate, + itemPriceLimits?.Vest + ), [EquipmentSlots.TacticalVest], vestLootCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, botItemLimits, vestLootRoubleTotal, - isPmc + botGenerationDetails.IsPmc ); } - var pocketLootRoubleTotal = isPmc ? PMCConfig.LootSettings.Pocket.GetRoubleValue(botLevel, botGenerationDetails.Location) : 0; + var pocketLootRoubleTotal = botGenerationDetails.IsPmc + ? PMCConfig.LootSettings.Pocket.GetRoubleValue(botGenerationDetails.BotLevel, botGenerationDetails.Location) + : 0; // Pockets AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Pocket, botJsonTemplate, itemPriceLimits?.Pocket), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.Pocket, + botJsonTemplate, + itemPriceLimits?.Pocket + ), [EquipmentSlots.Pockets], pocketLootCount, botInventory, - botRole, + botGenerationDetails.RoleLowercase, botItemLimits, pocketLootRoubleTotal, - isPmc + botGenerationDetails.IsPmc ); // Secure // only add if not a pmc or is pmc and flag is true - if (!isPmc || (isPmc && PMCConfig.AddSecureContainerLootFromBotConfig)) + if (!botGenerationDetails.IsPmc || (botGenerationDetails.IsPmc && PMCConfig.AddSecureContainerLootFromBotConfig)) { AddLootFromPool( botId, - botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Secure, botJsonTemplate), + botLootCacheService.GetLootFromCache( + botGenerationDetails.RoleLowercase, + botGenerationDetails.IsPmc, + LootCacheType.Secure, + botJsonTemplate + ), [EquipmentSlots.SecuredContainer], 50, botInventory, - botRole, + botGenerationDetails.RoleLowercase, null, -1, - isPmc + botGenerationDetails.IsPmc ); } } @@ -615,21 +674,17 @@ public class BotLootGenerator( /// Session/Player id /// Inventory to add preset to /// Slot to place the preset in (backpack) + /// /// Bots template, assault.json /// Chances for mods to spawn on weapon - /// bots role .e.g. pmcBot - /// are we generating for a pmc - /// Level of bot having loose weapon generated public void AddLooseWeaponsToInventorySlot( MongoId botId, MongoId sessionId, BotBaseInventory botInventory, EquipmentSlots equipmentSlot, + BotGenerationDetails botGenerationDetails, BotTypeInventory? templateInventory, - Dictionary modChances, - string botRole, - bool isPmc, - int botLevel + Dictionary modChances ) { var chosenWeaponType = randomUtil.GetArrayValue( @@ -656,17 +711,17 @@ public class BotLootGenerator( sessionId, chosenWeaponType, templateInventory, + botGenerationDetails, botInventory.Equipment.Value, - modChances, - botRole, - isPmc, - botLevel + modChances ); var weaponRootItem = generatedWeapon.Weapon?.FirstOrDefault(); if (weaponRootItem is null) { - logger.Error($"Generated null loose weapon: {chosenWeaponType} for: {botRole} level: {botLevel}, skipping"); + logger.Error( + $"Generated null loose weapon: {chosenWeaponType} for: {botGenerationDetails.RoleLowercase} level: {botGenerationDetails.BotLevel}, skipping" + ); continue; } diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs index 74cdc4d5..51299a84 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs @@ -54,21 +54,17 @@ public class BotWeaponGenerator( /// Session identifier /// Primary/secondary/holster /// e.g. assault.json - /// + /// Details related to generating a bot + /// Details related to generating a bot /// - /// Role of bot, e.g. assault/followerBully - /// Is weapon generated for a pmc - /// /// GenerateWeaponResult object public GenerateWeaponResult? GenerateRandomWeapon( MongoId sessionId, string equipmentSlot, BotTypeInventory botTemplateInventory, + BotGenerationDetails botGenerationDetails, MongoId weaponParentId, - Dictionary modChances, - string botRole, - bool isPmc, - int botLevel + Dictionary modChances ) { var weaponTpl = PickWeightedWeaponTemplateFromPool(equipmentSlot, botTemplateInventory); @@ -79,9 +75,7 @@ public class BotWeaponGenerator( botTemplateInventory, weaponParentId, modChances, - botRole, - isPmc, - botLevel + botGenerationDetails ); } @@ -111,9 +105,7 @@ public class BotWeaponGenerator( /// e.g. assault.json. /// Parent ID of the weapon being generated. /// Dictionary of item types and % chance weapon will have that mod. - /// e.g. assault/exusec. - /// Is weapon being generated for a PMC. - /// The level of the bot. + /// /// GenerateWeaponResult object. public GenerateWeaponResult? GenerateWeaponByTpl( MongoId sessionId, @@ -122,9 +114,7 @@ public class BotWeaponGenerator( BotTypeInventory botTemplateInventory, MongoId weaponParentId, Dictionary modChances, - string botRole, - bool isPmc, - int botLevel + BotGenerationDetails botGenerationDetails ) { var modPool = botTemplateInventory.Mods; @@ -141,17 +131,24 @@ public class BotWeaponGenerator( // Find ammo to use when filling magazines/chamber if (botTemplateInventory.Ammo is null) { - logger.Error(serverLocalisationService.GetText("bot-no_ammo_found_in_bot_json", botRole)); + logger.Error(serverLocalisationService.GetText("bot-no_ammo_found_in_bot_json", botGenerationDetails.RoleLowercase)); logger.Error(serverLocalisationService.GetText("bot-generation_failed")); } var ammoTpl = GetWeightedCompatibleAmmo(botTemplateInventory.Ammo, weaponItemTemplate); // Create with just base weapon item - var weaponWithModsArray = ConstructWeaponBaseList(weaponTpl, weaponParentId, slotName, weaponItemTemplate, botRole).ToList(); + var weaponWithModsArray = ConstructWeaponBaseList( + weaponTpl, + weaponParentId, + slotName, + weaponItemTemplate, + botGenerationDetails.RoleLowercase + ) + .ToList(); // Chance to add randomised weapon enhancement - if (isPmc && randomUtil.GetChance100(PMCConfig.WeaponHasEnhancementChancePercent)) + if (botGenerationDetails.IsPmc && randomUtil.GetChance100(PMCConfig.WeaponHasEnhancementChancePercent)) // Add buff to weapon root { repairService.AddBuff(RepairConfig.RepairKit.Weapon, weaponWithModsArray[0]); @@ -161,7 +158,7 @@ public class BotWeaponGenerator( if (modPool.ContainsKey(weaponTpl)) { // Role to treat bot as e.g. pmc/scav/boss - var botEquipmentRole = botGeneratorHelper.GetBotEquipmentRole(botRole); + var botEquipmentRole = botGeneratorHelper.GetBotEquipmentRole(botGenerationDetails.RoleLowercase); // Different limits if bot is boss vs scav var modLimits = botWeaponModLimitService.GetWeaponModLimits(botEquipmentRole); @@ -176,8 +173,8 @@ public class BotWeaponGenerator( AmmoTpl = ammoTpl, BotData = new BotData { - Role = botRole, - Level = botLevel, + Role = botGenerationDetails.RoleLowercase, + Level = botGenerationDetails.BotLevel, EquipmentRole = botEquipmentRole, }, ModLimits = modLimits, @@ -188,10 +185,16 @@ public class BotWeaponGenerator( } // Use weapon preset from globals.json if weapon isn't valid - if (!IsWeaponValid(weaponWithModsArray, botRole)) + if (!IsWeaponValid(weaponWithModsArray, botGenerationDetails.RoleLowercase)) // Weapon is bad, fall back to weapons preset { - weaponWithModsArray = GetPresetWeaponMods(weaponTpl, slotName, weaponParentId, weaponItemTemplate, botRole); + weaponWithModsArray = GetPresetWeaponMods( + weaponTpl, + slotName, + weaponParentId, + weaponItemTemplate, + botGenerationDetails.RoleLowercase + ); } var tempList = cloner.Clone(weaponWithModsArray.Where(item => item.SlotId == ModMagazineSlotId)); @@ -708,10 +711,8 @@ public class BotWeaponGenerator( // Try to get cartridges from slots array first, if none found, try Cartridges array var cartridges = - magazineTemplate.Value.Properties.Slots.FirstOrDefault()?.Properties?.Filters.FirstOrDefault()?.Filter ?? magazineTemplate - .Value.Properties.Cartridges.FirstOrDefault() - ?.Properties?.Filters.FirstOrDefault() - ?.Filter; + magazineTemplate.Value.Properties.Slots.FirstOrDefault()?.Properties?.Filters.FirstOrDefault()?.Filter + ?? magazineTemplate.Value.Properties.Cartridges.FirstOrDefault()?.Properties?.Filters.FirstOrDefault()?.Filter; return cartridges ?? []; } diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Bots/BotGenerationDetails.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Bots/BotGenerationDetails.cs index d0321a7c..33b7e008 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Bots/BotGenerationDetails.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Bots/BotGenerationDetails.cs @@ -17,6 +17,12 @@ public record BotGenerationDetails [JsonPropertyName("role")] public string Role { get; set; } + /// + /// assault/pmcBot etc + /// + [JsonPropertyName("BotRoleLowercase")] + public string RoleLowercase { get; set; } + /// /// Side of bot /// @@ -85,4 +91,14 @@ public record BotGenerationDetails /// [JsonPropertyName("clearBotContainerCacheAfterGeneration")] public bool ClearBotContainerCacheAfterGeneration { get; set; } = true; + + /// + /// Level the bot will have once generated + /// + public int BotLevel { get; set; } + + /// + /// Version of the game bot will use - Only apples to PMCs + /// + public string GameVersion { get; set; } } diff --git a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentFilterService.cs b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentFilterService.cs index db7e77a6..c8e9b5e2 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentFilterService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentFilterService.cs @@ -28,22 +28,21 @@ public class BotEquipmentFilterService( /// /// Players id /// bots json data to filter - /// Level of the bot /// details on how to generate a bot - public void FilterBotEquipment(MongoId sessionId, BotType baseBotNode, int botLevel, BotGenerationDetails botGenerationDetails) + public void FilterBotEquipment(MongoId sessionId, BotType baseBotNode, BotGenerationDetails botGenerationDetails) { var pmcProfile = profileHelper.GetPmcProfile(sessionId); var botRole = botGenerationDetails.IsPmc ? "pmc" : botGenerationDetails.Role; - var botEquipmentBlacklist = GetBotEquipmentBlacklist(botRole, botLevel); - var botEquipmentWhitelist = GetBotEquipmentWhitelist(botRole, botLevel); - var botWeightingAdjustments = GetBotWeightingAdjustments(botRole, botLevel); + var botEquipmentBlacklist = GetBotEquipmentBlacklist(botRole, botGenerationDetails.BotLevel); + var botEquipmentWhitelist = GetBotEquipmentWhitelist(botRole, botGenerationDetails.BotLevel); + var botWeightingAdjustments = GetBotWeightingAdjustments(botRole, botGenerationDetails.BotLevel); var botWeightingAdjustmentsByPlayerLevel = GetBotWeightingAdjustmentsByPlayerLevel(botRole, pmcProfile?.Info?.Level ?? 1); RandomisationDetails? randomisationDetails = null; if (BotEquipmentConfig.TryGetValue(botRole.ToLowerInvariant(), out var botEquipmentConfig)) { - randomisationDetails = botHelper.GetBotRandomizationDetails(botLevel, botEquipmentConfig); + randomisationDetails = botHelper.GetBotRandomizationDetails(botGenerationDetails.BotLevel, botEquipmentConfig); } if (botEquipmentBlacklist is not null || botEquipmentWhitelist is not null) diff --git a/Libraries/SPTarkov.Server.Core/Services/BotNameService.cs b/Libraries/SPTarkov.Server.Core/Services/BotNameService.cs index 12636aff..f8a99104 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BotNameService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BotNameService.cs @@ -40,13 +40,11 @@ public class BotNameService( /// /// bot JSON data from db /// - /// role of bot e.g. assault /// Lowercase roles to always make unique /// Nickname for bot public string GenerateUniqueBotNickname( BotType botJsonTemplate, BotGenerationDetails botGenerationDetails, - string botRole, HashSet? uniqueRoles = null ) { @@ -54,7 +52,7 @@ public class BotNameService( // Never show for players var showTypeInNickname = !botGenerationDetails.IsPlayerScav && BotConfig.ShowTypeInNickname; - var roleShouldBeUnique = uniqueRoles?.Contains(botRole.ToLowerInvariant()); + var roleShouldBeUnique = uniqueRoles?.Contains(botGenerationDetails.RoleLowercase); var attempts = 0; while (attempts <= 5) @@ -69,7 +67,7 @@ public class BotNameService( // Config is set to add role to end of bot name if (showTypeInNickname) { - name += $" {botRole}"; + name += $" {botGenerationDetails.RoleLowercase}"; } // Replace pmc bot names with player name + prefix @@ -93,7 +91,7 @@ public class BotNameService( if (logger.IsLogEnabled(LogLevel.Debug)) { logger.Debug( - $"Failed to find unique name for: {botRole} {botGenerationDetails.Side} after 5 attempts, using: {genericName}" + $"Failed to find unique name for: {botGenerationDetails.RoleLowercase} {botGenerationDetails.Side} after 5 attempts, using: {genericName}" ); } @@ -114,7 +112,7 @@ public class BotNameService( } // Should never reach here - return $"BOT {botRole} {botGenerationDetails.BotDifficulty}"; + return $"BOT {botGenerationDetails.RoleLowercase} {botGenerationDetails.BotDifficulty}"; } private bool AddNameToCache(string name) diff --git a/Testing/UnitTests/Tests/Generators/BotWeaponGeneratorTests.cs b/Testing/UnitTests/Tests/Generators/BotWeaponGeneratorTests.cs index 36a392e7..9fb8588b 100644 --- a/Testing/UnitTests/Tests/Generators/BotWeaponGeneratorTests.cs +++ b/Testing/UnitTests/Tests/Generators/BotWeaponGeneratorTests.cs @@ -3,6 +3,7 @@ using SPTarkov.Server.Core.Generators; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Profile; +using SPTarkov.Server.Core.Models.Spt.Bots; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; @@ -45,6 +46,13 @@ public class BotWeaponGeneratorTests } var weaponParentId = new MongoId(); + var botGen = new BotGenerationDetails + { + Role = "pmcUSEC", + RoleLowercase = "pmcusec", + BotLevel = 69, + IsPmc = true, + }; for (var i = 0; i < 100; i++) { @@ -55,9 +63,7 @@ public class BotWeaponGeneratorTests botTemplateInventory, weaponParentId, weaponModChances, - "pmcUSEC", - true, - 69 + botGen ); var itemSize = _inventoryHelper.GetItemSize(weaponTpl, result.Weapon[0].Id, result.Weapon);