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
This commit is contained in:
Dev 2024-03-30 11:32:14 +00:00
parent 50f580858d
commit a66b6644a1
3 changed files with 116 additions and 35 deletions

View File

@ -678,7 +678,13 @@ export class HideoutController
// - normal recipe: Production time value is stored in attribute "productionTime" with small "p" // - 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" // - scav case recipe: Production time value is stored in attribute "ProductionTime" with capital "P"
const adjustedCraftTime = recipe.ProductionTime 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); const modifiedScavCaseTime = this.getScavCaseTime(pmcData, adjustedCraftTime);
pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction( pmcData.Hideout.Production[body.recipeId] = this.hideoutHelper.initProduction(

View File

@ -221,14 +221,14 @@ export class HideoutHelper
pmcData: IPmcData, pmcData: IPmcData,
): { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; } ): { 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 bitcoinCount = bitcoinFarm?.slots.filter((slot) => slot.item).length ?? 0; // Get slots with an item property
const hideoutProperties = { const hideoutProperties = {
btcFarmCGs: bitcoinCount, 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( 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 protected doesWaterCollectorHaveFilter(waterCollector: HideoutArea): boolean
{ {
// Can put filters in from L3
if (waterCollector.level === 3) if (waterCollector.level === 3)
{ // can put filters in from L3 {
// Has filter in at least one slot // Has filter in at least one slot
return waterCollector.slots.some((x) => x.item); return waterCollector.slots.some((slot) => slot.item);
} }
// No Filter // No Filter
@ -423,7 +424,7 @@ export class HideoutHelper
} }
break; break;
case HideoutAreas.WATER_COLLECTOR: case HideoutAreas.WATER_COLLECTOR:
this.updateWaterCollector(sessionID, pmcData, area, hideoutProperties.isGeneratorOn); this.updateWaterCollector(sessionID, pmcData, area, hideoutProperties);
break; break;
case HideoutAreas.AIR_FILTERING: case HideoutAreas.AIR_FILTERING:
@ -544,7 +545,7 @@ export class HideoutHelper
sessionId: string, sessionId: string,
pmcData: IPmcData, pmcData: IPmcData,
area: HideoutArea, area: HideoutArea,
isGeneratorOn: boolean, hideoutProperties: { btcFarmCGs: number; isGeneratorOn: boolean; waterCollectorHasFilter: boolean; },
): void ): void
{ {
// Skip water collector when not level 3 (cant collect until 3) // Skip water collector when not level 3 (cant collect until 3)
@ -553,14 +554,23 @@ export class HideoutHelper
return; return;
} }
if (!hideoutProperties.waterCollectorHasFilter)
{
return;
}
// Canister with purified water craft exists // Canister with purified water craft exists
const prod = pmcData.Hideout.Production[HideoutHelper.waterCollector]; const purifiedWaterCraft = pmcData.Hideout.Production[HideoutHelper.waterCollector];
if (prod && this.isProduction(prod)) if (purifiedWaterCraft && this.isProduction(purifiedWaterCraft))
{ {
// Update craft time to account for increases in players craft time skill // 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 else
{ {
@ -582,10 +592,17 @@ export class HideoutHelper
* Get craft time and make adjustments to account for dev profile + crafting skill level * Get craft time and make adjustments to account for dev profile + crafting skill level
* @param pmcData Player profile making craft * @param pmcData Player profile making craft
* @param recipeId Recipe being crafted * @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) => const recipe = this.databaseServer.getTables().hideout.production.find((production) =>
production._id === recipeId production._id === recipeId
); );
@ -596,9 +613,26 @@ export class HideoutHelper
return undefined; return undefined;
} }
let modifiedProductionTime = recipe.productionTime // Seconds to deduct from crafts total time
- this.getCraftingSkillProductionTimeReduction(pmcData, recipe.productionTime); 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)) if (modifiedProductionTime > 0 && this.profileHelper.isDeveloperAccount(pmcData._id))
{ {
modifiedProductionTime = 40; modifiedProductionTime = 40;
@ -625,7 +659,7 @@ export class HideoutHelper
const productionTime = this.getTotalProductionTimeSeconds(HideoutHelper.waterCollector); const productionTime = this.getTotalProductionTimeSeconds(HideoutHelper.waterCollector);
const secondsSinceServerTick = this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn); const secondsSinceServerTick = this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
filterDrainRate = this.getAdjustWaterFilterDrainRate( filterDrainRate = this.getTimeAdjustedWaterFilterDrainRate(
secondsSinceServerTick, secondsSinceServerTick,
productionTime, productionTime,
production.Progress, production.Progress,
@ -708,7 +742,7 @@ export class HideoutHelper
* @param baseFilterDrainRate Base drain rate * @param baseFilterDrainRate Base drain rate
* @returns drain rate (adjusted) * @returns drain rate (adjusted)
*/ */
protected getAdjustWaterFilterDrainRate( protected getTimeAdjustedWaterFilterDrainRate(
secondsSinceServerTick: number, secondsSinceServerTick: number,
totalProductionTime: number, totalProductionTime: number,
productionProgress: number, productionProgress: number,
@ -716,10 +750,10 @@ export class HideoutHelper
): number ): number
{ {
const drainTimeSeconds = secondsSinceServerTick > totalProductionTime 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; : secondsSinceServerTick;
// Multiply drain rate by calculated multiplier // Multiply base drain rate by time passed
return baseFilterDrainRate * drainTimeSeconds; return baseFilterDrainRate * drainTimeSeconds;
} }
@ -730,11 +764,28 @@ export class HideoutHelper
*/ */
protected getWaterFilterDrainRate(pmcData: IPmcData): number 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 // 100 resources last 8 hrs 20 min, 100/8.33/60/60 = 0.00333
const filterDrainRate = 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 pmcData Player profile
* @param productionTime Time to complete hideout craft in seconds * @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); const skillTimeReductionMultipler = this.getSkillBonusMultipliedBySkillLevel(pmcData, skill, amountPerLevel);
if (!craftingSkill)
{
return productionTime;
}
const roundedLevel = Math.floor(Math.min(HideoutHelper.maxSkillPoint, craftingSkill.Progress) / 100);
const percentageToDrop = roundedLevel * 0.75;
return (productionTime * percentageToDrop) / 100; return productionTime * skillTimeReductionMultipler;
} }
public isProduction(productive: Productive): productive is Production public isProduction(productive: Productive): productive is Production

View File

@ -495,12 +495,12 @@ export class ProfileHelper
/** /**
* Get a speciic common skill from supplied profile * Get a speciic common skill from supplied profile
* @param pmcData Player 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 * @returns Common skill object from desired profile
*/ */
public getSkillFromProfile(pmcData: IPmcData, skill: SkillTypes): Common 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) if (!skillToReturn)
{ {
this.logger.warning(`Profile ${pmcData.sessionId} does not have a skill named: ${skill}`); this.logger.warning(`Profile ${pmcData.sessionId} does not have a skill named: ${skill}`);