using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; using SPTarkov.Server.Core.Utils.Cloners; namespace SPTarkov.Server.Core.Helpers; [Injectable] public class RepeatableQuestHelper( ISptLogger logger, DatabaseService databaseService, ServerLocalisationService serverLocalisationService, ICloner cloner, ConfigServer configServer ) { protected readonly QuestConfig QuestConfig = configServer.GetConfig(); /// /// Get the relevant elimination config based on the current players PMC level /// /// Level of PMC character /// Main repeatable config /// EliminationConfig public EliminationConfig? GetEliminationConfigByPmcLevel(int pmcLevel, RepeatableQuestConfig repeatableConfig) { return repeatableConfig.QuestConfig.Elimination.FirstOrDefault(x => pmcLevel >= x.LevelRange.Min && pmcLevel <= x.LevelRange.Max); } /// /// Gets a cloned repeatable quest template for the provided type with a unique id /// /// Type of template to retrieve /// TraderId that should provide this quest /// Cloned quest template /// public RepeatableQuest? GetClonedQuestTemplateForType(RepeatableQuestType type, MongoId traderId) { var repeatableQuestTemplates = databaseService.GetTemplates().RepeatableQuests.Templates; var quest = type switch { RepeatableQuestType.Elimination => cloner.Clone(repeatableQuestTemplates?.Elimination), RepeatableQuestType.Completion => cloner.Clone(repeatableQuestTemplates?.Completion), RepeatableQuestType.Exploration => cloner.Clone(repeatableQuestTemplates?.Exploration), RepeatableQuestType.Pickup => cloner.Clone(repeatableQuestTemplates?.Pickup), _ => null, }; if (quest is null) { return null; } quest.Id = new MongoId(); quest.TraderId = traderId; return quest; } /// /// Generates the base object of quest type format given as templates in /// assets/database/templates/repeatableQuests.json /// The templates include Elimination, Completion and Extraction quest types /// /// Quest type: "Elimination", "Completion" or "Extraction" /// Trader from which the quest will be provided /// Scav daily or pmc daily/weekly quest /// sessionId to generate template for /// /// Object which contains the base elements for repeatable quests of the requests type /// (needs to be filled with reward and conditions by called to make a valid quest) /// public RepeatableQuest? GenerateRepeatableTemplate( RepeatableQuestType type, MongoId traderId, PlayerGroup playerGroup, MongoId sessionId ) { var questData = GetClonedQuestTemplateForType(type, traderId); if (questData is null) { logger.Error(serverLocalisationService.GetText("repeatable-quest_helper_template_not_found", type)); return null; } var templateName = Enum.GetName(type); if (templateName is null) { logger.Error(serverLocalisationService.GetText("repeatable-quest_helper_template_name_not_found", type)); return null; } // Get template id from config based on side and type of quest var typeIds = GetRepeatableQuestTemplatesByGroup(playerGroup); questData.TemplateId = typeIds.GetValueOrDefault(templateName); // Force REF templates to use prapors ID - solves missing text issue var desiredTraderId = traderId == Traders.REF ? Traders.PRAPOR : traderId; // In locale, these id correspond to the text of quests // template ids -pmc : Elimination = 616052ea3054fc0e2c24ce6e / Completion = 61604635c725987e815b1a46 / Exploration = 616041eb031af660100c9967 // template ids -scav : Elimination = 62825ef60e88d037dc1eb428 / Completion = 628f588ebb558574b2260fe5 / Exploration = 62825ef60e88d037dc1eb42c questData.Name = questData.Name.Replace("{traderId}", traderId).Replace("{templateId}", questData.TemplateId); questData.Note = questData.Note?.Replace("{traderId}", desiredTraderId).Replace("{templateId}", questData.TemplateId); questData.Description = questData.Description.Replace("{traderId}", desiredTraderId).Replace("{templateId}", questData.TemplateId); questData.SuccessMessageText = questData .SuccessMessageText?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); questData.FailMessageText = questData .FailMessageText?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); questData.StartedMessageText = questData .StartedMessageText?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); questData.ChangeQuestMessageText = questData .ChangeQuestMessageText?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); questData.AcceptPlayerMessage = questData .AcceptPlayerMessage?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); questData.DeclinePlayerMessage = questData .DeclinePlayerMessage?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); questData.CompletePlayerMessage = questData .CompletePlayerMessage?.Replace("{traderId}", desiredTraderId) .Replace("{templateId}", questData.TemplateId); if (questData.QuestStatus is null) { logger.Error(serverLocalisationService.GetText("repeatable-quest_helper_no_status", type)); return null; } questData.QuestStatus.Id = new MongoId(); questData.QuestStatus.Uid = sessionId; // Needs to match user id questData.QuestStatus.QId = questData.Id; // Needs to match quest id return questData; } /// /// Returns the repeatable template ids for the provided side /// /// Side to get the templates for /// /// protected Dictionary GetRepeatableQuestTemplatesByGroup(PlayerGroup playerGroup) { var templates = QuestConfig.RepeatableQuestTemplates; return playerGroup switch { PlayerGroup.Pmc => templates.Pmc, PlayerGroup.Scav => templates.Scav, _ => throw new ArgumentOutOfRangeException(nameof(playerGroup), playerGroup, null), }; } /// /// Convert a raw location string into a location code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567) /// /// e.g. factory4_day /// guid public string? GetQuestLocationByMapId(string locationKey) { if (!QuestConfig.LocationIdMap.TryGetValue(locationKey, out var locationId)) { logger.Error(serverLocalisationService.GetText("repeatable-quest_helper_no_loc_id", locationKey)); return null; } return locationId; } }