diff --git a/Libraries/SPTarkov.Server.Core/Generators/LootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/LootGenerator.cs index 49298738..58afe175 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/LootGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/LootGenerator.cs @@ -36,9 +36,10 @@ public class LootGenerator( /// /// parameters to adjust how loot is generated /// An array of loot items - public List> CreateRandomLoot(LootRequest options) + public IEnumerable> CreateRandomLoot(LootRequest options) { var result = new List>(); + var itemTypeCounts = InitItemLimitCounter(options.ItemLimits); // Handle sealed weapon containers @@ -81,7 +82,7 @@ public class LootGenerator( ); // Pool has items we could add as loot, proceed - if (rewardPoolResults.ItemPool.Count > 0) + if (rewardPoolResults.ItemPool.Any()) { var randomisedItemCount = randomUtil.GetInt( options.ItemCount.Min, @@ -113,11 +114,9 @@ public class LootGenerator( ); if (randomisedWeaponPresetCount > 0) { - var weaponDefaultPresets = globalDefaultPresets - .Where(preset => - itemHelper.IsOfBaseclass(preset.Encyclopedia.Value, BaseClasses.WEAPON) - ) - .ToList(); + var weaponDefaultPresets = globalDefaultPresets.Where(preset => + itemHelper.IsOfBaseclass(preset.Encyclopedia.Value, BaseClasses.WEAPON) + ); if (weaponDefaultPresets.Any()) { @@ -149,9 +148,9 @@ public class LootGenerator( var armorDefaultPresets = globalDefaultPresets.Where(preset => itemHelper.ArmorItemCanHoldMods(preset.Encyclopedia.Value) ); - var levelFilteredArmorPresets = armorDefaultPresets - .Where(armor => IsArmorOfDesiredProtectionLevel(armor, options)) - .ToList(); + var levelFilteredArmorPresets = armorDefaultPresets.Where(armor => + IsArmorOfDesiredProtectionLevel(armor, options) + ); // Add some armors to rewards if (levelFilteredArmorPresets.Any()) @@ -245,7 +244,7 @@ public class LootGenerator( /// results of filtering + blacklist used protected ItemRewardPoolResults GetItemRewardPool( HashSet itemTplBlacklist, - List itemTypeWhitelist, + HashSet itemTypeWhitelist, bool useRewardItemBlacklist, bool allowBossItems, bool blockSeasonalItemsOutOfSeason @@ -281,14 +280,12 @@ public class LootGenerator( itemBlacklist.UnionWith(seasonalEventService.GetInactiveSeasonalEventItems()); } - var items = itemsDb - .Where(item => - !itemBlacklist.Contains(item.Id) - && string.Equals(item.Type, "item", StringComparison.OrdinalIgnoreCase) - && !item.Properties.QuestItem.GetValueOrDefault(false) - && itemTypeWhitelist.Contains(item.Parent) - ) - .ToList(); + var items = itemsDb.Where(item => + !itemBlacklist.Contains(item.Id) + && string.Equals(item.Type, "item", StringComparison.OrdinalIgnoreCase) + && !item.Properties.QuestItem.GetValueOrDefault(false) + && itemTypeWhitelist.Contains(item.Parent) + ); return new ItemRewardPoolResults { ItemPool = items, Blacklist = itemBlacklist }; } @@ -348,7 +345,7 @@ public class LootGenerator( /// array to add found item to /// true if item was valid and added to pool protected bool FindAndAddRandomItemToLoot( - List items, + IEnumerable items, Dictionary itemTypeCounts, LootRequest options, List> result @@ -426,13 +423,13 @@ public class LootGenerator( /// List to add chosen preset to /// true if preset was valid and added to pool protected bool FindAndAddRandomPresetToLoot( - List presetPool, + IEnumerable presetPool, Dictionary itemTypeCounts, HashSet itemBlacklist, List> result ) { - if (presetPool.Count == 0) + if (!presetPool.Any()) { logger.Warning(serverLocalisationService.GetText("loot-preset_pool_is_empty")); @@ -732,10 +729,10 @@ public class LootGenerator( // Find a random item of the desired type and add as reward for (var index = 0; index < rewardCount; index++) { - var chosenItem = randomUtil.DrawRandomFromList(relatedItems.ToList()); + var chosenItem = randomUtil.GetArrayValue(relatedItems); var reward = new List { - new() { Id = new MongoId(), Template = chosenItem[0].Id }, + new() { Id = new MongoId(), Template = chosenItem.Id }, }; modRewards.Add(reward); @@ -785,7 +782,7 @@ public class LootGenerator( /// /// /// Single tpl - protected string PickRewardItem(RewardDetails rewardContainerDetails) + protected MongoId PickRewardItem(RewardDetails rewardContainerDetails) { if ( rewardContainerDetails.RewardTplPool is not null @@ -803,7 +800,7 @@ public class LootGenerator( protected record ItemRewardPoolResults { - public List ItemPool { get; set; } + public IEnumerable ItemPool { get; set; } public HashSet Blacklist { get; set; } } diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Location/GetAirdropLootResponse.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Location/GetAirdropLootResponse.cs index 89607980..d03bf129 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Location/GetAirdropLootResponse.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Location/GetAirdropLootResponse.cs @@ -13,8 +13,8 @@ public record GetAirdropLootResponse /// The type of airdrop /// [JsonPropertyName("icon")] - public AirdropTypeEnum? Icon { get; set; } + public AirdropTypeEnum Icon { get; set; } [JsonPropertyName("container")] - public List? Container { get; set; } + public IEnumerable Container { get; set; } } diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/AirdropConfig.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/AirdropConfig.cs index 38e4ce2f..683b3c39 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/AirdropConfig.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/AirdropConfig.cs @@ -68,7 +68,7 @@ public record AirdropLoot /// Item type (parentId) to allow inside crate /// [JsonPropertyName("itemTypeWhitelist")] - public required List ItemTypeWhitelist { get; set; } + public required HashSet ItemTypeWhitelist { get; set; } /// /// Item type/ item tpls to limit count of inside crate - key: item base type: value: max count diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/InventoryConfig.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/InventoryConfig.cs index 61e967ed..f52014a9 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/InventoryConfig.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/InventoryConfig.cs @@ -51,7 +51,7 @@ public record RewardDetails public Dictionary? RewardTplPool { get; set; } [JsonPropertyName("rewardTypePool")] - public List? RewardTypePool { get; set; } + public HashSet? RewardTypePool { get; set; } } public record SealedAirdropContainerSettings diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Services/LootRequest.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Services/LootRequest.cs index 20f3e892..e5864c65 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Services/LootRequest.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Services/LootRequest.cs @@ -43,7 +43,7 @@ public record LootRequest /// Item tpl whitelist to pick from /// [JsonPropertyName("itemTypeWhitelist")] - public List? ItemTypeWhitelist { get; set; } + public HashSet? ItemTypeWhitelist { get; set; } /// /// key: item base type: value: max count diff --git a/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs b/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs index fcdbf5e0..3ab1c736 100644 --- a/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/AirdropService.cs @@ -16,8 +16,8 @@ namespace SPTarkov.Server.Core.Services; [Injectable] public class AirdropService( - ConfigServer configServer, ISptLogger _logger, + ConfigServer configServer, LootGenerator _lootGenerator, DatabaseService databaseService, WeightedRandomHelper _weightedRandomHelper, @@ -80,14 +80,12 @@ public class AirdropService( // Filter loot pool to just items that fit crate var crateLoot = GetLootThatFitsContainer(airdropCrateItem, crateLootPool); - // Flatten loot into single array ready to be returned - var flattenedCrateLoot = crateLoot.SelectMany(x => x).ToList(); + // Create new array with crate at front + loot behind + var containerWithLoot = Enumerable.Empty().Append(airdropCrateItem); + containerWithLoot = containerWithLoot.Union(crateLoot.SelectMany(x => x)); - // Add crate to front of loot rewards - flattenedCrateLoot.Insert(0, airdropCrateItem); - - // Re-parent loot items to crate we just added - foreach (var item in flattenedCrateLoot) + // Re-parent root loot items to have crate as parent + foreach (var item in containerWithLoot) { if (item.Id == airdropCrateItem.Id) // Crate itself, skip @@ -105,8 +103,8 @@ public class AirdropService( return new GetAirdropLootResponse { - Icon = airdropConfig.Icon, - Container = flattenedCrateLoot, + Icon = airdropConfig.Icon.Value, + Container = containerWithLoot, }; } @@ -116,9 +114,9 @@ public class AirdropService( /// Crate item to fit items into /// Item pool to try and fit into container /// Items that will fit container - protected List> GetLootThatFitsContainer( + protected IEnumerable> GetLootThatFitsContainer( Item container, - List> crateLootPool + IEnumerable> crateLootPool ) { // list of root item + children in list diff --git a/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs b/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs index 44fc663a..cf0d9c5f 100644 --- a/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs @@ -447,7 +447,7 @@ public class LocationLifecycleService( } // Flatten - List mailableLoot = [.. loot.SelectMany(x => x)]; + IEnumerable mailableLoot = [.. loot.SelectMany(x => x)]; // Send message from fence giving player reward generated above mailSendService.SendLocalisedNpcMessageToPlayer(