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