437 lines
20 KiB
C#
437 lines
20 KiB
C#
using Core.Annotations;
|
|
using Core.Context;
|
|
using Core.Helpers;
|
|
using Core.Models.Eft.Common.Tables;
|
|
using Core.Models.Eft.Match;
|
|
using Core.Models.Enums;
|
|
using Core.Models.Spt.Bots;
|
|
using Core.Models.Spt.Config;
|
|
using Core.Servers;
|
|
using Core.Utils;
|
|
using Equipment = Core.Models.Eft.Common.Tables.Equipment;
|
|
using ILogger = Core.Models.Utils.ILogger;
|
|
|
|
namespace Core.Generators;
|
|
|
|
[Injectable]
|
|
public class BotInventoryGenerator
|
|
{
|
|
private readonly ILogger _logger;
|
|
private readonly HashUtil _hashUtil;
|
|
private readonly RandomUtil _randomUtil;
|
|
private readonly DatabaseService _databaseService;
|
|
private readonly ApplicationContext _applicationContext;
|
|
private readonly BotWeaponGenerator _botWeaponGenerator;
|
|
private readonly BotLootGenerator _botLootGenerator;
|
|
private readonly BotGeneratorHelper _botGeneratorHelper;
|
|
private readonly ProfileHelper _profileHelper;
|
|
private readonly BotHelper _botHelper;
|
|
private readonly WeightedRandomHelper _weightedRandomHelper;
|
|
private readonly ItemHelper _itemHelper;
|
|
private readonly WeatherHelper _weatherHelper;
|
|
private readonly LocalisationService _localisationService;
|
|
private readonly BotEquipmentFilterService _botEquipmentFilterService;
|
|
private readonly BotEquipmentModPoolService _botEquipmentModPoolService;
|
|
private readonly BotEquipmentModGenerator _botEquipmentModGenerator;
|
|
private readonly ConfigServer _configServer;
|
|
|
|
private BotConfig _botConfig;
|
|
|
|
public BotInventoryGenerator(
|
|
ILogger logger,
|
|
HashUtil hashUtil,
|
|
RandomUtil randomUtil,
|
|
DatabaseService databaseService,
|
|
ApplicationContext applicationContext,
|
|
BotWeaponGenerator botWeaponGenerator,
|
|
BotLootGenerator botLootGenerator,
|
|
BotGeneratorHelper botGeneratorHelper,
|
|
ProfileHelper profileHelper,
|
|
BotHelper botHelper,
|
|
WeightedRandomHelper weightedRandomHelper,
|
|
ItemHelper itemHelper,
|
|
WeatherHelper weatherHelper,
|
|
LocalisationService localisationService,
|
|
BotEquipmentFilterService botEquipmentFilterService,
|
|
BotEquipmentModPoolService botEquipmentModPoolService,
|
|
BotEquipmentModGenerator botEquipmentModGenerator,
|
|
ConfigServer configServer
|
|
)
|
|
{
|
|
_logger = logger;
|
|
_hashUtil = hashUtil;
|
|
_randomUtil = randomUtil;
|
|
_databaseService = databaseService;
|
|
_applicationContext = applicationContext;
|
|
_botWeaponGenerator = botWeaponGenerator;
|
|
_botLootGenerator = botLootGenerator;
|
|
_botGeneratorHelper = botGeneratorHelper;
|
|
_profileHelper = profileHelper;
|
|
_botHelper = botHelper;
|
|
_weightedRandomHelper = weightedRandomHelper;
|
|
_itemHelper = itemHelper;
|
|
_weatherHelper = weatherHelper;
|
|
_localisationService = localisationService;
|
|
_botEquipmentFilterService = botEquipmentFilterService;
|
|
_botEquipmentModPoolService = botEquipmentModPoolService;
|
|
_botEquipmentModGenerator = botEquipmentModGenerator;
|
|
_configServer = configServer;
|
|
|
|
_botConfig = _configServer.GetConfig<BotConfig>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add equipment/weapons/loot to bot
|
|
/// </summary>
|
|
/// <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="isPmc">Is bot being converted into a pmc</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(string sessionId, BotType botJsonTemplate, string botRole, bool isPmc, int botLevel, string chosenGameVersion)
|
|
{
|
|
var templateInventory = botJsonTemplate.BotInventory;
|
|
var wornItemChances = botJsonTemplate.BotChances;
|
|
var itemGenerationLimitsMinMax = botJsonTemplate.BotGeneration;
|
|
|
|
// Generate base inventory with no items
|
|
var botInventory = GenerateInventoryBase();
|
|
|
|
// Get generated raid details bot will be spawned in
|
|
var raidConfig = _applicationContext
|
|
.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
|
|
?.GetValue<GetRaidConfigurationRequestData>();
|
|
|
|
GenerateAndAddEquipmentToBot(
|
|
sessionId,
|
|
templateInventory,
|
|
wornItemChances,
|
|
botRole,
|
|
botInventory,
|
|
botLevel,
|
|
chosenGameVersion,
|
|
raidConfig);
|
|
|
|
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
|
|
GenerateAndAddWeaponsToBot(
|
|
templateInventory,
|
|
wornItemChances,
|
|
sessionId,
|
|
botInventory,
|
|
botRole,
|
|
isPmc,
|
|
itemGenerationLimitsMinMax,
|
|
botLevel);
|
|
|
|
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
|
|
_botLootGenerator.GenerateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
|
|
|
|
return botInventory;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a pmcInventory object with all the base/generic items needed
|
|
/// </summary>
|
|
/// <returns>PmcInventory object</returns>
|
|
public BotBaseInventory GenerateInventoryBase()
|
|
{
|
|
var equipmentId = _hashUtil.Generate();
|
|
var stashId = _hashUtil.Generate();
|
|
var questRaidItemsId = _hashUtil.Generate();
|
|
var questStashItemsId = _hashUtil.Generate();
|
|
var sortingTableId = _hashUtil.Generate();
|
|
|
|
return new BotBaseInventory{
|
|
Items =
|
|
[
|
|
new() { Id = equipmentId, Template = ItemTpl.INVENTORY_DEFAULT },
|
|
new() { Id = stashId, Template = ItemTpl.STASH_STANDARD_STASH_10X30 },
|
|
new() { Id = questRaidItemsId, Template = ItemTpl.STASH_QUESTRAID },
|
|
new() { Id = questStashItemsId, Template = ItemTpl.STASH_QUESTOFFLINE },
|
|
new() { Id = sortingTableId, Template = ItemTpl.SORTINGTABLE_SORTING_TABLE }
|
|
],
|
|
Equipment = equipmentId,
|
|
Stash = stashId,
|
|
QuestRaidItems = questRaidItemsId,
|
|
QuestStashItems = questStashItemsId,
|
|
SortingTable = sortingTableId,
|
|
HideoutAreaStashes = { },
|
|
FastPanel = { },
|
|
FavoriteItems = [],
|
|
HideoutCustomizationStashId = "",
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add equipment to a bot
|
|
/// </summary>
|
|
/// <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="raidConfig">RadiConfig</param>
|
|
public void GenerateAndAddEquipmentToBot(string sessionId, BotTypeInventory templateInventory, Chances wornItemChances, string botRole,
|
|
BotBaseInventory botInventory, int botLevel, string chosenGameVersion, GetRaidConfigurationRequestData raidConfig)
|
|
{
|
|
// These will be handled later
|
|
var excludedSlots = new List<EquipmentSlots>(){
|
|
EquipmentSlots.Pockets,
|
|
EquipmentSlots.FirstPrimaryWeapon,
|
|
EquipmentSlots.SecondPrimaryWeapon,
|
|
EquipmentSlots.Holster,
|
|
EquipmentSlots.ArmorVest,
|
|
EquipmentSlots.TacticalVest,
|
|
EquipmentSlots.FaceCover,
|
|
EquipmentSlots.Headwear,
|
|
EquipmentSlots.Earpiece
|
|
};
|
|
|
|
_botConfig.Equipment.TryGetValue(_botGeneratorHelper.GetBotEquipmentRole(botRole), out var botEquipConfig);
|
|
var randomistionDetails = _botHelper.GetBotRandomizationDetails(botLevel, botEquipConfig);
|
|
|
|
// Apply nighttime changes if its nighttime + there's changes to make
|
|
if (
|
|
randomistionDetails?.NighttimeChanges is not null &&
|
|
raidConfig is not null &&
|
|
_weatherHelper.IsNightTime(raidConfig.TimeVariant)
|
|
)
|
|
{
|
|
foreach (var equipmentSlotKvP in (randomistionDetails.NighttimeChanges.EquipmentModsModifiers)) {
|
|
// Never let mod chance go outside of 0 - 100
|
|
randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] = Math.Min(
|
|
Math.Max( randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] + equipmentSlotKvP.Value, 0), 100);
|
|
}
|
|
}
|
|
|
|
// Get profile of player generating bots, we use their level later on
|
|
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
|
|
var botEquipmentRole = _botGeneratorHelper.GetBotEquipmentRole(botRole);
|
|
|
|
|
|
// Iterate over all equipment slots of bot, do it in specifc order to reduce conflicts
|
|
// e.g. ArmorVest should be generated after TactivalVest
|
|
// or FACE_COVER before HEADWEAR
|
|
foreach (var equipmentSlotKvP in templateInventory.Equipment) {
|
|
// Skip some slots as they need to be done in a specific order + with specific parameter values
|
|
// e.g. Weapons
|
|
if (excludedSlots.Contains(equipmentSlotKvP.Key)) {
|
|
continue;
|
|
}
|
|
|
|
GenerateEquipment( new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = equipmentSlotKvP.Key,
|
|
RootEquipmentPool = equipmentSlotKvP.Value,
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
}
|
|
|
|
// Generate below in specific order
|
|
GenerateEquipment( new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = EquipmentSlots.Pockets,
|
|
// Unheard profiles have unique sized pockets, TODO - handle this somewhere else in a better way
|
|
RootEquipmentPool =
|
|
chosenGameVersion == GameEditions.UNHEARD
|
|
? new Dictionary<string, double>{ [ItemTpl.POCKETS_1X4_TUE] = 1 }
|
|
: templateInventory.Equipment[EquipmentSlots.Pockets],
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData{ Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GenerateModsBlacklist = [ItemTpl.POCKETS_1X4_TUE],
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
|
|
GenerateEquipment( new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = EquipmentSlots.FaceCover,
|
|
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.FaceCover],
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
|
|
GenerateEquipment( new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = EquipmentSlots.Headwear,
|
|
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Headwear],
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
|
|
GenerateEquipment(new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = EquipmentSlots.Earpiece,
|
|
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Earpiece],
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
|
|
var hasArmorVest = GenerateEquipment( new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = EquipmentSlots.ArmorVest,
|
|
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.ArmorVest],
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
|
|
// Bot has no armor vest and flagged to be forced to wear armored rig in this event
|
|
if (botEquipConfig.ForceOnlyArmoredRigWhenNoArmor.GetValueOrDefault(false) && !hasArmorVest) {
|
|
// Filter rigs down to only those with armor
|
|
FilterRigsToThoseWithProtection(templateInventory.Equipment, botRole);
|
|
}
|
|
|
|
// Optimisation - Remove armored rigs from pool
|
|
if (hasArmorVest) {
|
|
// Filter rigs down to only those with armor
|
|
FilterRigsToThoseWithoutProtection(templateInventory.Equipment, botRole);
|
|
}
|
|
|
|
// Bot is flagged as always needing a vest
|
|
if (botEquipConfig.ForceRigWhenNoVest.GetValueOrDefault(false) && !hasArmorVest) {
|
|
wornItemChances.EquipmentChances["TacticalVest"] = 100;
|
|
}
|
|
|
|
GenerateEquipment( new GenerateEquipmentProperties
|
|
{
|
|
RootEquipmentSlot = EquipmentSlots.Earpiece,
|
|
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Earpiece],
|
|
ModPool = templateInventory.Mods,
|
|
SpawnChances = wornItemChances,
|
|
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
|
Inventory = botInventory,
|
|
BotEquipmentConfig = botEquipConfig,
|
|
RandomisationDetails = randomistionDetails,
|
|
GeneratingPlayerLevel = pmcProfile.Info.Level,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove non-armored rigs from parameter data
|
|
/// </summary>
|
|
/// <param name="templateEquipment">Equpiment to filter TacticalVest of</param>
|
|
/// <param name="botRole">Role of bot vests are being filtered for</param>
|
|
public void FilterRigsToThoseWithProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void FilterRigsToThoseWithoutProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole, bool allowEmptyResult = true)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove armored rigs from parameter data
|
|
/// </summary>
|
|
/// <param name="templateEquipment">Equpiment to filter TacticalVest of</param>
|
|
/// <param name="botRole">Role of bot vests are being filtered for</param>
|
|
/// <param name="allowEmptyRequest">Should the function return all rigs when 0 unarmored are found</param>
|
|
public void FilterRigsTothoseWithoutProtection(Equipment templateEquipment, string botRole, bool allowEmptyRequest = false)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a piece of equipment with mods to inventory from the provided pools
|
|
/// </summary>
|
|
/// <param name="settings">Values to adjust how item is chosen and added to bot</param>
|
|
/// <returns>true when item added</returns>
|
|
public bool GenerateEquipment(GenerateEquipmentProperties settings)
|
|
{
|
|
_logger.Error("NOT IMPLEMENTED - GenerateEquipment");
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all possible mods for item and filter down based on equipment blacklist from bot.json config
|
|
/// </summary>
|
|
/// <param name="itemTpl">Item mod pool is being retrieved and filtered</param>
|
|
/// <param name="equipmentBlacklist">Blacklist to filter mod pool with</param>
|
|
/// <returns>Filtered pool of mods</returns>
|
|
public Dictionary<string, List<string>> GetFilteredDynamicModsForItem(string itemTpl, Dictionary<string, List<string>> equipmentBlacklist)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Work out what weapons bot should have equipped and add them to bot inventory
|
|
/// </summary>
|
|
/// <param name="templateInventory">bot/x.json data from db</param>
|
|
/// <param name="equipmentChances">Chances bot can have equipment equipped</param>
|
|
/// <param name="sessionId">Session id</param>
|
|
/// <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="itemGenerationLimitsMinMax">Limits for items the bot can have</param>
|
|
/// <param name="botLevel">level of bot having weapon generated</param>
|
|
public void GenerateAndAddWeaponsToBot(BotTypeInventory templateInventory, Chances equipmentChances, string sessionId, BotBaseInventory botInventory,
|
|
string botRole,
|
|
bool isPmc, Generation itemGenerationLimitsMinMax, int botLevel)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate if the bot should have weapons in Primary/Secondary/Holster slots
|
|
/// </summary>
|
|
/// <param name="equipmentChances">Chances bot has certain equipment</param>
|
|
/// <returns>What slots bot should have weapons generated for</returns>
|
|
public object GetDesiredWeaponsForBot(Chances equipmentChances) // TODO: Type fuckery { slot: EquipmentSlots; shouldSpawn: boolean }[]
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add weapon + spare mags/ammo to bots inventory
|
|
/// </summary>
|
|
/// <param name="sessionId">Session id</param>
|
|
/// <param name="weaponSlot">Weapon slot being generated</param>
|
|
/// <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="itemGenerationWeights"></param>
|
|
/// <param name="botLevel"></param>
|
|
public void AddWeaponAndMagazineToInventory(string sessionId, object weaponSlot, BotBaseInventory templateInventory, BotBaseInventory botInventory,
|
|
Chances equipmentChances, string botRole,
|
|
bool isPmc, Generation itemGenerationWeights, int botLevel)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|