Reduced number of params throughout bot generation process

This commit is contained in:
Chomp
2025-09-08 16:41:25 +01:00
parent 4830f1e2b2
commit 3af494317c
8 changed files with 254 additions and 211 deletions
@@ -171,8 +171,12 @@ public class BotGenerator(
/// <returns>BotBase object</returns>
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);
}
@@ -70,45 +70,26 @@ public class BotInventoryGenerator(
/// <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>
/// <param name="botGenerationDetails">Details related to generating a bot</param>
/// <param name="botLevel">Level of bot being generated</param>
/// <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,
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(
/// <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>
/// <param name="botRole">Role bot has (assault/pmcBot)</param>
/// <param name="botInventory">Inventory to add equipment to</param>
/// <param name="botLevel">Level of bot</param>
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
/// <param name="isPmc">Is the generated bot a PMC</param>
/// <param name="botGenerationDetails">Details related to generating a bot</param>
/// <param name="raidConfig">RadiConfig</param>
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(
/// <param name="equipmentChances">Chances bot can have equipment equipped</param>
/// <param name="sessionId">Session id</param>
/// <param name="botInventory">Inventory to add weapons to</param>
/// <param name="botRole">assault/pmcBot/bossTagilla etc</param>
/// <param name="isPmc">Is the bot being generated as a pmc</param>
/// <param name="botGenerationDetails">Details related to generating a bot</param>
/// <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,
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(
/// <param name="templateInventory">bot/x.json data from db</param>
/// <param name="botInventory">Inventory to add weapon+mags/ammo to</param>
/// <param name="equipmentChances">Chances bot can have equipment equipped</param>
/// <param name="botRole">assault/pmcBot/bossTagilla etc</param>
/// <param name="isPmc">Is the bot being generated as a pmc</param>
/// <param name="botGenerationDetails">Details related to generating a bot</param>
/// <param name="itemGenerationWeights"></param>
/// <param name="botLevel"></param>
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
);
}
}
@@ -61,19 +61,13 @@ public class BotLootGenerator(
/// <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>
/// <param name="isPmc">Will bot be a pmc</param>
/// <param name="botRole">Role of bot, e.g. assault</param>
/// <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,
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(
/// <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>
/// <param name="botGenerationDetails"></param>
/// <param name="templateInventory">Bots template, assault.json</param>
/// <param name="modChances">Chances for mods to spawn on weapon</param>
/// <param name="botRole">bots role .e.g. pmcBot</param>
/// <param name="isPmc">are we generating for a pmc</param>
/// <param name="botLevel">Level of bot having loose weapon generated</param>
public void AddLooseWeaponsToInventorySlot(
MongoId botId,
MongoId sessionId,
BotBaseInventory botInventory,
EquipmentSlots equipmentSlot,
BotGenerationDetails botGenerationDetails,
BotTypeInventory? templateInventory,
Dictionary<string, double> modChances,
string botRole,
bool isPmc,
int botLevel
Dictionary<string, double> modChances
)
{
var chosenWeaponType = randomUtil.GetArrayValue<string>(
@@ -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;
}
@@ -54,21 +54,17 @@ public class BotWeaponGenerator(
/// <param name="sessionId">Session identifier</param>
/// <param name="equipmentSlot">Primary/secondary/holster</param>
/// <param name="botTemplateInventory">e.g. assault.json</param>
/// <param name="weaponParentId"></param>
/// <param name="botGenerationDetails">Details related to generating a bot</param>
/// <param name="weaponParentId">Details related to generating a bot</param>
/// <param name="modChances"></param>
/// <param name="botRole">Role of bot, e.g. assault/followerBully</param>
/// <param name="isPmc">Is weapon generated for a pmc</param>
/// <param name="botLevel"></param>
/// <returns>GenerateWeaponResult object</returns>
public GenerateWeaponResult? GenerateRandomWeapon(
MongoId sessionId,
string equipmentSlot,
BotTypeInventory botTemplateInventory,
BotGenerationDetails botGenerationDetails,
MongoId weaponParentId,
Dictionary<string, double> modChances,
string botRole,
bool isPmc,
int botLevel
Dictionary<string, double> 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(
/// <param name="botTemplateInventory">e.g. assault.json.</param>
/// <param name="weaponParentId">Parent ID of the weapon being generated.</param>
/// <param name="modChances">Dictionary of item types and % chance weapon will have that mod.</param>
/// <param name="botRole">e.g. assault/exusec.</param>
/// <param name="isPmc">Is weapon being generated for a PMC.</param>
/// <param name="botLevel">The level of the bot.</param>
/// <param name="botGenerationDetails"></param>
/// <returns>GenerateWeaponResult object.</returns>
public GenerateWeaponResult? GenerateWeaponByTpl(
MongoId sessionId,
@@ -122,9 +114,7 @@ public class BotWeaponGenerator(
BotTypeInventory botTemplateInventory,
MongoId weaponParentId,
Dictionary<string, double> 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 ?? [];
}
@@ -17,6 +17,12 @@ public record BotGenerationDetails
[JsonPropertyName("role")]
public string Role { get; set; }
/// <summary>
/// assault/pmcBot etc
/// </summary>
[JsonPropertyName("BotRoleLowercase")]
public string RoleLowercase { get; set; }
/// <summary>
/// Side of bot
/// </summary>
@@ -85,4 +91,14 @@ public record BotGenerationDetails
/// </summary>
[JsonPropertyName("clearBotContainerCacheAfterGeneration")]
public bool ClearBotContainerCacheAfterGeneration { get; set; } = true;
/// <summary>
/// Level the bot will have once generated
/// </summary>
public int BotLevel { get; set; }
/// <summary>
/// Version of the game bot will use - Only apples to PMCs
/// </summary>
public string GameVersion { get; set; }
}
@@ -28,22 +28,21 @@ public class BotEquipmentFilterService(
/// </summary>
/// <param name="sessionId">Players id</param>
/// <param name="baseBotNode">bots json data to filter</param>
/// <param name="botLevel">Level of the bot</param>
/// <param name="botGenerationDetails">details on how to generate a bot</param>
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)
@@ -40,13 +40,11 @@ public class BotNameService(
/// </summary>
/// <param name="botJsonTemplate">bot JSON data from db</param>
/// <param name="botGenerationDetails"></param>
/// <param name="botRole">role of bot e.g. assault</param>
/// <param name="uniqueRoles">Lowercase roles to always make unique</param>
/// <returns>Nickname for bot</returns>
public string GenerateUniqueBotNickname(
BotType botJsonTemplate,
BotGenerationDetails botGenerationDetails,
string botRole,
HashSet<string>? 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)
@@ -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);