diff --git a/Libraries/Core/Helpers/SecureContainerHelper.cs b/Libraries/Core/Helpers/SecureContainerHelper.cs index d0273a18..2399c1c2 100644 --- a/Libraries/Core/Helpers/SecureContainerHelper.cs +++ b/Libraries/Core/Helpers/SecureContainerHelper.cs @@ -1,10 +1,10 @@ -using SptCommon.Annotations; +using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; namespace Core.Helpers; [Injectable] -public class SecureContainerHelper +public class SecureContainerHelper(ItemHelper _itemHelper) { /// /// Get a list of the item IDs (NOT tpls) inside a secure container @@ -13,6 +13,16 @@ public class SecureContainerHelper /// List of ids public List GetSecureContainerItems(List items) { - throw new NotImplementedException(); + var secureContainer = items.First((x) => x.SlotId == "SecuredContainer"); + + // No container found, drop out + if (secureContainer is null) { + return []; + } + + var itemsInSecureContainer = _itemHelper.FindAndReturnChildrenByItems(items, secureContainer.Id); + + // Return all items returned and exclude the secure container item itself + return itemsInSecureContainer.Where((x) => x != secureContainer.Id).ToList(); } } diff --git a/Libraries/Core/Models/Spt/Services/LootRequest.cs b/Libraries/Core/Models/Spt/Services/LootRequest.cs index 446e9459..b993932b 100644 --- a/Libraries/Core/Models/Spt/Services/LootRequest.cs +++ b/Libraries/Core/Models/Spt/Services/LootRequest.cs @@ -46,7 +46,7 @@ public record LootRequest /// key: item base type: value: max count /// [JsonPropertyName("itemLimits")] - public Dictionary? ItemLimits { get; set; } + public Dictionary? ItemLimits { get; set; } [JsonPropertyName("itemStackLimits")] public Dictionary? ItemStackLimits { get; set; } diff --git a/Libraries/Core/Services/AirdropService.cs b/Libraries/Core/Services/AirdropService.cs index 4a38f5f9..8abe2d67 100644 --- a/Libraries/Core/Services/AirdropService.cs +++ b/Libraries/Core/Services/AirdropService.cs @@ -1,17 +1,41 @@ -using SptCommon.Annotations; +using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.Location; using Core.Models.Enums; using Core.Models.Spt.Services; +using Core.Servers; +using Core.Models.Spt.Config; +using Core.Models.Utils; +using Core.Generators; +using Core.Utils; +using Core.Helpers; namespace Core.Services; [Injectable] -public class AirdropService +public class AirdropService( + ConfigServer configServer, + ISptLogger _logger, + LootGenerator _lootGenerator, + HashUtil _hashUtil, + WeightedRandomHelper _weightedRandomHelper, + LocalisationService _localisationService, + ItemFilterService _itemFilterService, + ItemHelper _itemHelper) { + protected AirdropConfig _airdropConfig = configServer.GetConfig(); + public GetAirdropLootResponse GenerateCustomAirdropLoot(GetAirdropLootRequest request) { - throw new NotImplementedException(); + if (!_airdropConfig.CustomAirdropMapping.TryGetValue(request.ContainerId, out var customAirdropInformation)) { + _logger.Warning( + $"Unable to find data for custom airdrop {request.ContainerId}, returning random airdrop instead" + ); + + return GenerateAirdropLoot(); + } + + return GenerateAirdropLoot(customAirdropInformation); } /// @@ -21,9 +45,40 @@ public class AirdropService /// /// OPTIONAL - Desired airdrop type, randomised when not provided /// List of LootItem objects - public GetAirdropLootResponse GenerateAirdropLoot(string forcedAirdropType = null) + public GetAirdropLootResponse GenerateAirdropLoot(SptAirdropTypeEnum? forcedAirdropType = null) { - throw new NotImplementedException(); + var airdropType = forcedAirdropType != null ? forcedAirdropType : ChooseAirdropType(); + _logger.Debug($"Chose: {airdropType} for airdrop loot"); + + // Common/weapon/etc + var airdropConfig = GetAirdropLootConfigByType((AirdropTypeEnum)airdropType); + + // generate loot to put into airdrop crate + var crateLoot = airdropConfig.UseForcedLoot.GetValueOrDefault(false) + ? _lootGenerator.CreateForcedLoot(airdropConfig.ForcedLoot) + : _lootGenerator.CreateRandomLoot(airdropConfig); + + // Create airdrop crate and add to result in first spot + var airdropCrateItem = GetAirdropCrateItem((SptAirdropTypeEnum)airdropType); + + // Add crate to front of list + crateLoot.Insert(0, airdropCrateItem); + + // Reparent loot items to crate we added above + foreach (var item in crateLoot) { + if (item.Id == airdropCrateItem.Id) { + // Crate itself, don't alter + continue; + } + + // no parentId = root item, make item have create as parent + if (item.ParentId is null) { + item.ParentId = airdropCrateItem.Id; + item.SlotId = "main"; + } + } + + return new GetAirdropLootResponse { Icon = airdropConfig.Icon, Container = crateLoot }; } /// @@ -33,7 +88,38 @@ public class AirdropService /// Item protected Item GetAirdropCrateItem(SptAirdropTypeEnum airdropType) { - throw new NotImplementedException(); + var airdropContainer = new Item { + Id = _hashUtil.Generate(), + Template = "", // picked later + Upd = new Upd() + { + SpawnedInSession = true, + StackObjectsCount = 1, + }, + }; + + switch (airdropType) { + case SptAirdropTypeEnum.foodMedical: + airdropContainer.Template = ItemTpl.LOOTCONTAINER_AIRDROP_MEDICAL_CRATE; + break; + case SptAirdropTypeEnum.barter: + airdropContainer.Template = ItemTpl.LOOTCONTAINER_AIRDROP_SUPPLY_CRATE; + break; + case SptAirdropTypeEnum.weaponArmor: + airdropContainer.Template = ItemTpl.LOOTCONTAINER_AIRDROP_WEAPON_CRATE; + break; + case SptAirdropTypeEnum.mixed: + airdropContainer.Template = ItemTpl.LOOTCONTAINER_AIRDROP_COMMON_SUPPLY_CRATE; + break; + case SptAirdropTypeEnum.radar: + airdropContainer.Template = ItemTpl.LOOTCONTAINER_AIRDROP_TECHNICAL_SUPPLY_CRATE_EVENT_1; + break; + default: + airdropContainer.Template = ItemTpl.LOOTCONTAINER_AIRDROP_COMMON_SUPPLY_CRATE; + break; + } + + return airdropContainer; } /// @@ -42,7 +128,9 @@ public class AirdropService /// airdrop type value protected SptAirdropTypeEnum ChooseAirdropType() { - throw new NotImplementedException(); + var possibleAirdropTypes = _airdropConfig.AirdropTypeWeightings; + + return _weightedRandomHelper.GetWeightedValue(possibleAirdropTypes); } /// @@ -50,8 +138,43 @@ public class AirdropService /// /// Type of airdrop to get settings for /// LootRequest - protected LootRequest GetAirdropLootConfigByType(AirdropTypeEnum airdropType) + protected AirdropLootRequest GetAirdropLootConfigByType(AirdropTypeEnum airdropType) { - throw new NotImplementedException(); + var lootSettingsByType = _airdropConfig.Loot[airdropType.ToString()]; + if (lootSettingsByType is null) { + _logger.Error( + _localisationService.GetText("location-unable_to_find_airdrop_drop_config_of_type", airdropType) + ); + + // Default to common + lootSettingsByType = _airdropConfig.Loot[AirdropTypeEnum.Common.ToString()]; + } + + // Get all items that match the blacklisted types and fold into item blacklist + var itemTypeBlacklist = _itemFilterService.GetItemRewardBaseTypeBlacklist(); + var itemsMatchingTypeBlacklist = _itemHelper.GetItems() + .Where((templateItem) => _itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist)) + .Select((templateItem) => templateItem.Id); + var itemBlacklist = new HashSet(); + itemBlacklist.UnionWith(lootSettingsByType.ItemBlacklist); + itemBlacklist.UnionWith(_itemFilterService.GetItemRewardBlacklist()); + itemBlacklist.UnionWith(_itemFilterService.GetBossItems()); + itemBlacklist.UnionWith(itemsMatchingTypeBlacklist); + + return new AirdropLootRequest { + Icon = lootSettingsByType.Icon, + WeaponPresetCount = lootSettingsByType.WeaponPresetCount, + ArmorPresetCount = lootSettingsByType.ArmorPresetCount, + ItemCount = lootSettingsByType.ItemCount, + WeaponCrateCount = lootSettingsByType.WeaponCrateCount, + ItemBlacklist = itemBlacklist.ToList(), + ItemTypeWhitelist = lootSettingsByType.ItemTypeWhitelist, + ItemLimits = lootSettingsByType.ItemLimits, + ItemStackLimits = lootSettingsByType.ItemStackLimits, + ArmorLevelWhitelist = lootSettingsByType.ArmorLevelWhitelist, + AllowBossItems = lootSettingsByType.AllowBossItems, + UseForcedLoot = lootSettingsByType.UseForcedLoot, + ForcedLoot = lootSettingsByType.ForcedLoot, + }; } }