using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; using SPTarkov.Server.Core.Utils; using SPTarkov.Server.Core.Utils.Cloners; using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel; namespace SPTarkov.Server.Core.Helpers; [Injectable] public class RepairHelper( ISptLogger logger, RandomUtil randomUtil, DatabaseService databaseService, ConfigServer configServer, ICloner cloner ) { protected RepairConfig _repairConfig = configServer.GetConfig(); /// /// Alter an items durability after a repair by trader/repair kit /// /// item to update durability details /// db details of item to repair /// Is item being repaired a piece of armor /// how many unit of durability to repair /// Is item being repaired with a repair kit /// Trader quality value from traders base json /// should item have max durability reduced public void UpdateItemDurability( Item itemToRepair, TemplateItem itemToRepairDetails, bool isArmor, double amountToRepair, bool useRepairKit, double traderQualityMultiplier, bool applyMaxDurabilityDegradation = true ) { if (logger.IsLogEnabled(LogLevel.Debug)) { logger.Debug($"Adding {amountToRepair} to {itemToRepairDetails.Name} using kit: {useRepairKit}"); } var itemMaxDurability = cloner.Clone(itemToRepair.Upd.Repairable.MaxDurability); var itemCurrentDurability = cloner.Clone(itemToRepair.Upd.Repairable.Durability); var itemCurrentMaxDurability = cloner.Clone(itemToRepair.Upd.Repairable.MaxDurability); var newCurrentDurability = itemCurrentDurability + amountToRepair; var newCurrentMaxDurability = itemCurrentMaxDurability + amountToRepair; // Ensure new max isnt above items max if (newCurrentMaxDurability > itemMaxDurability) { newCurrentMaxDurability = itemMaxDurability; } // Ensure new current isn't above items max if (newCurrentDurability > itemMaxDurability) { newCurrentDurability = itemMaxDurability; } // Update Repairable properties with new values after repair itemToRepair.Upd.Repairable = new UpdRepairable { Durability = newCurrentDurability, MaxDurability = newCurrentMaxDurability }; // when modders set the repair coefficient to 0 it means that they don't want to lose durability on items // the code below generates a random degradation on the weapon durability if (applyMaxDurabilityDegradation) { var randomisedWearAmount = isArmor ? GetRandomisedArmorRepairDegradationValue( itemToRepairDetails.Properties.ArmorMaterial.Value, useRepairKit, itemCurrentMaxDurability ?? 0, traderQualityMultiplier ) : GetRandomisedWeaponRepairDegradationValue( itemToRepairDetails.Properties, useRepairKit, itemCurrentMaxDurability ?? 0, traderQualityMultiplier ); // Apply wear to durability itemToRepair.Upd.Repairable.MaxDurability -= randomisedWearAmount; // After adjusting max durability with degradation, ensure current dura isnt above max if (itemToRepair.Upd.Repairable.Durability > itemToRepair.Upd.Repairable.MaxDurability) { itemToRepair.Upd.Repairable.Durability = itemToRepair.Upd.Repairable.MaxDurability; } } // Repair mask cracks if (itemToRepair.Upd.FaceShield is not null && itemToRepair.Upd.FaceShield?.Hits > 0) { itemToRepair.Upd.FaceShield.Hits = 0; } } /// /// Repairing armor reduces the total durability value slightly, get a randomised (to 2dp) amount based on armor material /// /// What material is the armor being repaired made of /// Was a repair kit used /// Max amount of durability item can have /// Different traders produce different loss values /// Amount to reduce max durability by protected double GetRandomisedArmorRepairDegradationValue( ArmorMaterial material, bool isRepairKit, double armorMax, double traderQualityMultiplier ) { // Degradation value is based on the armor material if (!databaseService.GetGlobals().Configuration.ArmorMaterials.TryGetValue(material, out var armorMaterialSettings)) { logger.Error($"Unable to find armor with a type of: {material}"); } var minMultiplier = isRepairKit ? armorMaterialSettings.MinRepairKitDegradation : armorMaterialSettings.MinRepairDegradation; var maxMultiplier = isRepairKit ? armorMaterialSettings.MaxRepairKitDegradation : armorMaterialSettings.MaxRepairDegradation; var duraLossPercent = randomUtil.GetDouble(minMultiplier, maxMultiplier); var duraLossMultipliedByTraderMultiplier = duraLossPercent * armorMax * traderQualityMultiplier; return Math.Round(duraLossMultipliedByTraderMultiplier, 2); } /// /// Repairing weapons reduces the total durability value slightly, get a randomised (to 2dp) amount /// /// Weapon properties /// Was a repair kit used /// Max amount of durability item can have /// Different traders produce different loss values /// Amount to reduce max durability by protected double GetRandomisedWeaponRepairDegradationValue( TemplateItemProperties itemProperties, bool isRepairKit, double weaponMax, double traderQualityMultiplier ) { var minRepairDeg = isRepairKit ? itemProperties.MinRepairKitDegradation : itemProperties.MinRepairDegradation; var maxRepairDeg = isRepairKit ? itemProperties.MaxRepairKitDegradation : itemProperties.MaxRepairDegradation; // WORKAROUND: Some items are always 0 when repairkit is true if (maxRepairDeg == 0) { maxRepairDeg = itemProperties.MaxRepairDegradation; } var duraLossPercent = randomUtil.GetDouble((double)minRepairDeg, (double)maxRepairDeg); var duraLossMultipliedByTraderMultiplier = duraLossPercent * weaponMax * traderQualityMultiplier; return Math.Round(duraLossMultipliedByTraderMultiplier, 2); } }