From a66b6644a12274424531f68bb7a74368f8077814 Mon Sep 17 00:00:00 2001 From: Dev Date: Sat, 30 Mar 2024 11:32:14 +0000 Subject: [PATCH] Add crafting skill into water filter drain rate calculation Added small optimisation to only process water filters if a water filter is installed Added crafting skill bonus to water filter craft time calculation Various variable/comment renames for clarity --- project/src/controllers/HideoutController.ts | 8 +- project/src/helpers/HideoutHelper.ts | 139 ++++++++++++++----- project/src/helpers/ProfileHelper.ts | 4 +- 3 files changed, 116 insertions(+), 35 deletions(-) diff --git a/project/src/controllers/HideoutController.ts b/project/src/controllers/HideoutController.ts index 889fdee6..564bdd41 100644 --- a/project/src/controllers/HideoutController.ts +++ b/project/src/controllers/HideoutController.ts @@ -678,7 +678,13 @@ export class HideoutController // - normal recipe: Production time value is stored in attribute "productionTime" with small "p" // - scav case recipe: Production time value is stored in attribute "ProductionTime" with capital "P" const adjustedCraftTime = recipe.ProductionTime - - this.hideoutHelper.getCraftingSkillProductionTimeReduction(pmcData, recipe.ProductionTime); + - this.hideoutHelper.getSkillProductionTimeReduction( + pmcData, + recipe.ProductionTime, + SkillTypes.CRAFTING, + this.databaseServer.getTables().globals.config.SkillsSettings.Crafting.CraftTimeReductionPerLevel, + ); + const modifiedScavCaseTime = this.getScavCaseTime(pmcData, adjustedCraftTime); pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction( diff --git a/project/src/helpers/HideoutHelper.ts b/project/src/helpers/HideoutHelper.ts index 2d1b686a..7793db80 100644 --- a/project/src/helpers/HideoutHelper.ts +++ b/project/src/helpers/HideoutHelper.ts @@ -221,14 +221,14 @@ export class HideoutHelper pmcData: IPmcData, ): { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; } { - const bitcoinFarm = pmcData.Hideout.Areas.find((x) => x.type === HideoutAreas.BITCOIN_FARM); + const bitcoinFarm = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.BITCOIN_FARM); const bitcoinCount = bitcoinFarm?.slots.filter((slot) => slot.item).length ?? 0; // Get slots with an item property const hideoutProperties = { btcFarmCGs: bitcoinCount, - isGeneratorOn: pmcData.Hideout.Areas.find((x) => x.type === HideoutAreas.GENERATOR)?.active ?? false, + isGeneratorOn: pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.GENERATOR)?.active ?? false, waterCollectorHasFilter: this.doesWaterCollectorHaveFilter( - pmcData.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR), + pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR), ), }; @@ -237,10 +237,11 @@ export class HideoutHelper protected doesWaterCollectorHaveFilter(waterCollector: HideoutArea): boolean { + // Can put filters in from L3 if (waterCollector.level === 3) - { // can put filters in from L3 + { // Has filter in at least one slot - return waterCollector.slots.some((x) => x.item); + return waterCollector.slots.some((slot) => slot.item); } // No Filter @@ -423,7 +424,7 @@ export class HideoutHelper } break; case HideoutAreas.WATER_COLLECTOR: - this.updateWaterCollector(sessionID, pmcData, area, hideoutProperties.isGeneratorOn); + this.updateWaterCollector(sessionID, pmcData, area, hideoutProperties); break; case HideoutAreas.AIR_FILTERING: @@ -544,7 +545,7 @@ export class HideoutHelper sessionId: string, pmcData: IPmcData, area: HideoutArea, - isGeneratorOn: boolean, + hideoutProperties: { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; }, ): void { // Skip water collector when not level 3 (cant collect until 3) @@ -553,14 +554,23 @@ export class HideoutHelper return; } + if (!hideoutProperties.waterCollectorHasFilter) + { + return; + } + // Canister with purified water craft exists - const prod = pmcData.Hideout.Production[HideoutHelper.waterCollector]; - if (prod && this.isProduction(prod)) + const purifiedWaterCraft = pmcData.Hideout.Production[HideoutHelper.waterCollector]; + if (purifiedWaterCraft && this.isProduction(purifiedWaterCraft)) { // Update craft time to account for increases in players craft time skill - prod.ProductionTime = this.getAdjustedCraftTimeWithSkills(pmcData, prod.RecipeId); + purifiedWaterCraft.ProductionTime = this.getAdjustedCraftTimeWithSkills( + pmcData, + purifiedWaterCraft.RecipeId, + true, + ); - this.updateWaterFilters(area, prod, isGeneratorOn, pmcData); + this.updateWaterFilters(area, purifiedWaterCraft, hideoutProperties.isGeneratorOn, pmcData); } else { @@ -582,10 +592,17 @@ export class HideoutHelper * Get craft time and make adjustments to account for dev profile + crafting skill level * @param pmcData Player profile making craft * @param recipeId Recipe being crafted - * @returns + * @param applyHideoutManagementBonus should the hideout mgmt bonus be appled to the calculation + * @returns Items craft time with bonuses subtracted */ - protected getAdjustedCraftTimeWithSkills(pmcData: IPmcData, recipeId: string): number + protected getAdjustedCraftTimeWithSkills( + pmcData: IPmcData, + recipeId: string, + applyHideoutManagementBonus = false, + ): number { + const globalSkillsDb = this.databaseServer.getTables().globals.config.SkillsSettings; + const recipe = this.databaseServer.getTables().hideout.production.find((production) => production._id === recipeId ); @@ -596,9 +613,26 @@ export class HideoutHelper return undefined; } - let modifiedProductionTime = recipe.productionTime - - this.getCraftingSkillProductionTimeReduction(pmcData, recipe.productionTime); + // Seconds to deduct from crafts total time + let timeReductionSeconds = this.getSkillProductionTimeReduction( + pmcData, + recipe.productionTime, + SkillTypes.CRAFTING, + globalSkillsDb.Crafting.ProductionTimeReductionPerLevel, + ); + // Some crafts take into account hideout management, e.g. fuel, water/air filters + if (applyHideoutManagementBonus) + { + timeReductionSeconds += this.getSkillProductionTimeReduction( + pmcData, + recipe.productionTime, + SkillTypes.HIDEOUT_MANAGEMENT, + globalSkillsDb.HideoutManagement.ConsumptionReductionPerLevel, + ); + } + + let modifiedProductionTime = recipe.productionTime - timeReductionSeconds; if (modifiedProductionTime > 0 && this.profileHelper.isDeveloperAccount(pmcData._id)) { modifiedProductionTime = 40; @@ -625,7 +659,7 @@ export class HideoutHelper const productionTime = this.getTotalProductionTimeSeconds(HideoutHelper.waterCollector); const secondsSinceServerTick = this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn); - filterDrainRate = this.getAdjustWaterFilterDrainRate( + filterDrainRate = this.getTimeAdjustedWaterFilterDrainRate( secondsSinceServerTick, productionTime, production.Progress, @@ -708,7 +742,7 @@ export class HideoutHelper * @param baseFilterDrainRate Base drain rate * @returns drain rate (adjusted) */ - protected getAdjustWaterFilterDrainRate( + protected getTimeAdjustedWaterFilterDrainRate( secondsSinceServerTick: number, totalProductionTime: number, productionProgress: number, @@ -716,10 +750,10 @@ export class HideoutHelper ): number { const drainTimeSeconds = secondsSinceServerTick > totalProductionTime - ? (totalProductionTime - productionProgress) // more time passed than prod time, get total minus the current progress + ? (totalProductionTime - productionProgress) // More time passed than prod time, get total minus the current progress : secondsSinceServerTick; - // Multiply drain rate by calculated multiplier + // Multiply base drain rate by time passed return baseFilterDrainRate * drainTimeSeconds; } @@ -730,11 +764,28 @@ export class HideoutHelper */ protected getWaterFilterDrainRate(pmcData: IPmcData): number { + const globalSkillsDb = this.databaseServer.getTables().globals.config.SkillsSettings; + // 100 resources last 8 hrs 20 min, 100/8.33/60/60 = 0.00333 const filterDrainRate = 0.00333; - const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData); - return filterDrainRate * hideoutManagementConsumptionBonus; + const hideoutManagementConsumptionBonus = this.getSkillBonusMultipliedBySkillLevel( + pmcData, + SkillTypes.HIDEOUT_MANAGEMENT, + globalSkillsDb.HideoutManagement.ConsumptionReductionPerLevel, + ); + const craftSkillTimeReductionMultipler = this.getSkillBonusMultipliedBySkillLevel( + pmcData, + SkillTypes.CRAFTING, + globalSkillsDb.Crafting.CraftTimeReductionPerLevel, + ); + + // Never let bonus become 0 + const reductionBonus = hideoutManagementConsumptionBonus + craftSkillTimeReductionMultipler === 0 + ? 1 + : 1 - (hideoutManagementConsumptionBonus + craftSkillTimeReductionMultipler); + + return filterDrainRate * reductionBonus; } /** @@ -1013,22 +1064,46 @@ export class HideoutHelper } /** - * Adjust craft time based on crafting skill level found in player profile + * Get a multipler based on players skill level and value per level + * @param pmcData Player profile + * @param skill Player skill from profile + * @param valuePerLevel Value from globals.config.SkillsSettings - `PerLevel` + * @returns Multipler from 0 to 1 + */ + protected getSkillBonusMultipliedBySkillLevel(pmcData: IPmcData, skill: SkillTypes, valuePerLevel: number): number + { + const profileSkill = this.profileHelper.getSkillFromProfile(pmcData, skill); + if (!profileSkill || profileSkill.Progress === 0) + { + return 0; + } + + // If the level is 51 we need to round it at 50 so on elite you dont get 25.5% + // at level 1 you already get 0.5%, so it goes up until level 50. For some reason the wiki + // says that it caps at level 51 with 25% but as per dump data that is incorrect apparently + let roundedLevel = Math.floor(profileSkill.Progress / 100); + roundedLevel = (roundedLevel === 51) ? roundedLevel - 1 : roundedLevel; + + return (roundedLevel * valuePerLevel) / 100; + } + + /** * @param pmcData Player profile * @param productionTime Time to complete hideout craft in seconds - * @returns Adjusted craft time in seconds + * @param skill Skill bonus to get reduction from + * @param amountPerLevel Skill bonus amount to apply + * @returns Seconds to reduce craft time by */ - public getCraftingSkillProductionTimeReduction(pmcData: IPmcData, productionTime: number): number + public getSkillProductionTimeReduction( + pmcData: IPmcData, + productionTime: number, + skill: SkillTypes, + amountPerLevel: number, + ): number { - const craftingSkill = pmcData.Skills.Common.find((skill) => skill.Id === SkillTypes.CRAFTING); - if (!craftingSkill) - { - return productionTime; - } - const roundedLevel = Math.floor(Math.min(HideoutHelper.maxSkillPoint, craftingSkill.Progress) / 100); - const percentageToDrop = roundedLevel * 0.75; + const skillTimeReductionMultipler = this.getSkillBonusMultipliedBySkillLevel(pmcData, skill, amountPerLevel); - return (productionTime * percentageToDrop) / 100; + return productionTime * skillTimeReductionMultipler; } public isProduction(productive: Productive): productive is Production diff --git a/project/src/helpers/ProfileHelper.ts b/project/src/helpers/ProfileHelper.ts index dde48ecc..2ecdb3e8 100644 --- a/project/src/helpers/ProfileHelper.ts +++ b/project/src/helpers/ProfileHelper.ts @@ -495,12 +495,12 @@ export class ProfileHelper /** * Get a speciic common skill from supplied profile * @param pmcData Player profile - * @param skill Skill get get + * @param skill Skill to look up and return value from * @returns Common skill object from desired profile */ public getSkillFromProfile(pmcData: IPmcData, skill: SkillTypes): Common { - const skillToReturn = pmcData.Skills.Common.find((x) => x.Id === skill); + const skillToReturn = pmcData.Skills.Common.find((commonSkill) => commonSkill.Id === skill); if (!skillToReturn) { this.logger.warning(`Profile ${pmcData.sessionId} does not have a skill named: ${skill}`);