diff --git a/Libraries/Core/Controllers/RepairController.cs b/Libraries/Core/Controllers/RepairController.cs index cef7b881..c347c7cd 100644 --- a/Libraries/Core/Controllers/RepairController.cs +++ b/Libraries/Core/Controllers/RepairController.cs @@ -17,10 +17,10 @@ public class RepairController( /// Handle TraderRepair event /// Repair with trader /// - /// session id + /// session id /// endpoint request data /// player profile - /// item event router action + /// ItemEventRouterResponse public ItemEventRouterResponse TraderRepair( string sessionID, TraderRepairActionDataRequest body, @@ -64,7 +64,7 @@ public class RepairController( /// session id /// endpoint request data /// player profile - /// + /// ItemEventRouterResponse public ItemEventRouterResponse RepairWithKit( string sessionId, RepairActionDataRequest body, diff --git a/Libraries/Core/Controllers/RepeatableQuestController.cs b/Libraries/Core/Controllers/RepeatableQuestController.cs index b3c9fd79..fd65d41e 100644 --- a/Libraries/Core/Controllers/RepeatableQuestController.cs +++ b/Libraries/Core/Controllers/RepeatableQuestController.cs @@ -43,6 +43,13 @@ public class RepeatableQuestController( { protected QuestConfig _questConfig = _configServer.GetConfig(); + /// + /// Handle RepeatableQuestChange event + /// + /// + /// Change quest request + /// + /// public ItemEventRouterResponse ChangeRepeatableQuest(PmcData pmcData, RepeatableQuestChangeRequest changeRequest, string sessionID) { @@ -175,14 +182,15 @@ public class RepeatableQuestController( return output; } - /** - * Some accounts have access to free repeatable quest refreshes - * Track the usage of them inside players profile - * @param fullProfile Player profile - * @param repeatableSubType Can be daily / weekly / scav repeatable - * @param repeatableTypeName Subtype of repeatable quest: daily / weekly / scav - * @returns Is the repeatable being replaced for free - */ + /// + /// Some accounts have access to free repeatable quest refreshes + /// Track the usage of them inside players profile + /// + /// + /// Full player profile + /// Can be daily / weekly / scav repeatable + /// Subtype of repeatable quest: daily / weekly / scav + /// Is the repeatable being replaced for free protected bool UseFreeRefreshIfAvailable(SptProfile? fullProfile, PmcDataRepeatableQuest repeatableSubType, string repeatableTypeName) { @@ -218,11 +226,11 @@ public class RepeatableQuestController( return false; } - /** - * Clean up the repeatables `changeRequirement` dictionary of expired data - * @param repeatablesOfTypeInProfile The repeatables that have the replaced and new quest - * @param replacedQuestId Id of the replaced quest - */ + /// + /// Clean up the repeatables `changeRequirement` dictionary of expired data + /// + /// repeatables that have the replaced and new quest + /// Id of the replaced quest protected void CleanUpRepeatableChangeRequirements(PmcDataRepeatableQuest repeatablesOfTypeInProfile, string replacedQuestId) { @@ -239,6 +247,14 @@ public class RepeatableQuestController( } } + /// + /// Generate a repeatable quest + /// + /// + /// + /// What type/level range of quests can be generated for player + /// Config for the quest type to generate + /// protected RepeatableQuest? AttemptToGenerateRepeatableQuest(string sessionId, PmcData pmcData, QuestTypePool questTypePool, RepeatableQuestConfig repeatableConfig) { @@ -272,6 +288,11 @@ public class RepeatableQuestController( return newRepeatableQuest; } + /// + /// Remove the provided quest from pmc and scav character profiles + /// + /// Profile to remove quest from + /// Quest id to remove from profile protected void RemoveQuestFromProfile(SptProfile? fullProfile, string questToReplaceId) { // Find quest we're replacing in pmc profile quests array and remove it @@ -287,12 +308,13 @@ public class RepeatableQuestController( } } - /** - * Find a repeatable (daily/weekly/scav) from a players profile by its id - * @param questId Id of quest to find - * @param pmcData Profile that contains quests to look through - * @returns IGetRepeatableByIdResult - */ + /// + /// Find a repeatable (daily/weekly/scav) from a players profile by its id + /// + /// + /// Id of quest to find + /// Profile that contains quests to look through + /// protected GetRepeatableByIdResult GetRepeatableById(string questId, PmcData pmcData) { foreach (var repeatablesInProfile in pmcData.RepeatableQuests) @@ -316,6 +338,29 @@ public class RepeatableQuestController( return null; } + /// + /// Handle client/repeatalbeQuests/activityPeriods + /// Returns an array of objects in the format of repeatable quests to the client. + /// repeatableQuestObject = { + /// *id: Unique Id, + ///name: "Daily", + ///endTime: the time when the quests expire + ///activeQuests: currently available quests in an array. Each element of quest type format(see assets/ database / templates / repeatableQuests.json). + ///inactiveQuests: the quests which were previously active(required by client to fail them if they are not completed) + /// } + /// + /// The method checks if the player level requirement for repeatable quests(e.g.daily lvl5, weekly lvl15) is met and if the previously active quests + /// are still valid.This ischecked by endTime persisted in profile accordning to the resetTime configured for each repeatable kind(daily, weekly) + /// in QuestCondig.js + /// + /// If the condition is met, new repeatableQuests are created, old quests(which are persisted in the profile.RepeatableQuests[i].activeQuests) are + /// moved to profile.RepeatableQuests[i].inactiveQuests.This memory is required to get rid of old repeatable quest data in the profile, otherwise + /// they'll litter the profile's Quests field. + /// (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards) + /// The new quests generated are again persisted in profile.RepeatableQuests + /// + /// + /// Array of repeatable quests public List GetClientRepeatableQuests(string sessionID) { var returnData = new List(); @@ -446,6 +491,12 @@ public class RepeatableQuestController( return returnData; } + /// + /// Get repeatable quest data from profile from name (daily/weekly), creates base repeatable quest object if none exists + /// + /// daily/weekly config + /// + /// PmcDataRepeatableQuest protected PmcDataRepeatableQuest GetRepeatableQuestSubTypeFromProfile(RepeatableQuestConfig repeatableConfig, PmcData pmcData) { @@ -475,6 +526,12 @@ public class RepeatableQuestController( return repeatableQuestDetails; } + /// + /// Check if a repeatable quest type (daily/weekly) is active for the given profile + /// + /// Repeatable quest config + /// + /// True if profile has access to repeatables protected bool CanProfileAccessRepeatableQuests(RepeatableQuestConfig repeatableConfig, PmcData pmcData) { // PMC and daily quests not unlocked yet @@ -497,22 +554,22 @@ public class RepeatableQuestController( return true; } - /** - * Does player have daily pmc quests unlocked - * @param pmcData Player profile to check - * @param repeatableConfig Config of daily type to check - * @returns True if unlocked - */ + /// + /// Does player have daily pmc quests unlocked + /// + /// + /// Config of daily type to check + /// True if unlocked protected static bool PlayerHasDailyPmcQuestsUnlocked(PmcData pmcData, RepeatableQuestConfig repeatableConfig) { return pmcData.Info.Level >= repeatableConfig.MinPlayerLevel; } - /** - * Does player have daily scav quests unlocked - * @param pmcData Player profile to check - * @returns True if unlocked - */ + /// + /// Does player have daily scav quests unlocked + /// + /// + /// True if unlocked protected bool PlayerHasDailyScavQuestsUnlocked(PmcData pmcData) { return pmcData?.Hideout?.Areas?.FirstOrDefault(hideoutArea => hideoutArea.Type == HideoutAreas.INTEL_CENTER) @@ -520,6 +577,11 @@ public class RepeatableQuestController( 1; } + /// + /// Expire quests and replace expired quests with ready-to-hand-in quests inside generatedRepeatables.activeQuests + /// + /// Repeatables to process (daily/weekly) + /// protected void ProcessExpiredQuests(PmcDataRepeatableQuest generatedRepeatables, PmcData pmcData) { var questsToKeep = new List(); @@ -558,6 +620,14 @@ public class RepeatableQuestController( generatedRepeatables.ActiveQuests = questsToKeep; } + /// + /// Used to create a quest pool during each cycle of repeatable quest generation. The pool will be subsequently + /// narrowed down during quest generation to avoid duplicate quests. Like duplicate extractions or elimination quests + /// where you have to e.g. kill scavs in same locations + /// + /// main repeatable quest config + /// Players level + /// Allowed quest pool protected QuestTypePool GenerateQuestPool(RepeatableQuestConfig repeatableConfig, int? pmcLevel) { var questPool = CreateBaseQuestPool(repeatableConfig); @@ -617,6 +687,11 @@ public class RepeatableQuestController( return questPool; } + /// + /// Create a pool of quests to generate quests from + /// + /// Main repeatable config + /// QuestTypePool protected QuestTypePool CreateBaseQuestPool(RepeatableQuestConfig repeatableConfig) { return new QuestTypePool @@ -640,11 +715,16 @@ public class RepeatableQuestController( }; } + /// + /// Get a dictionary of map locations the player can access based on their current level + /// + /// + /// + /// Dictionary protected Dictionary> GetAllowedLocationsForPmcLevel( Dictionary> locations, int pmcLevel) { var allowedLocation = new Dictionary>(); - foreach (var (location, value) in locations) { var locationNames = new List(); @@ -665,12 +745,12 @@ public class RepeatableQuestController( return allowedLocation; } - /** - * Return true if the given pmcLevel is allowed on the given location - * @param location The location name to check - * @param pmcLevel The level of the pmc - * @returns True if the given pmc level is allowed to access the given location - */ + /// + /// Return true if the given pmcLevel is allowed on the given location + /// + /// location name to check + /// level of the pmc + /// True if the given pmc level is allowed to access the given location protected bool IsPmcLevelAllowedOnLocation(string location, int pmcLevel) { // All PMC levels are allowed for 'any' location requirement diff --git a/Libraries/Core/Controllers/TraderController.cs b/Libraries/Core/Controllers/TraderController.cs index 0a1b8b7d..8898ba04 100644 --- a/Libraries/Core/Controllers/TraderController.cs +++ b/Libraries/Core/Controllers/TraderController.cs @@ -82,9 +82,10 @@ public class TraderController( /// /// Adjust trader item prices based on config value multiplier + /// only applies to items sold for currency /// - /// - /// + /// Trader to adjust prices of + /// Coef to apply to traders' items' prices protected void AdjustTraderItemPrices(Trader trader, double multiplier) { foreach (var kvp in trader.Assort?.BarterScheme) diff --git a/Libraries/Core/Generators/BotEquipmentModGenerator.cs b/Libraries/Core/Generators/BotEquipmentModGenerator.cs index 09e6ae8c..0dbc07b9 100644 --- a/Libraries/Core/Generators/BotEquipmentModGenerator.cs +++ b/Libraries/Core/Generators/BotEquipmentModGenerator.cs @@ -40,7 +40,6 @@ public class BotEquipmentModGenerator( ) { protected BotConfig _botConfig = _configServer.GetConfig(); - protected static HashSet _modSightIds = ["mod_sight_front", "mod_sight_rear"]; // Slots that hold scopes @@ -380,6 +379,11 @@ public class BotEquipmentModGenerator( return result; } + /// + /// Gets the minimum and maximum plate class levels from an array of plates + /// + /// Pool of plates to sort by armorClass to get min and max + /// MinMax of armorClass from plate pool protected static MinMax GetMinMaxArmorPlateClass(List platePool) { platePool.Sort( @@ -406,12 +410,12 @@ public class BotEquipmentModGenerator( }; } - /** - * Get the default plate an armor has in its db item - * @param armorItem Item to look up default plate - * @param modSlot front/back - * @returns Tpl of plate - */ + /// + /// Get the default plate an armor has in its db item + /// + /// Item to look up default plate + /// front/back + /// Tpl of plate protected string? GetDefaultPlateTpl(TemplateItem armorItem, string modSlot) { var relatedItemDbModSlot = armorItem.Properties.Slots?.FirstOrDefault(slot => string.Equals(slot.Name, modSlot, StringComparison.OrdinalIgnoreCase)); @@ -419,12 +423,12 @@ public class BotEquipmentModGenerator( return relatedItemDbModSlot?.Props.Filters[0].Plate; } - /** - * Get the matching armor slot from the default preset matching passed in armor tpl - * @param presetItemId Id of preset - * @param modSlot front/back - * @returns Armor IItem - */ + /// + /// Get the matching armor slot from the default preset matching passed in armor tpl + /// + /// + /// + /// Armor IItem protected Item? GetDefaultPresetArmorSlot(string armorItemTpl, string modSlot) { var defaultPreset = _presetHelper.GetDefaultPreset(armorItemTpl); @@ -433,7 +437,6 @@ public class BotEquipmentModGenerator( string.Equals(item.SlotId, modSlot, StringComparison.OrdinalIgnoreCase)); } - /// /// Add mods to a weapon using the provided mod pool /// @@ -713,9 +716,16 @@ public class BotEquipmentModGenerator( return request.Weapon; } + /// + /// Does the passed in db item lack slot cartridges or chambers + /// + /// Item to check + /// True it lacks cartridges/chamber slots protected bool ItemLacksSlotsCartridgesAndChambers(TemplateItem item) { - return item.Properties.Slots?.Count == 0 && item.Properties.Cartridges?.Count == 0 && item.Properties.Chambers?.Count == 0; + return item.Properties.Slots?.Count == 0 + && item.Properties.Cartridges?.Count == 0 + && item.Properties.Chambers?.Count == 0; } /// @@ -734,7 +744,7 @@ public class BotEquipmentModGenerator( } /// - /// Is this modslot a front or rear sight + /// Is passed in modslot a front or rear sight /// /// Slot to check /// @@ -968,7 +978,7 @@ public class BotEquipmentModGenerator( /// itemHelper.getItem() result public KeyValuePair? ChooseModToPutIntoSlot(ModToSpawnRequest request) { - /** Slot mod will fill */ + // Slot mod will fill var parentSlot = request.ParentTemplate.Properties.Slots?.FirstOrDefault(i => i.Name == request.ModSlot); var weaponTemplate = _itemHelper.GetItem(request.Weapon[0].Template).Value; @@ -1291,7 +1301,7 @@ public class BotEquipmentModGenerator( } // Required mod is not default or randomisable, use existing pool - if (request.ItemModPool.TryGetValue(request.ModSlot, out var modsForSlot)) + if (!request.ItemModPool.TryGetValue(request.ModSlot, out var modsForSlot)) { return null; } @@ -1299,6 +1309,12 @@ public class BotEquipmentModGenerator( return modsForSlot; } + /// + /// Get a pool of mods from the default weapon preset for passed in weapon + /// + /// + /// + /// Hashset of mods keyed by slot public HashSet GetModPoolForDefaultSlot(ModToSpawnRequest request, TemplateItem weaponTemplate) { var matchingModFromPreset = GetMatchingModFromPreset(request, weaponTemplate); @@ -1380,7 +1396,13 @@ public class BotEquipmentModGenerator( return request.ItemModPool[request.ModSlot]; } - public Item? GetMatchingModFromPreset(ModToSpawnRequest request, TemplateItem weaponTemplate) + /// + /// Get Desired item from preset + /// + /// + /// + /// + protected Item? GetMatchingModFromPreset(ModToSpawnRequest request, TemplateItem weaponTemplate) { var matchingPreset = GetMatchingPreset(weaponTemplate, request.ParentTemplate.Id); return matchingPreset?.Items?.FirstOrDefault(item => @@ -1393,7 +1415,7 @@ public class BotEquipmentModGenerator( /// Weapons db template /// Tpl of the parent item /// Default preset found - public Preset? GetMatchingPreset(TemplateItem weaponTemplate, string parentItemTpl) + protected Preset? GetMatchingPreset(TemplateItem weaponTemplate, string parentItemTpl) { // Edge case - using mp5sd reciever means default mp5 handguard doesn't fit var isMp5sd = parentItemTpl == "5926f2e086f7745aae644231"; @@ -1493,7 +1515,7 @@ public class BotEquipmentModGenerator( /// /// Check if mod exists in db + is for a required slot /// - /// Db template of mod to check + /// Db template of mod to check /// Slot object the item will be placed as child into /// Slot the mod will fill /// Db template of the mods being added