From 5c83bf3925cedf3befbddbe3ae3c1cdb519f297c Mon Sep 17 00:00:00 2001 From: Chomp Date: Sun, 15 Jun 2025 09:03:39 +0100 Subject: [PATCH] Repeatable code cleanup --- .../Controllers/RepeatableQuestController.cs | 123 +++++++++--------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs b/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs index de59d8b6..a11f0f4f 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/RepeatableQuestController.cs @@ -137,7 +137,7 @@ public class RepeatableQuestController( // Delete the replaced quest change requirement data as we're going to add new data below repeatablesOfTypeInProfile.ChangeRequirement.Remove(changeRequest.QuestId); - // Get config for this repeatable sub-type (daily/weekly/scav) + // Get config for this repeatable subtype (daily/weekly/scav) var repeatableConfig = _questConfig.RepeatableQuests.FirstOrDefault(config => config.Name == repeatablesOfTypeInProfile.Name ); @@ -147,8 +147,8 @@ public class RepeatableQuestController( repeatableConfig.Types = [questToReplace.Type.ToString()]; } - // Generate meta-data for what type/levelrange of quests can be generated for player - var allowedQuestTypes = GenerateQuestPool(repeatableConfig, pmcData.Info.Level); + // Generate meta-data for what type/level range of quests can be generated for player + var allowedQuestTypes = GenerateQuestPool(repeatableConfig, pmcData.Info.Level.GetValueOrDefault(1)); var newRepeatableQuest = AttemptToGenerateRepeatableQuest( sessionID, pmcData, @@ -176,6 +176,7 @@ public class RepeatableQuestController( ); } + // Remove the replaced quest from profile RemoveQuestFromProfile(fullProfile, questToReplace.Id); // Delete the replaced quest change requirement from profile @@ -203,7 +204,7 @@ public class RepeatableQuestController( var charismaBonus = _profileHelper.GetSkillFromProfile(pmcData, SkillTypes.Charisma)?.Progress ?? 0; foreach (var cost in previousChangeRequirement.ChangeCost) { - // Not free, Charge player + appy charisma bonus to cost of replacement + // Not free, Charge player + apply charisma bonus to cost of replacement cost.Count = (int) Math.Truncate(cost.Count.Value * (1 - Math.Truncate(charismaBonus / 100) * 0.001)); _paymentService.AddPaymentToOutput(pmcData, cost.TemplateId, cost.Count.Value, sessionID, output); if (output.Warnings.Count > 0) @@ -219,10 +220,8 @@ public class RepeatableQuestController( // Purge inactive repeatables repeatableToChangeClone.InactiveQuests = []; - // Nullguard - output.ProfileChanges[sessionID].RepeatableQuests ??= []; - // Update client output with new repeatable + output.ProfileChanges[sessionID].RepeatableQuests ??= []; output.ProfileChanges[sessionID].RepeatableQuests.Add(repeatableToChangeClone); return output; @@ -238,18 +237,21 @@ public class RepeatableQuestController( { foreach (var repeatableQuest in pmcData.RepeatableQuests) { - var matchingQuest = repeatableQuest.ActiveQuests.FirstOrDefault(x => x.Id == questId); - if (matchingQuest is not null) + var matchingQuest = repeatableQuest.ActiveQuests?.FirstOrDefault(x => x.Id == questId); + if (matchingQuest is null) { - if (_logger.IsLogEnabled(LogLevel.Debug)) - { - _logger.Debug($"Accepted repeatable quest: {questId} from: {repeatableQuest.Name}"); - } - - matchingQuest.SptRepatableGroupName = repeatableQuest.Name; - - return matchingQuest; + // No daily/weekly/scav repeatable, skip over to next subtype + continue; } + + if (_logger.IsLogEnabled(LogLevel.Debug)) + { + _logger.Debug($"Accepted repeatable quest: {questId} from: {repeatableQuest.Name}"); + } + + matchingQuest.SptRepatableGroupName = repeatableQuest.Name; + + return matchingQuest; } return null; @@ -311,18 +313,18 @@ public class RepeatableQuestController( // Will assist in cleanup of existing profiles data { repeatablesOfTypeInProfile.ChangeRequirement.Clear(); + + return; } - else - // Multiple active quests of this type (e.g. daily or weekly) are active, just remove the single replaced quest - { - repeatablesOfTypeInProfile.ChangeRequirement.Remove(replacedQuestId); - } + + // Multiple active quests of this type (e.g. daily or weekly) are active, just remove the single replaced quest + repeatablesOfTypeInProfile.ChangeRequirement.Remove(replacedQuestId); } /// /// Generate a repeatable quest /// - /// Session/Player id + /// Session/Player id /// Players PMC profile /// What type/level range of quests can be generated for player /// Config for the quest type to generate @@ -331,7 +333,7 @@ public class RepeatableQuestController( QuestTypePool questTypePool, RepeatableQuestConfig repeatableConfig) { const int maxAttempts = 10; - RepeatableQuest newRepeatableQuest = null; + RepeatableQuest? newRepeatableQuest = null; var attempts = 0; while (attempts < maxAttempts && questTypePool.Types.Count > 0) { @@ -365,12 +367,12 @@ public class RepeatableQuestController( /// /// Profile to remove quest from /// Quest id to remove from profile - protected void RemoveQuestFromProfile(SptProfile? fullProfile, string questToReplaceId) + protected void RemoveQuestFromProfile(SptProfile fullProfile, string questToReplaceId) { // Find quest we're replacing in pmc profile quests array and remove it _questHelper.FindAndRemoveQuestFromArrayIfExists(questToReplaceId, fullProfile.CharacterData.PmcData.Quests); - // Find quest we're replacing in scav profile quests array and remove it + // Look for and remove quest we're replacing in scav profile too if (fullProfile.CharacterData.ScavData is not null) { _questHelper.FindAndRemoveQuestFromArrayIfExists( @@ -392,9 +394,9 @@ public class RepeatableQuestController( { // Check for existing quest in (daily/weekly/scav arrays) var questToReplace = - repeatablesInProfile.ActiveQuests.FirstOrDefault(repeatable => repeatable.Id == questId); + repeatablesInProfile.ActiveQuests?.FirstOrDefault(repeatable => repeatable.Id == questId); if (questToReplace is null) - // Not found, skip to next repeatable sub-type + // Not found, skip to next repeatable subtype { continue; } @@ -410,7 +412,7 @@ public class RepeatableQuestController( } /// - /// Handle client/repeatalbeQuests/activityPeriods + /// Handle client/repeatableQuests/activityPeriods /// Returns an array of objects in the format of repeatable quests to the client. /// repeatableQuestObject = { /// *id: Unique Id, @@ -481,7 +483,7 @@ public class RepeatableQuestController( ProcessExpiredQuests(generatedRepeatables, pmcData); // Create dynamic quest pool to avoid generating duplicates - var questTypePool = GenerateQuestPool(repeatableConfig, pmcData.Info.Level); + var questTypePool = GenerateQuestPool(repeatableConfig, pmcData.Info.Level.GetValueOrDefault(1)); // Add repeatable quests of this loops sub-type (daily/weekly) for (var i = 0; i < GetQuestCount(repeatableConfig, fullProfile); i++) @@ -704,12 +706,12 @@ public class RepeatableQuestController( /// main repeatable quest config /// Players level /// Allowed quest pool - protected QuestTypePool GenerateQuestPool(RepeatableQuestConfig repeatableConfig, int? pmcLevel) + protected QuestTypePool GenerateQuestPool(RepeatableQuestConfig repeatableConfig, int pmcLevel) { var questPool = CreateBaseQuestPool(repeatableConfig); // Get the allowed locations based on the PMC's level - var locations = GetAllowedLocationsForPmcLevel(repeatableConfig.Locations, pmcLevel.Value); + var locations = GetAllowedLocationsForPmcLevel(repeatableConfig.Locations, pmcLevel); // Populate Exploration and Pickup quest locations foreach (var (location, value) in locations) @@ -724,39 +726,40 @@ public class RepeatableQuestController( // Add "any" to pickup quest pool questPool.Pool.Pickup.Locations[ELocationName.any] = ["any"]; - var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel.Value, repeatableConfig); + var eliminationConfig = _repeatableQuestHelper.GetEliminationConfigByPmcLevel(pmcLevel, repeatableConfig); var targetsConfig = new ProbabilityObjectArray(_mathUtil, _cloner, eliminationConfig.Targets); // Populate Elimination quest targets and their locations - foreach (var targetKvP in targetsConfig) - // Target is boss + foreach (var target in targetsConfig) { - if (targetKvP.Data.IsBoss.GetValueOrDefault(false)) + // Target is boss + if (target.Data.IsBoss.GetValueOrDefault(false)) { questPool.Pool.Elimination.Targets.Add( - targetKvP.Key, + target.Key, new TargetLocation { Locations = ["any"] } ); + + continue; } - else - { - // Non-boss targets - var possibleLocations = locations.Keys; - var allowedLocations = - targetKvP.Key == "Savage" - ? possibleLocations.Where(location => location != ELocationName.laboratory - ) // Exclude labs for Savage targets. - : possibleLocations; + // Target is not boss + var possibleLocations = locations.Keys; + var allowedLocations = + target.Key == "Savage" + ? possibleLocations.Where(location => location != ELocationName.laboratory + ) // Exclude labs for Savage targets. + : possibleLocations; - questPool.Pool.Elimination.Targets[targetKvP.Key] = new TargetLocation + questPool.Pool.Elimination.Targets.Add( + target.Key, + new TargetLocation { Locations = allowedLocations.Select(x => x.ToString()).ToList() - }; - } + }); } return questPool; @@ -800,20 +803,13 @@ public class RepeatableQuestController( Dictionary> locations, int pmcLevel) { var allowedLocation = new Dictionary>(); - foreach (var (location, value) in locations) + foreach (var (location, matchingLocations) in locations) { - var locationNames = new List(); - foreach (var locationName in value) + var playerAccessibleMapIds = matchingLocations + .Where(locationName => IsPmcLevelAllowedOnLocation(locationName, pmcLevel)).ToList(); + if (playerAccessibleMapIds.Any()) { - if (IsPmcLevelAllowedOnLocation(locationName, pmcLevel)) - { - locationNames.Add(locationName); - } - } - - if (locationNames.Count > 0) - { - allowedLocation[location] = locationNames; + allowedLocation.Add(location, playerAccessibleMapIds); } } @@ -847,15 +843,14 @@ public class RepeatableQuestController( /// Get count of repeatable quests profile should have access to /// /// - /// - /// Player profile + /// Full player profile /// Quest count protected int GetQuestCount(RepeatableQuestConfig repeatableConfig, SptProfile fullProfile) { var questCount = repeatableConfig.NumQuests.GetValueOrDefault(0); if (questCount == 0) { - _logger.Warning($"Repeatable {repeatableConfig.Name} quests have a count of 0"); + _logger.Warning($"Repeatable: {repeatableConfig.Name} quests have a count of 0"); } // Add elite bonus to daily quests