using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums;
namespace SPTarkov.Server.Core.Extensions;
public static class ProfileExtensions
{
///
/// Return all quest items current in the supplied profile
///
/// Profile to get quest items from
/// List of item objects
public static IEnumerable- GetQuestItemsInProfile(this PmcData profile)
{
return profile?.Inventory?.Items.Where(i => i.ParentId == profile.Inventory.QuestRaidItems).ToList();
}
///
/// Upgrade hideout wall from starting level to interactable level if necessary stations have been upgraded
///
/// Profile to upgrade wall in
public static void UnlockHideoutWallInProfile(this PmcData profile)
{
var profileHideoutAreas = profile.Hideout.Areas;
var waterCollector = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.WaterCollector);
var medStation = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.MedStation);
var wall = profileHideoutAreas.FirstOrDefault(x => x.Type == HideoutAreas.EmergencyWall);
// No collector or med station, skip
if (waterCollector is null && medStation is null)
{
return;
}
// If med-station > level 1 AND water collector > level 1 AND wall is level 0
if (waterCollector?.Level >= 1 && medStation?.Level >= 1 && wall?.Level <= 0)
{
wall.Level = 3;
}
}
///
/// Does the provided profile contain any condition counters
///
/// Profile to check for condition counters
/// Profile has condition counters
public static bool ProfileHasConditionCounters(this PmcData profile)
{
if (profile.TaskConditionCounters is null)
{
return false;
}
return profile.TaskConditionCounters.Count > 0;
}
///
/// Get a specific common skill from supplied profile
///
/// Player profile
/// Skill to look up and return value from
/// Common skill object from desired profile
public static CommonSkill? GetSkillFromProfile(this PmcData profile, SkillTypes skill)
{
return profile?.Skills?.Common?.FirstOrDefault(s => s.Id == skill);
}
///
/// Get the scav karma level for a profile
/// Is also the fence trader rep level
///
/// pmc profile
/// karma level
public static double GetScavKarmaLevel(this PmcData pmcData)
{
// can be empty during profile creation
if (!pmcData.TradersInfo.TryGetValue(Traders.FENCE, out var fenceInfo))
{
return 0;
}
if (fenceInfo.Standing > 6)
{
return 6;
}
return Math.Floor(fenceInfo.Standing ?? 0);
}
public static Skills GetSkillsOrDefault(this PmcData profile)
{
return profile?.Skills ?? GetDefaultSkills();
}
private static Skills GetDefaultSkills()
{
return new Skills
{
Common = [],
Mastering = [],
Points = 0,
};
}
///
/// Recursively checks if the given item is
/// inside the stash, that is it has the stash as
/// ancestor with slotId=hideout
///
/// Player profile
/// Item to look for
/// True if item exists inside stash
public static bool IsItemInStash(this PmcData pmcData, Item itemToCheck)
{
// Start recursive check
return pmcData.IsParentInStash(itemToCheck.Id);
}
public static bool IsParentInStash(this PmcData pmcData, MongoId itemId)
{
// Item not found / has no parent
var item = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == itemId);
if (item?.ParentId is null)
{
return false;
}
// Root level. Items parent is the stash with slotId "hideout"
if (item.ParentId == pmcData.Inventory.Stash && item.SlotId == "hideout")
{
return true;
}
// Recursive case: Check the items parent
return IsParentInStash(pmcData, item.ParentId);
}
///
/// Iterate over all bonuses and sum up all bonuses of desired type in provided profile
///
/// Player profile
/// Bonus to sum up
/// Summed bonus value or 0 if no bonus found
public static double GetBonusValueFromProfile(this PmcData pmcProfile, BonusType desiredBonus)
{
var bonuses = pmcProfile?.Bonuses?.Where(b => b.Type == desiredBonus);
if (!bonuses.Any())
{
return 0;
}
// Sum all bonuses found above
return bonuses?.Sum(bonus => bonus?.Value ?? 0) ?? 0;
}
public static bool PlayerIsFleaBanned(this PmcData pmcProfile, long currentTimestamp)
{
return pmcProfile?.Info?.Bans?.Any(b => b.BanType == BanType.RagFair && currentTimestamp < b.DateTime) ?? false;
}
///
/// Calculates the current level of a player based on their accumulated experience points.
/// This method iterates through an experience table to determine the highest level achieved
/// by comparing the player's experience against cumulative thresholds.
///
/// Player profile
/// Experience table from globals.json
///
/// The calculated level of the player as an integer, or null if the level cannot be determined.
/// This value is also assigned to within the provided profile.
///
public static int? CalculateLevel(this PmcData pmcData, ExpTable[] expTable)
{
var accExp = 0;
for (var i = 0; i < expTable.Length; i++)
{
accExp += expTable[i].Experience;
if (pmcData.Info.Experience < accExp)
{
break;
}
pmcData.Info.Level = i + 1;
}
return pmcData.Info.Level;
}
///
/// Does the provided item have a root item with the provided id
///
/// Profile with items
/// Item to check
/// Root item id to check for
/// True when item has rootId, false when not
public static bool DoesItemHaveRootId(this PmcData pmcData, Item item, MongoId rootId)
{
var currentItem = item;
while (currentItem is not null)
{
// If we've found the equipment root ID, return true
if (currentItem.Id == rootId)
{
return true;
}
// Otherwise get the parent item
currentItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == currentItem.ParentId);
}
return false;
}
///
/// Get status of a quest in player profile by its id
///
/// Profile to search
/// Quest id to look up
/// QuestStatus enum
public static QuestStatusEnum GetQuestStatus(this PmcData pmcData, MongoId questId)
{
var quest = pmcData.Quests?.FirstOrDefault(q => q.QId == questId);
return quest?.Status ?? QuestStatusEnum.Locked;
}
///
/// Use values from the profiles template to reset all body part max values
///
/// Profile to update
/// Template used to create profile
public static void ResetMaxLimbHp(this PmcData profile, TemplateSide profileTemplate)
{
foreach (var (partKey, bodyPart) in profile.Health.BodyParts)
{
bodyPart.Health.Maximum = profileTemplate.Character.Health.BodyParts[partKey].Health.Maximum;
}
}
}