diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json index 8381ad65..41b5a386 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json @@ -597,28 +597,107 @@ } } ], - "Completion": { - "possibleSkillRewards": [ - "Endurance", - "Strength", - "Vitality" - ], - "minRequestedAmount": 1, - "maxRequestedAmount": 4, - "uniqueItemCount": 2, - "minRequestedBulletAmount": 15, - "maxRequestedBulletAmount": 40, - "useWhitelist": true, - "useBlacklist": false, - "requiredItemsAreFiR": true, - "requiredItemMinDurabilityMinMax": { - "min": 60, - "max": 80 + "Completion": [ + { + "levelRange": { + "min": 1, + "max": 15 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 1, + "max": 4 + }, + "uniqueItemCount": { + "min": 1, + "max": 1 + }, + "requestedBulletCount": { + "min": 15, + "max": 40 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] }, - "requiredItemTypeBlacklist": [ - "5485a8684bdc2da71d8b4567" - ] - }, + { + "levelRange": { + "min": 16, + "max": 40 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 2, + "max": 4 + }, + "uniqueItemCount": { + "min": 1, + "max": 2 + }, + "requestedBulletCount": { + "min": 15, + "max": 40 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] + }, + { + "levelRange": { + "min": 41, + "max": 100 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 3, + "max": 6 + }, + "uniqueItemCount": { + "min": 1, + "max": 2 + }, + "requestedBulletCount": { + "min": 15, + "max": 40 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] + } + ], "Elimination": [ { "levelRange": { @@ -1745,28 +1824,107 @@ } } ], - "Completion": { - "possibleSkillRewards": [ - "Endurance", - "Strength", - "Vitality" - ], - "minRequestedAmount": 4, - "maxRequestedAmount": 12, - "uniqueItemCount": 4, - "minRequestedBulletAmount": 20, - "maxRequestedBulletAmount": 60, - "useWhitelist": true, - "useBlacklist": false, - "requiredItemsAreFiR": true, - "requiredItemMinDurabilityMinMax": { - "min": 60, - "max": 80 + "Completion": [ + { + "levelRange": { + "min": 1, + "max": 15 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 4, + "max": 6 + }, + "uniqueItemCount": { + "min": 1, + "max": 2 + }, + "requestedBulletCount": { + "min": 20, + "max": 60 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] }, - "requiredItemTypeBlacklist": [ - "5485a8684bdc2da71d8b4567" - ] - }, + { + "levelRange": { + "min": 16, + "max": 40 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 4, + "max": 8 + }, + "uniqueItemCount": { + "min": 1, + "max": 2 + }, + "requestedBulletCount": { + "min": 20, + "max": 60 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] + }, + { + "levelRange": { + "min": 41, + "max": 100 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 6, + "max": 12 + }, + "uniqueItemCount": { + "min": 1, + "max": 3 + }, + "requestedBulletCount": { + "min": 20, + "max": 60 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] + } + ], "Elimination": [ { "levelRange": { @@ -2887,28 +3045,107 @@ ], "maxItemFetchCount": 3 }, - "Completion": { - "possibleSkillRewards": [ - "Endurance", - "Strength", - "Vitality" - ], - "minRequestedAmount": 1, - "maxRequestedAmount": 3, - "uniqueItemCount": 1, - "minRequestedBulletAmount": 15, - "maxRequestedBulletAmount": 40, - "useWhitelist": true, - "useBlacklist": false, - "requiredItemsAreFiR": true, - "requiredItemMinDurabilityMinMax": { - "min": 60, - "max": 80 + "Completion": [ + { + "levelRange": { + "min": 1, + "max": 15 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 1, + "max": 3 + }, + "uniqueItemCount": { + "min": 1, + "max": 1 + }, + "requestedBulletCount": { + "min": 15, + "max": 40 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] }, - "requiredItemTypeBlacklist": [ - "5485a8684bdc2da71d8b4567" - ] - }, + { + "levelRange": { + "min": 16, + "max": 40 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 2, + "max": 5 + }, + "uniqueItemCount": { + "min": 1, + "max": 1 + }, + "requestedBulletCount": { + "min": 15, + "max": 40 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] + }, + { + "levelRange": { + "min": 41, + "max": 100 + }, + "possibleSkillRewards": [ + "Endurance", + "Strength", + "Vitality" + ], + "requestedItemCount": { + "min": 4, + "max": 6 + }, + "uniqueItemCount": { + "min": 1, + "max": 1 + }, + "requestedBulletCount": { + "min": 15, + "max": 40 + }, + "useWhitelist": true, + "useBlacklist": false, + "requiredItemsAreFiR": true, + "requiredItemMinDurabilityMinMax": { + "min": 60, + "max": 80 + }, + "requiredItemTypeBlacklist": [ + "5485a8684bdc2da71d8b4567" + ] + } + ], "Elimination": [ { "levelRange": { diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locales/server/en.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locales/server/en.json index efca89e5..7b32e000 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locales/server/en.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locales/server/en.json @@ -663,6 +663,7 @@ "repair-unable_to_find_item_repair_cost": "Unable to find repair cost for item: %s", "repair-unable_to_find_trader_details_by_id": "Unable to find trader: %s repair details", "repeatable-accepted_repeatable_quest_not_found_in_active_quests": "Accepted a repeatable quest: %s which could not be found in the activeQuests array. Please report this bug", + "repeatable-completion_config_no_template": "Unable to find completion config for pmc level: {{pmcLevel}}", "repeatable-completion_quest_whitelist_too_small_or_blacklist_too_restrictive": "Generate Completion Quest: No items remain. Either Whitelist is too small or Blacklist too restrictive", "repeatable-difficulty_was_nan": "Repeatable Reward Generation: Difficulty was NaN. Setting to 1.", "repeatable-exploration_config_no_template": "Unable to find exploration config for pmc level: {{pmcLevel}}", diff --git a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/CompletionQuestGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/CompletionQuestGenerator.cs index 26eeb644..32d98145 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/CompletionQuestGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/CompletionQuestGenerator.cs @@ -47,7 +47,13 @@ public class CompletionQuestGenerator( RepeatableQuestConfig repeatableConfig ) { - var completionConfig = repeatableConfig.QuestConfig.CompletionConfig; + var completionConfig = repeatableQuestHelper.GetCompletionConfigByPmcLevel(pmcLevel, repeatableConfig); + if (completionConfig is null) + { + logger.Warning(localisationService.GetText("repeatable-completion_config_no_template", new { pmcLevel })); + return null; + } + var levelsConfig = repeatableConfig.RewardScaling.Levels; var roublesConfig = repeatableConfig.RewardScaling.Roubles; @@ -73,12 +79,12 @@ public class CompletionQuestGenerator( // We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as // [{"minPlayerLevel": 1, "itemIds": ["id1",...]}, {"minPlayerLevel": 15, "itemIds": ["id3",...]}] - if (repeatableConfig.QuestConfig.CompletionConfig.UseWhitelist) + if (completionConfig.UseWhitelist) { itemsToRetrievePool = GetWhitelistedItemSelection(itemsToRetrievePool, pmcLevel); } - if (repeatableConfig.QuestConfig.CompletionConfig.UseBlacklist) + if (completionConfig.UseBlacklist) { itemsToRetrievePool = GetBlacklistedItemSelection(itemsToRetrievePool, pmcLevel); } @@ -91,7 +97,7 @@ public class CompletionQuestGenerator( return null; } - var selectedItems = GenerateAvailableForFinish(quest, completionConfig, repeatableConfig, itemsToRetrievePool.ToList(), budget); + var selectedItems = GenerateAvailableForFinish(quest, completionConfig, itemsToRetrievePool.ToList(), budget); quest.Rewards = repeatableQuestRewardGenerator.GenerateReward( pmcLevel, @@ -240,20 +246,18 @@ public class CompletionQuestGenerator( /// /// Quest to add the conditions to /// Completion config - /// Repeatable config /// Filtered item selection /// Budget in roubles /// Chosen item template Ids protected List GenerateAvailableForFinish( RepeatableQuest quest, CompletionConfig completionConfig, - RepeatableQuestConfig repeatableConfig, List itemSelection, double roublesBudget ) { // Store the indexes of items we are asking player to supply - var distinctItemsToRetrieveCount = randomUtil.GetInt(1, completionConfig.UniqueItemCount); + var distinctItemsToRetrieveCount = randomUtil.GetInt(completionConfig.UniqueItemCount.Min, completionConfig.UniqueItemCount.Max); var chosenRequirementItemsTpls = new List(); var usedItemIndexes = new HashSet(); @@ -289,8 +293,8 @@ public class CompletionQuestGenerator( var tplChosen = itemSelection[chosenItemIndex]; var itemPrice = itemHelper.GetItemPrice(tplChosen)!.Value; - var minValue = completionConfig.MinimumRequestedAmount; - var maxValue = completionConfig.MaximumRequestedAmount; + var minValue = completionConfig.RequestedItemCount.Min; + var maxValue = completionConfig.RequestedItemCount.Max; var value = minValue; @@ -308,7 +312,7 @@ public class CompletionQuestGenerator( // Push a CompletionCondition with the item and the amount of the item into quest chosenRequirementItemsTpls.Add(tplChosen); - quest.Conditions.AvailableForFinish!.Add(GenerateCondition(tplChosen, value, repeatableConfig.QuestConfig.CompletionConfig)); + quest.Conditions.AvailableForFinish!.Add(GenerateCondition(tplChosen, value, completionConfig)); // Is there budget left for more items if (roublesBudget > 0) diff --git a/Libraries/SPTarkov.Server.Core/Helpers/RepeatableQuestHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/RepeatableQuestHelper.cs index 0904e6a8..ee113a86 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/RepeatableQuestHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/RepeatableQuestHelper.cs @@ -45,6 +45,19 @@ public class RepeatableQuestHelper( ); } + /// + /// Get the relevant completion config based on the current players PMC level + /// + /// Level of PMC character + /// Main repeatable config + /// CompletionConfig + public CompletionConfig? GetCompletionConfigByPmcLevel(int pmcLevel, RepeatableQuestConfig repeatableConfig) + { + return repeatableConfig.QuestConfig.CompletionConfig.FirstOrDefault(x => + pmcLevel >= x.LevelRange.Min && pmcLevel <= x.LevelRange.Max + ); + } + /// /// Gets a cloned repeatable quest template for the provided type with a unique id /// diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/QuestConfig.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/QuestConfig.cs index b8b3d3c4..bf7b1156 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/QuestConfig.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/QuestConfig.cs @@ -344,7 +344,7 @@ public record RepeatableQuestTypesConfig /// Defines completion repeatable task generation parameters /// [JsonPropertyName("Completion")] - public required CompletionConfig CompletionConfig { get; set; } + public required List CompletionConfig { get; set; } /// /// Defines pickup repeatable task generation parameters - TODO: Not implemented/No Data - NOTE: Does not work with dynamicLocale @@ -416,34 +416,28 @@ public record SpecificExits public record CompletionConfig : BaseQuestConfig { /// - /// Minimum item count that can be requested + /// Level range at which completion tasks should be generated from this config /// - [JsonPropertyName("minRequestedAmount")] - public required int MinimumRequestedAmount { get; set; } + [JsonPropertyName("levelRange")] + public required MinMax LevelRange { get; set; } /// - /// Maximum item count that can be requested + /// The minimum and maximum amounts that can be requested for an item /// - [JsonPropertyName("maxRequestedAmount")] - public required int MaximumRequestedAmount { get; set; } + [JsonPropertyName("requestedItemCount")] + public required MinMax RequestedItemCount { get; set; } /// - /// How many unique items should be requested - TODO: This needs to be a range + /// How many different unique items should be requested /// [JsonPropertyName("uniqueItemCount")] - public required int UniqueItemCount { get; set; } + public required MinMax UniqueItemCount { get; set; } /// - /// Minimum bullet count that can be requested - TODO: Not implemented + /// The minimum and maximum amounts that can be requested for bullets - TODO: Not implemented /// - [JsonPropertyName("minRequestedBulletAmount")] - public required int MinimumRequestedBulletAmount { get; set; } - - /// - /// Maximum bullet count that can be requested - TODO: Not implemented - /// - [JsonPropertyName("maxRequestedBulletAmount")] - public required int MaximumRequestedBulletAmount { get; set; } + [JsonPropertyName("requestedBulletCount")] + public required MinMax RequestedBulletCount { get; set; } /// /// Should the item whitelist be used @@ -467,7 +461,7 @@ public record CompletionConfig : BaseQuestConfig /// Min/Max durability requirements for the item /// [JsonPropertyName("requiredItemMinDurabilityMinMax")] - public required MinMax RequiredItemMinDurabilityMinMax { get; set; } + public required MinMax RequiredItemMinDurabilityMinMax { get; set; } /// /// Blacklisted item types to not collect