From 7aba4b1575eae9368b99f75f0e6fa10ee609fe89 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 24 Jan 2025 12:02:56 +0000 Subject: [PATCH] Implemented `RagfairTaxService` --- Libraries/Core/Services/RagfairTaxService.cs | 199 +++++++++++++++++-- 1 file changed, 179 insertions(+), 20 deletions(-) diff --git a/Libraries/Core/Services/RagfairTaxService.cs b/Libraries/Core/Services/RagfairTaxService.cs index 16441c15..04f88e50 100644 --- a/Libraries/Core/Services/RagfairTaxService.cs +++ b/Libraries/Core/Services/RagfairTaxService.cs @@ -1,57 +1,216 @@ -using SptCommon.Annotations; +using Core.Helpers; using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.Ragfair; +using Core.Models.Enums; +using Core.Models.Utils; +using Core.Utils.Cloners; +using SptCommon.Annotations; namespace Core.Services; [Injectable(InjectionType.Singleton)] -public class RagfairTaxService +public class RagfairTaxService( + ISptLogger _logger, + DatabaseService _databaseService, + RagfairPriceService _ragfairPriceService, + ItemHelper _itemHelper, + ProfileHelper _profileHelper, + ICloner _cloner +) { + protected Dictionary _playerOfferTaxCache = new(); + public void StoreClientOfferTaxValue(string sessionId, StorePlayerOfferTaxAmountRequestData offer) { - throw new NotImplementedException(); + _playerOfferTaxCache[offer.Id] = offer; } public void ClearStoredOfferTaxById(string offerIdToRemove) { - throw new NotImplementedException(); + _playerOfferTaxCache.Remove(offerIdToRemove); } public StorePlayerOfferTaxAmountRequestData GetStoredClientOfferTaxValueById(string offerIdToGet) { - throw new NotImplementedException(); + return _playerOfferTaxCache[offerIdToGet]; } /** - // This method, along with CalculateItemWorth, is trying to mirror the client-side code found in the method "CalculateTaxPrice". - // It's structured to resemble the client-side code as closely as possible - avoid making any big structure changes if it's not necessary. - * @param item Item being sold on flea - * @param pmcData player profile - * @param requirementsValue - * @param offerItemCount Number of offers being created - * @param sellInOnePiece - * @returns Tax in roubles + * // This method, along with CalculateItemWorth, is trying to mirror the client-side code found in the method "CalculateTaxPrice". + * // It's structured to resemble the client-side code as closely as possible - avoid making any big structure changes if it's not necessary. + * * @param item Item being sold on flea + * * @param pmcData player profile + * * @param requirementsValue + * * @param offerItemCount Number of offers being created + * * @param sellInOnePiece + * * @returns Tax in roubles */ public double CalculateTax( Item item, PmcData pmcData, - double requirementsValue, - int offerItemCount, + double? requirementsValue, + int? offerItemCount, bool sellInOnePiece) { - throw new NotImplementedException(); + if (requirementsValue is null) + { + return 0; + } + + if (offerItemCount is null) + { + return 0; + } + + var globals = _databaseService.GetGlobals(); + + var itemTemplate = _itemHelper.GetItem(item.Template).Value; + var itemWorth = CalculateItemWorth(item, itemTemplate, offerItemCount.Value, pmcData); + var requirementsPrice = requirementsValue * (sellInOnePiece ? 1 : offerItemCount); + + var itemTaxMult = globals.Configuration.RagFair.CommunityItemTax / 100.0; + var requirementTaxMult = globals.Configuration.RagFair.CommunityRequirementTax / 100.0; + + var itemPriceMult = Math.Log10(itemWorth / requirementsPrice.Value); + var requirementPriceMult = Math.Log10(requirementsPrice.Value / itemWorth); + + if (requirementsPrice >= itemWorth) + { + requirementPriceMult = Math.Pow(requirementPriceMult, 1.08); + } + else + { + itemPriceMult = Math.Pow(itemPriceMult, 1.08); + } + + itemPriceMult = Math.Pow(4.0, itemPriceMult); + requirementPriceMult = Math.Pow(4.0, requirementPriceMult); + + var hideoutFleaTaxDiscountBonusSum = _profileHelper.GetBonusValueFromProfile( + pmcData, + BonusType.RagfairCommission + ); + // A negative bonus implies a lower discount, since we subtract later, invert the value here + var taxDiscountPercent = -(hideoutFleaTaxDiscountBonusSum / 100.0); + + var tax = + itemWorth * itemTaxMult * itemPriceMult + requirementsPrice * requirementTaxMult * requirementPriceMult; + var discountedTax = tax * (1.0 - taxDiscountPercent); + var itemComissionMult = itemTemplate.Properties.RagFairCommissionModifier.HasValue + ? itemTemplate.Properties.RagFairCommissionModifier.Value + : 1; + + if (item.Upd.Buff is not null) + { + var buffType = item.Upd.Buff.BuffType; + var itemEnhancementSettings = + _databaseService.GetGlobals().Configuration.RepairSettings.ItemEnhancementSettings; + var priceModiferValue = buffType switch + { + "DamageReduction" => itemEnhancementSettings.DamageReduction.PriceModifierValue.Value, + "MalfunctionProtections" => itemEnhancementSettings.MalfunctionProtections.PriceModifierValue.Value, + "WeaponSpread" => itemEnhancementSettings.WeaponSpread.PriceModifierValue.Value, + _ => 1d + }; + discountedTax *= 1.0 + Math.Abs(item.Upd.Buff.Value.Value - 1.0) * priceModiferValue; + } + + var taxValue = Math.Round(discountedTax.Value * itemComissionMult); + _logger.Debug("Tax Calculated to be: {taxValue}"); + + return taxValue; } // This method is trying to replicate the item worth calculation method found in the client code. // Any inefficiencies or style issues are intentional and should not be fixed, to preserve the client-side code mirroring. protected double CalculateItemWorth( - Dictionary item, - Dictionary itemTemplate, + Item item, + TemplateItem itemTemplate, int itemCount, - Dictionary pmcData, + PmcData pmcData, bool isRootItem = true) { - throw new NotImplementedException(); + var worth = _ragfairPriceService.GetFleaPriceForItem(item.Template); + + // In client, all item slots are traversed and any items contained within have their values added + if (isRootItem) + { + // Since we get a flat list of all child items, we only want to recurse from parent item + var itemChildren = _itemHelper.FindAndReturnChildrenAsItems(pmcData.Inventory.Items, item.Id); + if (itemChildren.Count > 1) + { + var itemChildrenClone = _cloner.Clone(itemChildren); // Clone is expensive, only run if necessary + foreach (var child in itemChildrenClone.Where(child => child.Id != item.Id)) + { + child.Upd ??= new Upd(); + + worth += CalculateItemWorth( + child, + _itemHelper.GetItem(child.Template).Value, + (int)(child.Upd.StackObjectsCount ?? 1), + pmcData, + false + ); + } + } + } + + var upd = item.Upd ??= new Upd(); + + if (upd.Dogtag is not null) + { + worth *= upd.Dogtag.Level.Value; + } + + if (itemTemplate.Properties is null) + { + _logger.Warning($"Item: {item.Id} lacks _props and cannot have its worth calculated properly"); + + return worth; + } + + if (upd.Key is not null && (itemTemplate.Properties.MaximumNumberOfUsage ?? 0) > 0) + { + worth = + worth / + (itemTemplate.Properties.MaximumNumberOfUsage ?? 1) * + ((itemTemplate.Properties.MaximumNumberOfUsage ?? 1) - upd.Key.NumberOfUsages.Value); + } + + if (upd.Resource is not null && (itemTemplate.Properties.MaxResource ?? 0) > 0) + { + worth = (double)(worth * 0.1 + + (worth * 0.9 / (itemTemplate.Properties.MaxResource ?? 1) * upd.Resource.Value)); + } + + if (upd.SideEffect is not null && (itemTemplate.Properties.MaxResource ?? 0) > 0) + { + worth = (double)(worth * 0.1 + + worth * 0.9 / (itemTemplate.Properties.MaxResource ?? 1) * upd.SideEffect.Value); + } + + if (upd.MedKit is not null && (itemTemplate.Properties.MaxHpResource ?? 0) > 0) + { + worth = worth / (itemTemplate.Properties.MaxHpResource ?? 1) * upd.MedKit.HpResource.Value; + } + + if (upd.FoodDrink is not null && (itemTemplate.Properties.MaxResource ?? 0) > 0) + { + worth = worth / (itemTemplate.Properties.MaxResource ?? 1) * upd.FoodDrink.HpPercent.Value; + } + + if (upd.Repairable is not null && (itemTemplate.Properties.ArmorClass ?? 0) > 0) + { + var num2 = 0.01 * Math.Pow(0.0, upd.Repairable.MaxDurability.Value); + worth = + worth * (upd.Repairable.MaxDurability.Value / (itemTemplate.Properties.Durability ?? 1) - num2) - + Math.Floor( + (itemTemplate.Properties.RepairCost ?? 0) * + (upd.Repairable.MaxDurability.Value - upd.Repairable.Durability.Value) + ); + } + + return worth * itemCount; } }