f6192f1891
Added the vest and headwear classes to be eligible for armor enhancement
Co-authored-by: Snow <Notarealemail@email.com>
Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/200
Co-authored-by: snowythefox811 <snowythefox811@noreply.dev.sp-tarkov.com>
Co-committed-by: snowythefox811 <snowythefox811@noreply.dev.sp-tarkov.com>
(cherry picked from commit 35eb9feebf
)
596 lines
24 KiB
TypeScript
596 lines
24 KiB
TypeScript
import { inject, injectable } from "tsyringe";
|
|
|
|
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
|
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
|
import { RepairHelper } from "@spt-aki/helpers/RepairHelper";
|
|
import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
|
|
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
|
|
import { IArmorType } from "@spt-aki/models/eft/common/IGlobals";
|
|
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
|
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
|
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
|
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
|
import { RepairKitsInfo } from "@spt-aki/models/eft/repair/IRepairActionDataRequest";
|
|
import { RepairItem } from "@spt-aki/models/eft/repair/ITraderRepairActionDataRequest";
|
|
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
|
|
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
|
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
|
import { SkillTypes } from "@spt-aki/models/enums/SkillTypes";
|
|
import { BonusSettings, IRepairConfig } from "@spt-aki/models/spt/config/IRepairConfig";
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
import { PaymentService } from "@spt-aki/services/PaymentService";
|
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
|
|
|
@injectable()
|
|
export class RepairService
|
|
{
|
|
protected repairConfig: IRepairConfig;
|
|
constructor(
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
|
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
|
|
@inject("PaymentService") protected paymentService: PaymentService,
|
|
@inject("RepairHelper") protected repairHelper: RepairHelper,
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
|
)
|
|
{
|
|
this.repairConfig = this.configServer.getConfig(ConfigTypes.REPAIR);
|
|
}
|
|
|
|
/**
|
|
* Use trader to repair an items durability
|
|
* @param sessionID Session id
|
|
* @param pmcData profile to find item to repair in
|
|
* @param repairItemDetails details of the item to repair
|
|
* @param traderId Trader being used to repair item
|
|
* @returns RepairDetails object
|
|
*/
|
|
public repairItemByTrader(
|
|
sessionID: string,
|
|
pmcData: IPmcData,
|
|
repairItemDetails: RepairItem,
|
|
traderId: string,
|
|
): RepairDetails
|
|
{
|
|
const itemToRepair = pmcData.Inventory.items.find((x) => x._id === repairItemDetails._id);
|
|
if (itemToRepair === undefined)
|
|
{
|
|
throw new Error(`Item ${repairItemDetails._id} not found in profile inventory, unable to repair`);
|
|
}
|
|
|
|
const priceCoef = this.traderHelper.getLoyaltyLevel(traderId, pmcData).repair_price_coef;
|
|
const traderRepairDetails = this.traderHelper.getTrader(traderId, sessionID).repair;
|
|
const repairQualityMultiplier = Number(traderRepairDetails.quality);
|
|
const repairRate = (priceCoef <= 0) ? 1 : (priceCoef / 100 + 1);
|
|
|
|
const itemToRepairDetails = this.databaseServer.getTables().templates.items[itemToRepair._tpl];
|
|
const repairItemIsArmor = !!itemToRepairDetails._props.ArmorMaterial;
|
|
|
|
this.repairHelper.updateItemDurability(
|
|
itemToRepair,
|
|
itemToRepairDetails,
|
|
repairItemIsArmor,
|
|
repairItemDetails.count,
|
|
false,
|
|
repairQualityMultiplier,
|
|
repairQualityMultiplier !== 0 && this.repairConfig.applyRandomizeDurabilityLoss,
|
|
);
|
|
|
|
// get repair price
|
|
const itemRepairCost = this.databaseServer.getTables().templates.items[itemToRepair._tpl]._props.RepairCost;
|
|
const repairCost = Math.round(
|
|
(itemRepairCost * repairItemDetails.count * repairRate) * this.repairConfig.priceMultiplier,
|
|
);
|
|
|
|
this.logger.debug(`item base repair cost: ${itemRepairCost}`, true);
|
|
this.logger.debug(`price multipler: ${this.repairConfig.priceMultiplier}`, true);
|
|
this.logger.debug(`repair cost: ${repairCost}`, true);
|
|
|
|
return {
|
|
repairCost: repairCost,
|
|
repairedItem: itemToRepair,
|
|
repairedItemIsArmor: repairItemIsArmor,
|
|
repairAmount: repairItemDetails.count,
|
|
repairedByKit: false,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param sessionID Session id
|
|
* @param pmcData profile to take money from
|
|
* @param repairedItemId Repaired item id
|
|
* @param repairCost Cost to repair item in roubles
|
|
* @param traderId Id of the trader who repaired the item / who is paid
|
|
* @param output
|
|
*/
|
|
public payForRepair(
|
|
sessionID: string,
|
|
pmcData: IPmcData,
|
|
repairedItemId: string,
|
|
repairCost: number,
|
|
traderId: string,
|
|
output: IItemEventRouterResponse,
|
|
): void
|
|
{
|
|
const options: IProcessBuyTradeRequestData = {
|
|
scheme_items: [{
|
|
id: "5449016a4bdc2d6f028b456f", // Rouble tpl
|
|
count: Math.round(repairCost)
|
|
}],
|
|
tid: traderId,
|
|
Action: "SptRepair",
|
|
type: "",
|
|
item_id: "",
|
|
count: 0,
|
|
scheme_id: 0,
|
|
};
|
|
|
|
this.paymentService.payMoney(pmcData, options, sessionID, output);
|
|
}
|
|
|
|
/**
|
|
* Add skill points to profile after repairing an item
|
|
* @param sessionId Session id
|
|
* @param repairDetails details of item repaired, cost/item
|
|
* @param pmcData Profile to add points to
|
|
*/
|
|
public addRepairSkillPoints(sessionId: string, repairDetails: RepairDetails, pmcData: IPmcData): void
|
|
{
|
|
// Handle kit repair of weapon
|
|
if (
|
|
repairDetails.repairedByKit
|
|
&& this.itemHelper.isOfBaseclass(repairDetails.repairedItem._tpl, BaseClasses.WEAPON)
|
|
)
|
|
{
|
|
const skillPoints = this.getWeaponRepairSkillPoints(repairDetails);
|
|
|
|
if (skillPoints > 0)
|
|
{
|
|
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.WEAPON_TREATMENT, skillPoints, true);
|
|
}
|
|
}
|
|
|
|
// Handle kit repair of armor
|
|
if (
|
|
repairDetails.repairedByKit
|
|
&& this.itemHelper.isOfBaseclasses(repairDetails.repairedItem._tpl, [BaseClasses.ARMOR, BaseClasses.VEST])
|
|
)
|
|
{
|
|
const itemDetails = this.itemHelper.getItem(repairDetails.repairedItem._tpl);
|
|
if (!itemDetails[0])
|
|
{
|
|
// No item found
|
|
this.logger.error(
|
|
this.localisationService.getText(
|
|
"repair-unable_to_find_item_in_db",
|
|
repairDetails.repairedItem._tpl,
|
|
),
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
const isHeavyArmor = itemDetails[1]._props.ArmorType === "Heavy";
|
|
const vestSkillToLevel = isHeavyArmor ? SkillTypes.HEAVY_VESTS : SkillTypes.LIGHT_VESTS;
|
|
const pointsToAddToVestSkill = repairDetails.repairPoints
|
|
* this.repairConfig.armorKitSkillPointGainPerRepairPointMultiplier;
|
|
|
|
this.profileHelper.addSkillPointsToPlayer(pmcData, vestSkillToLevel, pointsToAddToVestSkill);
|
|
}
|
|
|
|
// Handle giving INT to player - differs if using kit/trader and weapon vs armor
|
|
const intellectGainedFromRepair = this.getIntellectGainedFromRepair(repairDetails);
|
|
if (intellectGainedFromRepair > 0)
|
|
{
|
|
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectGainedFromRepair);
|
|
}
|
|
}
|
|
|
|
protected getIntellectGainedFromRepair(repairDetails: RepairDetails): number
|
|
{
|
|
if (repairDetails.repairedByKit)
|
|
{
|
|
// Weapons/armor have different multipliers
|
|
const intRepairMultiplier =
|
|
(this.itemHelper.isOfBaseclass(repairDetails.repairedItem._tpl, BaseClasses.WEAPON))
|
|
? this.repairConfig.repairKitIntellectGainMultiplier.weapon
|
|
: this.repairConfig.repairKitIntellectGainMultiplier.armor;
|
|
|
|
// Limit gain to a max value defined in config.maxIntellectGainPerRepair
|
|
return Math.min(
|
|
repairDetails.repairPoints * intRepairMultiplier,
|
|
this.repairConfig.maxIntellectGainPerRepair.kit,
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Trader repair - Not as accurate as kit, needs data from live
|
|
return Math.min(
|
|
repairDetails.repairAmount / 10,
|
|
this.repairConfig.maxIntellectGainPerRepair.trader,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return an appromixation of the amount of skill points live would return for the given repairDetails
|
|
* @param repairDetails the repair details to calculate skill points for
|
|
* @returns the number of skill points to reward the user
|
|
*/
|
|
protected getWeaponRepairSkillPoints(repairDetails: RepairDetails): number
|
|
{
|
|
// This formula and associated configs is calculated based on 30 repairs done on live
|
|
// The points always came out 2-aligned, which is why there's a divide/multiply by 2 with ceil calls
|
|
const gainMult = this.repairConfig.weaponTreatment.pointGainMultiplier;
|
|
|
|
// First we get a baseline based on our repair amount, and gain multiplier with a bit of rounding
|
|
const step1 = Math.ceil(repairDetails.repairAmount / 2) * gainMult;
|
|
|
|
// Then we have to get the next even number
|
|
const step2 = Math.ceil(step1 / 2) * 2;
|
|
|
|
// Then multiply by 2 again to hopefully get to what live would give us
|
|
let skillPoints = step2 * 2;
|
|
|
|
// 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
|
|
if (Math.random() <= this.repairConfig.weaponTreatment.critFailureChance)
|
|
{
|
|
skillPoints -= this.repairConfig.weaponTreatment.critFailureAmount;
|
|
}
|
|
|
|
// Add a random chance to crit-succeed
|
|
if (Math.random() <= this.repairConfig.weaponTreatment.critSuccessChance)
|
|
{
|
|
skillPoints += this.repairConfig.weaponTreatment.critSuccessAmount;
|
|
}
|
|
|
|
return Math.max(skillPoints, 0);
|
|
}
|
|
|
|
/**
|
|
* @param sessionId Session id
|
|
* @param pmcData Profile to update repaired item in
|
|
* @param repairKits Array of Repair kits to use
|
|
* @param itemToRepairId Item id to repair
|
|
* @param output IItemEventRouterResponse
|
|
* @returns Details of repair, item/price
|
|
*/
|
|
public repairItemByKit(
|
|
sessionId: string,
|
|
pmcData: IPmcData,
|
|
repairKits: RepairKitsInfo[],
|
|
itemToRepairId: string,
|
|
output: IItemEventRouterResponse,
|
|
): RepairDetails
|
|
{
|
|
// Find item to repair in inventory
|
|
const itemToRepair = pmcData.Inventory.items.find((x: { _id: string; }) => x._id === itemToRepairId);
|
|
if (itemToRepair === undefined)
|
|
{
|
|
throw new Error(`Item ${itemToRepairId} not found, unable to repair`);
|
|
}
|
|
|
|
const itemsDb = this.databaseServer.getTables().templates.items;
|
|
const itemToRepairDetails = itemsDb[itemToRepair._tpl];
|
|
const repairItemIsArmor = !!itemToRepairDetails._props.ArmorMaterial;
|
|
const repairAmount = repairKits[0].count / this.getKitDivisor(itemToRepairDetails, repairItemIsArmor, pmcData);
|
|
const shouldApplyDurabilityLoss = this.shouldRepairKitApplyDurabilityLoss(
|
|
pmcData,
|
|
this.repairConfig.applyRandomizeDurabilityLoss,
|
|
);
|
|
|
|
this.repairHelper.updateItemDurability(
|
|
itemToRepair,
|
|
itemToRepairDetails,
|
|
repairItemIsArmor,
|
|
repairAmount,
|
|
true,
|
|
1,
|
|
shouldApplyDurabilityLoss,
|
|
);
|
|
|
|
// Find and use repair kit defined in body
|
|
for (const repairKit of repairKits)
|
|
{
|
|
const repairKitInInventory = pmcData.Inventory.items.find((x) => x._id === repairKit._id);
|
|
const repairKitDetails = itemsDb[repairKitInInventory._tpl];
|
|
const repairKitReductionAmount = repairKit.count;
|
|
|
|
this.addMaxResourceToKitIfMissing(repairKitDetails, repairKitInInventory);
|
|
|
|
// reduce usages on repairkit used
|
|
repairKitInInventory.upd.RepairKit.Resource -= repairKitReductionAmount;
|
|
|
|
output.profileChanges[sessionId].items.change.push(repairKitInInventory);
|
|
}
|
|
|
|
return {
|
|
repairPoints: repairKits[0].count,
|
|
repairedItem: itemToRepair,
|
|
repairedItemIsArmor: repairItemIsArmor,
|
|
repairAmount: repairAmount,
|
|
repairedByKit: true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Calculate value repairkit points need to be divided by to get the durability points to be added to an item
|
|
* @param itemToRepairDetails Item to repair details
|
|
* @param isArmor Is the item being repaired armor
|
|
* @param pmcData Player profile
|
|
* @returns Number to divide kit points by
|
|
*/
|
|
protected getKitDivisor(itemToRepairDetails: ITemplateItem, isArmor: boolean, pmcData: IPmcData): number
|
|
{
|
|
const globals = this.databaseServer.getTables().globals;
|
|
const globalRepairSettings = globals.config.RepairSettings;
|
|
|
|
const intellectRepairPointsPerLevel = globals.config.SkillsSettings.Intellect.RepairPointsCostReduction;
|
|
const profileIntellectLevel = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.INTELLECT)?.Progress
|
|
?? 0;
|
|
const intellectPointReduction = intellectRepairPointsPerLevel * Math.trunc(profileIntellectLevel / 100);
|
|
|
|
if (isArmor)
|
|
{
|
|
const durabilityPointCostArmor = globalRepairSettings.durabilityPointCostArmor;
|
|
const repairArmorBonus = this.getBonusMultiplierValue("RepairArmorBonus", pmcData);
|
|
const armorBonus = 1.0 - (repairArmorBonus - 1.0) - intellectPointReduction;
|
|
const materialType = itemToRepairDetails._props.ArmorMaterial ?? "";
|
|
const armorMaterial = globals.config.ArmorMaterials[materialType] as IArmorType;
|
|
const destructability = 1 + armorMaterial.Destructibility;
|
|
const armorClass = parseInt(`${itemToRepairDetails._props.armorClass}`);
|
|
const armorClassDivisor = globals.config.RepairSettings.armorClassDivisor;
|
|
const armorClassMultiplier = 1.0 + armorClass / armorClassDivisor;
|
|
|
|
return durabilityPointCostArmor * armorBonus * destructability * armorClassMultiplier;
|
|
}
|
|
else
|
|
{
|
|
const repairWeaponBonus = this.getBonusMultiplierValue("RepairWeaponBonus", pmcData) - 1;
|
|
const repairPointMultiplier = 1.0 - repairWeaponBonus - intellectPointReduction;
|
|
const durabilityPointCostGuns = globals.config.RepairSettings.durabilityPointCostGuns;
|
|
|
|
return durabilityPointCostGuns * repairPointMultiplier;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the bonus multiplier for a skill from a player profile
|
|
* @param skillBonusName Name of bonus to get multipler of
|
|
* @param pmcData Player profile to look in for skill
|
|
* @returns Multiplier value
|
|
*/
|
|
protected getBonusMultiplierValue(skillBonusName: string, pmcData: IPmcData): number
|
|
{
|
|
const bonusesMatched = pmcData?.Bonuses?.filter((b) => b.type === skillBonusName);
|
|
let value = 1;
|
|
if (bonusesMatched != null)
|
|
{
|
|
const sumedPercentage = bonusesMatched.map((b) => b.value).reduce((v1, v2) => v1 + v2, 0);
|
|
value = 1 + sumedPercentage / 100;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Should a repair kit apply total durability loss on repair
|
|
* @param pmcData Player profile
|
|
* @param applyRandomizeDurabilityLoss Value from repair config
|
|
* @returns True if loss should be applied
|
|
*/
|
|
protected shouldRepairKitApplyDurabilityLoss(pmcData: IPmcData, applyRandomizeDurabilityLoss: boolean): boolean
|
|
{
|
|
let shouldApplyDurabilityLoss = applyRandomizeDurabilityLoss;
|
|
if (shouldApplyDurabilityLoss)
|
|
{
|
|
// Random loss not disabled via config, perform charisma check
|
|
const hasEliteCharisma = this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData);
|
|
if (hasEliteCharisma)
|
|
{
|
|
// 50/50 chance of loss being ignored at elite level
|
|
shouldApplyDurabilityLoss = this.randomUtil.getChance100(50);
|
|
}
|
|
}
|
|
|
|
return shouldApplyDurabilityLoss;
|
|
}
|
|
|
|
/**
|
|
* Update repair kits Resource object if it doesn't exist
|
|
* @param repairKitDetails Repair kit details from db
|
|
* @param repairKitInInventory Repair kit to update
|
|
*/
|
|
protected addMaxResourceToKitIfMissing(repairKitDetails: ITemplateItem, repairKitInInventory: Item): void
|
|
{
|
|
const maxRepairAmount = repairKitDetails._props.MaxRepairResource;
|
|
if (!repairKitInInventory.upd)
|
|
{
|
|
this.logger.debug(`Repair kit: ${repairKitInInventory._id} in inventory lacks upd object, adding`);
|
|
repairKitInInventory.upd = { RepairKit: { Resource: maxRepairAmount } };
|
|
}
|
|
if (!repairKitInInventory.upd.RepairKit?.Resource)
|
|
{
|
|
repairKitInInventory.upd.RepairKit = { Resource: maxRepairAmount };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Chance to apply buff to an item (Armor/weapon) if repaired by armor kit
|
|
* @param repairDetails Repair details of item
|
|
* @param pmcData Player profile
|
|
*/
|
|
public addBuffToItem(repairDetails: RepairDetails, pmcData: IPmcData): void
|
|
{
|
|
// Buffs are repair kit only
|
|
if (!repairDetails.repairedByKit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this.shouldBuffItem(repairDetails, pmcData))
|
|
{
|
|
if (this.itemHelper.isOfBaseclasses(repairDetails.repairedItem._tpl, [BaseClasses.ARMOR, BaseClasses.VEST, BaseClasses.HEADWEAR]))
|
|
{
|
|
const armorConfig = this.repairConfig.repairKit.armor;
|
|
this.addBuff(armorConfig, repairDetails.repairedItem);
|
|
}
|
|
else if (this.itemHelper.isOfBaseclass(repairDetails.repairedItem._tpl, BaseClasses.WEAPON))
|
|
{
|
|
const weaponConfig = this.repairConfig.repairKit.weapon;
|
|
this.addBuff(weaponConfig, repairDetails.repairedItem);
|
|
}
|
|
// TODO: Knife repair kits may be added at some point, a bracket needs to be added here
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add random buff to item
|
|
* @param itemConfig weapon/armor config
|
|
* @param repairDetails Details for item to repair
|
|
*/
|
|
public addBuff(itemConfig: BonusSettings, item: Item): void
|
|
{
|
|
const bonusRarity = this.weightedRandomHelper.getWeightedValue<string>(itemConfig.rarityWeight);
|
|
const bonusType = this.weightedRandomHelper.getWeightedValue<string>(itemConfig.bonusTypeWeight);
|
|
|
|
const bonusValues = itemConfig[bonusRarity][bonusType].valuesMinMax;
|
|
const bonusValue = this.randomUtil.getFloat(bonusValues.min, bonusValues.max);
|
|
|
|
const bonusThresholdPercents = itemConfig[bonusRarity][bonusType].activeDurabilityPercentMinMax;
|
|
const bonusThresholdPercent = this.randomUtil.getInt(bonusThresholdPercents.min, bonusThresholdPercents.max);
|
|
|
|
item.upd.Buff = {
|
|
rarity: bonusRarity,
|
|
buffType: bonusType,
|
|
value: bonusValue,
|
|
thresholdDurability: this.randomUtil.getPercentOfValue(
|
|
bonusThresholdPercent,
|
|
item.upd.Repairable.Durability,
|
|
),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Check if item should be buffed by checking the item type and relevant player skill level
|
|
* @param repairDetails Item that was repaired
|
|
* @param itemTpl tpl of item to be buffed
|
|
* @param pmcData Player profile
|
|
* @returns True if item should have buff applied
|
|
*/
|
|
protected shouldBuffItem(repairDetails: RepairDetails, pmcData: IPmcData): boolean
|
|
{
|
|
const globals = this.databaseServer.getTables().globals;
|
|
|
|
const hasTemplate = this.itemHelper.getItem(repairDetails.repairedItem._tpl);
|
|
if (!hasTemplate[0])
|
|
{
|
|
return false;
|
|
}
|
|
const template = hasTemplate[1];
|
|
|
|
const itemSkillType = this.getItemSkillType(template);
|
|
if (!itemSkillType)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const commonBuffMinChanceValue =
|
|
globals.config.SkillsSettings[itemSkillType as string].BuffSettings.CommonBuffMinChanceValue;
|
|
const commonBuffChanceLevelBonus =
|
|
globals.config.SkillsSettings[itemSkillType as string].BuffSettings.CommonBuffChanceLevelBonus;
|
|
const receivedDurabilityMaxPercent =
|
|
globals.config.SkillsSettings[itemSkillType as string].BuffSettings.ReceivedDurabilityMaxPercent;
|
|
|
|
const skillLevel = Math.trunc(
|
|
(this.profileHelper.getSkillFromProfile(pmcData, itemSkillType)?.Progress ?? 0) / 100,
|
|
);
|
|
|
|
const durabilityToRestorePercent = repairDetails.repairPoints / template._props.MaxDurability;
|
|
const durabilityMultiplier = this.getDurabilityMultiplier(
|
|
receivedDurabilityMaxPercent,
|
|
durabilityToRestorePercent,
|
|
);
|
|
|
|
const doBuff = commonBuffMinChanceValue + commonBuffChanceLevelBonus * skillLevel * durabilityMultiplier;
|
|
|
|
if (Math.random() <= doBuff)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Based on item, what underlying skill does this item use for buff settings
|
|
* @param itemTemplate Item to check for skill
|
|
* @returns Skill name
|
|
*/
|
|
protected getItemSkillType(itemTemplate: ITemplateItem): SkillTypes
|
|
{
|
|
if (this.itemHelper.isOfBaseclasses(itemTemplate._id, [BaseClasses.ARMOR, BaseClasses.VEST, BaseClasses.HEADWEAR]))
|
|
{
|
|
if (itemTemplate._props.ArmorType === "Light")
|
|
{
|
|
return SkillTypes.LIGHT_VESTS;
|
|
}
|
|
else if (itemTemplate._props.ArmorType === "Heavy")
|
|
{
|
|
return SkillTypes.HEAVY_VESTS;
|
|
}
|
|
}
|
|
else if (this.itemHelper.isOfBaseclass(itemTemplate._id, BaseClasses.WEAPON))
|
|
{
|
|
return SkillTypes.WEAPON_TREATMENT;
|
|
}
|
|
else if (this.itemHelper.isOfBaseclass(itemTemplate._id, BaseClasses.KNIFE))
|
|
{
|
|
return SkillTypes.MELEE;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Ensure multiplier is between 1 and 0.01
|
|
* @param receiveDurabilityMaxPercent Max durabiltiy percent
|
|
* @param receiveDurabilityPercent current durability percent
|
|
* @returns durability multipler value
|
|
*/
|
|
protected getDurabilityMultiplier(receiveDurabilityMaxPercent: number, receiveDurabilityPercent: number): number
|
|
{
|
|
receiveDurabilityMaxPercent = (receiveDurabilityMaxPercent > 0) ? receiveDurabilityMaxPercent : 0.01;
|
|
const num = receiveDurabilityPercent / receiveDurabilityMaxPercent;
|
|
if (num > 1)
|
|
{
|
|
return 1.0;
|
|
}
|
|
if (num < 0.01)
|
|
{
|
|
return 0.01;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
}
|
|
|
|
export class RepairDetails
|
|
{
|
|
repairCost?: number;
|
|
repairPoints?: number;
|
|
repairedItem: Item;
|
|
repairedItemIsArmor: boolean;
|
|
repairAmount: number;
|
|
repairedByKit: boolean;
|
|
}
|