Various Skill Progress Hideout Adjustments (#739)

* Adjust ArmorKitSkillPointGainPerRepairPointMultiplier evaluation

* Adjust GetWeaponRepairSkillPoints

* adjust WeaponRepair Treatment gain values and intellect gain values

* enable scaling for hideout upgrade

* Adjust HandleRecipe

* adjust skill points addition for PrestigeHelper and RewardHelper

* format

* adjust doc

* adjust doc

* amend some ABI changes

* clarify bool usage

* adjust UpdateFuel, WaterFilters and AirFilters

* add HideoutManagement skill progression to scav case

---------

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
rootdarkarchon
2026-02-18 14:43:56 +01:00
committed by GitHub
parent 8cdce4e54a
commit b8f9ab8646
10 changed files with 82 additions and 37 deletions
@@ -5,7 +5,8 @@
"inRaid": 60, "inRaid": 60,
"outOfRaid": 10 "outOfRaid": 10
}, },
"expCraftAmount": 10, "craftingExpAmount": 12.5,
"craftingExpForHoursOfCrafting": 3.75,
"overrideCraftTimeSeconds": -1, "overrideCraftTimeSeconds": -1,
"overrideBuildTimeSeconds": -1, "overrideBuildTimeSeconds": -1,
"updateProfileHideoutWhenActiveWithinMinutes": 90, "updateProfileHideoutWhenActiveWithinMinutes": 90,
@@ -1,17 +1,17 @@
{ {
"priceMultiplier": 1, "priceMultiplier": 1,
"applyRandomizeDurabilityLoss": true, "applyRandomizeDurabilityLoss": true,
"armorKitSkillPointGainPerRepairPointMultiplier": 0.05, "armorKitSkillPointGainPerRepairPointMultiplier": 0.1,
"repairKitIntellectGainMultiplier": { "repairKitIntellectGainMultiplier": {
"weapon": 0.111, "weapon": 0.1,
"armor": 0.077 "armor": 0.1
}, },
"weaponTreatment": { "weaponTreatment": {
"critSuccessChance": 0.1, "critSuccessChance": 0.1,
"critSuccessAmount": 4, "critSuccessAmount": 4,
"critFailureChance": 0.1, "critFailureChance": 0.1,
"critFailureAmount": 4, "critFailureAmount": 4,
"pointGainMultiplier": 0.6 "pointGainMultiplier": 0.2
}, },
"repairKit": { "repairKit": {
"armor": { "armor": {
@@ -217,7 +217,8 @@ public class HideoutController(
profileHelper.AddSkillPointsToPlayer( profileHelper.AddSkillPointsToPlayer(
pmcData, pmcData,
SkillTypes.HideoutManagement, SkillTypes.HideoutManagement,
globals.Configuration.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade globals.Configuration.SkillsSettings.HideoutManagement.SkillPointsPerAreaUpgrade,
true
); );
} }
@@ -687,8 +688,9 @@ public class HideoutController(
); );
pmcData.Hideout.Production[request.RecipeId].SptIsScavCase = true; pmcData.Hideout.Production[request.RecipeId].SptIsScavCase = true;
// reward charisma based on skill progress rate for each scav production start // reward charisma and hideout management based on skill progress rate for each scav production start
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Charisma, 1, true); profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Charisma, 1, true);
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 1, true);
return output; return output;
} }
@@ -822,7 +824,7 @@ public class HideoutController(
} }
// Variables for management of skill // Variables for management of skill
var craftingExpAmount = 0; double craftingExpAmount = 0;
var counterHoursCrafting = GetCustomSptHoursCraftingTaskConditionCounter(pmcData, recipe); var counterHoursCrafting = GetCustomSptHoursCraftingTaskConditionCounter(pmcData, recipe);
var totalCraftingHours = counterHoursCrafting.Value; var totalCraftingHours = counterHoursCrafting.Value;
@@ -863,19 +865,19 @@ public class HideoutController(
// Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times // Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times
var area = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == recipe.AreaType); var area = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == recipe.AreaType);
if (area is not null && request.RecipeId != area.LastRecipe) if (area is not null && request.RecipeId != area.LastRecipe)
// 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module // 5 points per craft upon the end of production for alternating between 2 different crafting recipes in the same module
{ {
craftingExpAmount += HideoutConfig.ExpCraftAmount; // Default is 10 craftingExpAmount += HideoutConfig.CraftingExpAmount; // Default is 12.5, scaled (at 0.4 scale => 5 points per alternating craft)
} }
// Update variable with time spent crafting item(s) // Update variable with time spent crafting item(s)
// 1 point per 8 hours of crafting // 1.5 (3.75 w/ applying default 0.4 scale) points per 8 hours of crafting
totalCraftingHours += recipe.ProductionTime; totalCraftingHours += recipe.ProductionTime;
if (totalCraftingHours / HideoutConfig.HoursForSkillCrafting >= 1) if (totalCraftingHours / HideoutConfig.HoursForSkillCrafting >= 1)
{ {
// Spent enough time crafting to get a bonus xp multiplier // Spent enough time crafting to get a bonus xp multiplier
var multiplierCrafting = Math.Floor(totalCraftingHours.Value / HideoutConfig.HoursForSkillCrafting); var multiplierCrafting = Math.Floor(totalCraftingHours.Value / HideoutConfig.HoursForSkillCrafting);
craftingExpAmount += (int)(1 * multiplierCrafting); craftingExpAmount += (HideoutConfig.CraftingExpForHoursOfCrafting * multiplierCrafting);
totalCraftingHours -= HideoutConfig.HoursForSkillCrafting * multiplierCrafting; totalCraftingHours -= HideoutConfig.HoursForSkillCrafting * multiplierCrafting;
} }
@@ -943,12 +945,18 @@ public class HideoutController(
// Add Crafting skill to player profile // Add Crafting skill to player profile
if (craftingExpAmount > 0) if (craftingExpAmount > 0)
{ {
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Crafting, craftingExpAmount); profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Crafting, craftingExpAmount, true);
// TODO: verify this is still giving intellect skill points on live
var intellectAmountToGive = 0.5 * Math.Round((double)(craftingExpAmount / 15)); var intellectAmountToGive = 0.5 * Math.Round((double)(craftingExpAmount / 15));
if (intellectAmountToGive > 0) if (intellectAmountToGive > 0)
{ {
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Intellect, intellectAmountToGive); profileHelper.AddSkillPointsToPlayer(
pmcData,
SkillTypes.Intellect,
intellectAmountToGive,
useSkillProgressRateMultiplier: false
);
} }
} }
@@ -277,7 +277,13 @@ public class InventoryController(
} }
// TODO: update this with correct calculation using values from globals json // TODO: update this with correct calculation using values from globals json
profileHelper.AddSkillPointsToPlayer(fullProfile.CharacterData.PmcData, SkillTypes.Intellect, 0.05 * itemTpls.Count()); // TODO: verify this is still giving intellect skill points on live
profileHelper.AddSkillPointsToPlayer(
fullProfile.CharacterData.PmcData,
SkillTypes.Intellect,
0.05 * itemTpls.Count(),
useSkillProgressRateMultiplier: false
);
} }
/// <summary> /// <summary>
@@ -723,7 +723,7 @@ public class HideoutHelper(
// Fuel consumed / 10 is over 1, add hideout management skill point // Fuel consumed / 10 is over 1, add hideout management skill point
if (pmcData is not null && Math.Floor(pointsConsumed / 10) >= 1) if (pmcData is not null && Math.Floor(pointsConsumed / 10) >= 1)
{ {
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 1); profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 2, useSkillProgressRateMultiplier: true);
pointsConsumed -= 10; pointsConsumed -= 10;
} }
@@ -925,7 +925,7 @@ public class HideoutHelper(
// Check units consumed for possible increment of hideout mgmt skill point // Check units consumed for possible increment of hideout mgmt skill point
if (pmcData is not null && Math.Floor(pointsConsumed / 10) >= 1) if (pmcData is not null && Math.Floor(pointsConsumed / 10) >= 1)
{ {
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 1); profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 2, useSkillProgressRateMultiplier: true);
pointsConsumed -= 10; pointsConsumed -= 10;
} }
@@ -1076,7 +1076,7 @@ public class HideoutHelper(
// check unit consumed for increment skill point // check unit consumed for increment skill point
if (pmcData is not null && Math.Floor(pointsConsumed / 10) >= 1) if (pmcData is not null && Math.Floor(pointsConsumed / 10) >= 1)
{ {
profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 1); profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.HideoutManagement, 2, useSkillProgressRateMultiplier: true);
pointsConsumed -= 10; pointsConsumed -= 10;
} }
@@ -195,7 +195,14 @@ public class PrestigeHelper(
case RewardType.Skill: case RewardType.Skill:
if (Enum.TryParse(reward.Target, out SkillTypes result)) if (Enum.TryParse(reward.Target, out SkillTypes result))
{ {
profileHelper.AddSkillPointsToPlayer(newProfile.CharacterData!.PmcData!, result, reward.Value.GetValueOrDefault(0)); // skill reward values are always 100 (+1 level), so adjustment for low levels will give a wrong result
profileHelper.AddSkillPointsToPlayer(
newProfile.CharacterData!.PmcData!,
result,
reward.Value.GetValueOrDefault(0),
useSkillProgressRateMultiplier: false,
adjustSkillExpForLowLevels: false
);
} }
else else
{ {
@@ -461,7 +461,7 @@ public class ProfileHelper(
} }
/// <summary> /// <summary>
/// Add points to a specific skill in player profile /// Add points to a specific skill in player profile, adjusted for low levels by default
/// </summary> /// </summary>
/// <param name="pmcProfile">Player profile with skill</param> /// <param name="pmcProfile">Player profile with skill</param>
/// <param name="skill">Skill to add points to</param> /// <param name="skill">Skill to add points to</param>
@@ -473,6 +473,25 @@ public class ProfileHelper(
double pointsToAddToSkill, double pointsToAddToSkill,
bool useSkillProgressRateMultiplier = false bool useSkillProgressRateMultiplier = false
) )
{
AddSkillPointsToPlayer(pmcProfile, skill, pointsToAddToSkill, useSkillProgressRateMultiplier, true);
}
/// <summary>
/// Add points to a specific skill in player profile
/// </summary>
/// <param name="pmcProfile">Player profile with skill</param>
/// <param name="skill">Skill to add points to</param>
/// <param name="pointsToAddToSkill">Points to add</param>
/// <param name="useSkillProgressRateMultiplier">Skills are multiplied by a value in globals, default is off to maintain compatibility with legacy code</param>
/// <param name="adjustSkillExpForLowLevels">Skills are multiplied by a multiplier for lower levels; if false, treats every level as requiring 100 points</param>
public void AddSkillPointsToPlayer(
PmcData pmcProfile,
SkillTypes skill,
double pointsToAddToSkill,
bool useSkillProgressRateMultiplier = false,
bool adjustSkillExpForLowLevels = true
)
{ {
if (pointsToAddToSkill < 0D) if (pointsToAddToSkill < 0D)
{ {
@@ -517,7 +536,9 @@ public class ProfileHelper(
pointsToAddToSkill *= multiplier; pointsToAddToSkill *= multiplier;
} }
var adjustedSkillProgress = AdjustSkillExpForLowLevels(profileSkill.Progress, pointsToAddToSkill); var adjustedSkillProgress = adjustSkillExpForLowLevels
? AdjustSkillExpForLowLevels(profileSkill.Progress, pointsToAddToSkill)
: pointsToAddToSkill;
profileSkill.Progress += adjustedSkillProgress; profileSkill.Progress += adjustedSkillProgress;
profileSkill.Progress = Math.Min(profileSkill.Progress, 5100); // Prevent skill from ever going above level 51 (5100) profileSkill.Progress = Math.Min(profileSkill.Progress, 5100); // Prevent skill from ever going above level 51 (5100)
@@ -73,10 +73,13 @@ public class RewardHelper(
{ {
case RewardType.Skill: case RewardType.Skill:
// This needs to use the passed in profileData, as it could be the scav profile // This needs to use the passed in profileData, as it could be the scav profile
// skill reward values are always 100 (+1 level), so adjustment for low levels will give a wrong result
profileHelper.AddSkillPointsToPlayer( profileHelper.AddSkillPointsToPlayer(
profileData, profileData,
Enum.Parse<SkillTypes>(reward.Target), Enum.Parse<SkillTypes>(reward.Target),
reward.Value.GetValueOrDefault(0) reward.Value.GetValueOrDefault(0),
useSkillProgressRateMultiplier: false,
adjustSkillExpForLowLevels: false
); );
break; break;
case RewardType.Experience: case RewardType.Experience:
@@ -24,8 +24,14 @@ public record HideoutConfig : BaseConfig
[JsonPropertyName("hoursForSkillCrafting")] [JsonPropertyName("hoursForSkillCrafting")]
public int HoursForSkillCrafting { get; set; } public int HoursForSkillCrafting { get; set; }
[JsonPropertyName("expCraftAmount")] [Obsolete("Will be removed in 4.1, use CraftingExpAmount")]
public int ExpCraftAmount { get; set; } public int ExpCraftAmount { get; set; } = 0;
[JsonPropertyName("craftingExpAmount")]
public double CraftingExpAmount { get; set; }
[JsonPropertyName("craftingExpForHoursOfCrafting")]
public double CraftingExpForHoursOfCrafting { get; set; }
[JsonPropertyName("overrideCraftTimeSeconds")] [JsonPropertyName("overrideCraftTimeSeconds")]
public int OverrideCraftTimeSeconds { get; set; } public int OverrideCraftTimeSeconds { get; set; }
@@ -185,10 +185,12 @@ public class RepairService(
); );
} }
// Every 10 points of repair gives 1 skill point scaled by skillProgressRate
// ArmorKitSkillPointGainPerRepairPointMultiplier is 0.1
var pointsToAddToVestSkill = repairDetails.RepairPoints * RepairConfig.ArmorKitSkillPointGainPerRepairPointMultiplier; var pointsToAddToVestSkill = repairDetails.RepairPoints * RepairConfig.ArmorKitSkillPointGainPerRepairPointMultiplier;
logger.Debug($"Added: {pointsToAddToVestSkill} {vestSkillToLevel} skill"); logger.Debug($"Added: {pointsToAddToVestSkill} {vestSkillToLevel} skill");
profileHelper.AddSkillPointsToPlayer(pmcData, vestSkillToLevel, pointsToAddToVestSkill.GetValueOrDefault(0)); profileHelper.AddSkillPointsToPlayer(pmcData, vestSkillToLevel, pointsToAddToVestSkill.GetValueOrDefault(0), true);
} }
// Handle trader repair - gives charisma based on (repair cost/10 * skill progress rate) // Handle trader repair - gives charisma based on (repair cost/10 * skill progress rate)
@@ -245,18 +247,9 @@ public class RepairService(
protected double GetWeaponRepairSkillPoints(RepairDetails repairDetails) protected double GetWeaponRepairSkillPoints(RepairDetails repairDetails)
{ {
var random = new Random(); var random = new Random();
// This formula and associated configs is calculated based on 30 repairs done on live // Every 5 points repaired with kit should give 0.4 skill points, so PointGainMultiplier is 0.2
// The points always came out 2-aligned, which is why there's a divide/multiply by 2 with ceil calls // The return value is later scaled in AddSkillPointsToPlayer, i.e. 1 skill point returned here = 0.4 skill points added
var gainMult = RepairConfig.WeaponTreatment.PointGainMultiplier; var skillPoints = repairDetails.RepairAmount.GetValueOrDefault(0) * RepairConfig.WeaponTreatment.PointGainMultiplier;
// First we get a baseline based on our repair amount, and gain multiplier with a bit of rounding
var step1 = Math.Ceiling(repairDetails.RepairAmount.Value / 2) * gainMult;
// Then we have to get the next even number
var step2 = Math.Ceiling(step1 / 2) * 2;
// Then multiply by 2 again to hopefully get to what live would give us
var skillPoints = step2 * 2;
// You can both crit fail and succeed at the same time, for fun (Balances out to 0 with default settings) // You can both crit fail and succeed at the same time, for fun (Balances out to 0 with default settings)
// Add a random chance to crit-fail // Add a random chance to crit-fail