using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; using Core.Models.Enums; using Core.Models.Utils; using Core.Helpers; using Core.Services; using Core.Servers; using Core.Models.Spt.Config; namespace Core.Generators; [Injectable] public class PMCLootGenerator { private readonly ISptLogger _logger; private readonly DatabaseService _databaseService; private readonly ItemHelper _itemHelper; private readonly ItemFilterService _itemFilterService; private readonly RagfairPriceService _ragfairPriceService; private readonly SeasonalEventService _seasonalEventService; private readonly WeightedRandomHelper _weightedRandomHelper; private readonly ConfigServer _configServer; private Dictionary? _backpackLootPool; private Dictionary? _pocketLootPool; private Dictionary? _vestLootPool; private readonly PmcConfig _pmcConfig; public PMCLootGenerator( ISptLogger logger, DatabaseService databaseService, ItemHelper itemHelper, ItemFilterService itemFilterService, RagfairPriceService ragfairPriceService, SeasonalEventService seasonalEventService, WeightedRandomHelper weightedRandomHelper, ConfigServer configServer ) { _logger = logger; _databaseService = databaseService; _itemHelper = itemHelper; _itemFilterService = itemFilterService; _ragfairPriceService = ragfairPriceService; _seasonalEventService = seasonalEventService; _weightedRandomHelper = weightedRandomHelper; _configServer = configServer; _pmcConfig = _configServer.GetConfig(); } /// /// Create a List of loot items a PMC can have in their pockets /// /// /// Dictionary of string and number public Dictionary GeneratePMCPocketLootPool(string botRole) { // Hydrate loot dictionary if empty if (_pocketLootPool is null) { _pocketLootPool = new Dictionary(); var items = _databaseService.GetItems(); var pmcPriceOverrides = _databaseService.GetBots().Types[botRole.ToLower() == "pmcbear" ? "bear" : "usec"].BotInventory.Items.Pockets; var allowedItemTypeWhitelist = _pmcConfig.PocketLoot.Whitelist; var blacklist = GetLootBlacklist(); var itemsToAdd = items.Where( (item) => allowedItemTypeWhitelist.Contains(item.Value.Parent) && _itemHelper.isValidItem(item.Value.Id) && !blacklist.Contains(item.Value.Id) && !blacklist.Contains(item.Value.Parent) && ItemFitsInto1By2Slot(item.Value) ); foreach (var (tpl, template) in itemsToAdd) { // If pmc has price override, use that. Otherwise, use flea price if (pmcPriceOverrides.ContainsKey(tpl)) { _pocketLootPool[tpl] = pmcPriceOverrides[tpl]; } else { // Set price of item as its weight var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES); _pocketLootPool[tpl] = price; } } var highestPrice = _pocketLootPool.Max(price => price.Value); foreach (var (key, _) in _pocketLootPool) { // Invert price so cheapest has a larger weight // Times by highest price so most expensive item has weight of 1 _pocketLootPool[key] = Math.Round((1 / _pocketLootPool[key]) * highestPrice); } _weightedRandomHelper.ReduceWeightValues(_pocketLootPool); } return _pocketLootPool; } private HashSet GetLootBlacklist() { var blacklist = new HashSet(); foreach (var blacklistedItem in _pmcConfig.PocketLoot.Blacklist) { blacklist.Add(blacklistedItem); } foreach (var blacklistedItem in _pmcConfig.GlobalLootBlacklist) { blacklist.Add(blacklistedItem); } foreach (var blacklistedItem in _itemFilterService.GetBlacklistedItems()) { blacklist.Add(blacklistedItem); } foreach (var blacklistedItem in _seasonalEventService.GetInactiveSeasonalEventItems()) { blacklist.Add(blacklistedItem); } return blacklist; } /// /// Create a List of loot items a PMC can have in their vests /// /// /// Dictionary of string and number public Dictionary GeneratePMCVestLootPool(string botRole) { // Hydrate loot dictionary if empty if (_vestLootPool is null) { _vestLootPool = new Dictionary(); var items = _databaseService.GetItems(); var pmcPriceOverrides = _databaseService.GetBots().Types[botRole.ToLower() == "pmcbear" ? "bear" : "usec"].BotInventory.Items.TacticalVest; var allowedItemTypeWhitelist = _pmcConfig.VestLoot.Whitelist; var blacklist = GetLootBlacklist(); var itemsToAdd = items.Where( (item) => allowedItemTypeWhitelist.Contains(item.Value.Parent) && _itemHelper.isValidItem(item.Value.Id) && !blacklist.Contains(item.Value.Id) && !blacklist.Contains(item.Value.Parent) && ItemFitsInto2By2Slot(item.Value)); foreach (var (tpl, template) in itemsToAdd) { // If pmc has price override, use that. Otherwise, use flea price if (pmcPriceOverrides.ContainsKey(tpl)) { _vestLootPool[tpl] = pmcPriceOverrides[tpl]; } else { // Set price of item as its weight var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES); _vestLootPool[tpl] = price; } } var highestPrice = _vestLootPool.Max(price => price.Value); foreach (var (key, _) in _vestLootPool) { // Invert price so cheapest has a larger weight // Times by highest price so most expensive item has weight of 1 _vestLootPool[key] = Math.Round((1 / _vestLootPool[key]) * highestPrice); } _weightedRandomHelper.ReduceWeightValues(_vestLootPool); } return _vestLootPool; } /// /// Check if item has a width/height that lets it fit into a 2x2 slot /// 1x1 / 1x2 / 2x1 / 2x2 /// /// Item to check size of /// true if it fits protected bool ItemFitsInto2By2Slot(TemplateItem item) { return item.Properties.Width <= 2 && item.Properties.Height <= 2; } /// /// Check if item has a width/height that lets it fit into a 1x2 slot /// 1x1 / 1x2 / 2x1 /// /// Item to check size of /// true if it fits protected bool ItemFitsInto1By2Slot(TemplateItem item) { return $"{item.Properties.Width}x{item.Properties.Height}" switch { "1x1" or "1x2" or "2x1" => true, _ => false }; } /// /// Create a List of loot items a PMC can have in their backpack /// /// /// Dictionary of string and number public Dictionary GeneratePMCBackpackLootPool(string botRole) { // Hydrate loot dictionary if empty if (_backpackLootPool is null) { _backpackLootPool = new Dictionary(); var items = _databaseService.GetItems(); var pmcPriceOverrides = _databaseService.GetBots().Types[botRole.ToLower() == "pmcbear" ? "bear" : "usec"].BotInventory.Items.Backpack; var allowedItemTypeWhitelist = _pmcConfig.BackpackLoot.Whitelist; var blacklist = GetLootBlacklist(); var itemsToAdd = items.Where( (item) => allowedItemTypeWhitelist.Contains(item.Value.Parent) && _itemHelper.isValidItem(item.Value.Id) && !blacklist.Contains(item.Value.Id) && !blacklist.Contains(item.Value.Parent)); foreach (var (tpl, template) in itemsToAdd) { // If pmc has price override, use that. Otherwise, use flea price if (pmcPriceOverrides.ContainsKey(tpl)) { _backpackLootPool[tpl] = pmcPriceOverrides[tpl]; } else { // Set price of item as its weight var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES); _backpackLootPool[tpl] = price; } } var highestPrice = _backpackLootPool.Max(price => price.Value); foreach (var (key, _) in _backpackLootPool) { // Invert price so cheapest has a larger weight // Times by highest price so most expensive item has weight of 1 _backpackLootPool[key] = Math.Round((1 / _backpackLootPool[key]) * highestPrice); } _weightedRandomHelper.ReduceWeightValues(_backpackLootPool); } return _backpackLootPool; } }