diff --git a/Libraries/Core/Services/RagfairPriceService.cs b/Libraries/Core/Services/RagfairPriceService.cs
index 266c6e7e..1873faaa 100644
--- a/Libraries/Core/Services/RagfairPriceService.cs
+++ b/Libraries/Core/Services/RagfairPriceService.cs
@@ -3,13 +3,11 @@ using Core.Helpers;
using Core.Models.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Spt.Config;
-using Core.Models.Spt.Ragfair;
using Core.Models.Utils;
using Core.Models.Enums;
-using System;
using Core.Servers;
-using Core.Models.Eft.Player;
using Core.Utils;
+using Core.Models.Eft.Common;
namespace Core.Services;
@@ -96,7 +94,7 @@ public class RagfairPriceService(
/// Rouble price
public double GetFleaPriceForOfferItems(List- offerItems)
{
- // Preset weapons take the direct prices.json value, otherwise they're massivly inflated
+ // Preset weapons take the direct prices.json value, otherwise they're massively inflated
if (_itemHelper.IsOfBaseclass(offerItems[0].Template, BaseClasses.WEAPON))
{
return GetFleaPriceForItem(offerItems[0].Template);
@@ -105,6 +103,18 @@ public class RagfairPriceService(
return offerItems.Sum(item => GetFleaPriceForItem(item.Template));
}
+ /**
+ * get the dynamic (flea) price for an item
+ * @param itemTpl item template id to look up
+ * @returns price in roubles
+ */
+ public double? GetDynamicPriceForItem(string itemTpl)
+ {
+ _databaseService.GetPrices().TryGetValue(itemTpl, out var value);
+
+ return value;
+ }
+
///
/// Grab the static (handbook) for an item by its tplId
///
@@ -122,7 +132,9 @@ public class RagfairPriceService(
/// Dictionary of item tpls and rouble cost
public Dictionary GetAllFleaPrices()
{
- throw new NotImplementedException();
+ var dynamicPrices = _databaseService.GetPrices();
+ // { ...this.prices.dynamic, ...this.prices.static };
+ return dynamicPrices.Concat(_staticPrices).ToDictionary();
}
public Dictionary GetAllStaticPrices()
@@ -144,7 +156,7 @@ public class RagfairPriceService(
/// different in percent
protected double GetPriceDifference(double a, double b)
{
- throw new NotImplementedException();
+ return (100 * a) / (a + b);
}
///
@@ -154,7 +166,13 @@ public class RagfairPriceService(
/// Rouble price
public double GetBarterPrice(List barterScheme)
{
- throw new NotImplementedException();
+ var price = 0d;
+
+ foreach (var item in barterScheme) {
+ price += GetStaticPriceForItem(item.Template).Value * item.Count.Value;
+ }
+
+ return Math.Round(price);
}
///
@@ -166,7 +184,33 @@ public class RagfairPriceService(
/// cost of item in desired currency
public double GetDynamicOfferPriceForOffer(List
- offerItems, string desiredCurrency, bool isPackOffer)
{
- throw new NotImplementedException();
+ // Price to return.
+ var price = 0d;
+
+ // Iterate over each item in the offer.
+ foreach (var item in offerItems) {
+ // Skip over armor inserts as those are not factored into item prices.
+ if (_itemHelper.IsOfBaseclass(item.Template, BaseClasses.BUILT_IN_INSERTS))
+ {
+ continue;
+ }
+
+ price += GetDynamicItemPrice(item.Template, desiredCurrency, item, offerItems, isPackOffer).Value;
+
+ // Check if the item is a weapon preset.
+ if (
+ item?.Upd?.SptPresetId is not null &&
+ _presetHelper.IsPresetBaseClass(item.Upd.SptPresetId, BaseClasses.WEAPON)
+ )
+ {
+ // This is a weapon preset, which has it's own price calculation that takes into account the mods in the
+ // preset. Since we've already calculated the price for the preset entire preset in
+ // `getDynamicItemPrice`, we can skip the rest of the items in the offer.
+ break;
+ }
+ }
+
+ return Math.Round(price);
}
///
@@ -322,7 +366,23 @@ public class RagfairPriceService(
/// adjusted price value in roubles
protected double AdjustPriceIfBelowHandbook(double itemPrice, string itemTpl)
{
- throw new NotImplementedException();
+ var itemHandbookPrice = GetStaticPriceForItem(itemTpl);
+ var priceDifferencePercent = GetPriceDifference(itemHandbookPrice.Value, itemPrice);
+ var offerAdjustmentSettings = _ragfairConfig.Dynamic.OfferAdjustment;
+
+ // Only adjust price if difference is > a percent AND item price passes threshold set in config
+ if (
+ priceDifferencePercent >
+ offerAdjustmentSettings.MaxPriceDifferenceBelowHandbookPercent &&
+ itemPrice >= offerAdjustmentSettings.PriceThresholdRub
+ )
+ {
+ // var itemDetails = this.itemHelper.getItem(itemTpl);
+ // this.logger.debug(`item below handbook price ${itemDetails[1]._name} handbook: ${itemHandbookPrice} flea: ${itemPrice} ${priceDifferencePercent}%`);
+ return Math.Round(itemHandbookPrice.Value * offerAdjustmentSettings.HandbookPriceMultiplier);
+ }
+
+ return itemPrice;
}
///
@@ -349,7 +409,42 @@ public class RagfairPriceService(
/// price of weapon in roubles
protected double GetWeaponPresetPrice(Item weaponRootItem, List
- weaponWithChildren, double existingPrice)
{
- throw new NotImplementedException();
+ // Get the default preset for this weapon
+ var presetResult = GetWeaponPreset(weaponRootItem);
+ if (presetResult.IsDefault)
+ {
+ return GetFleaPriceForItem(weaponRootItem.Template);
+ }
+
+ // Get mods on current gun not in default preset
+ var newOrReplacedModsInPresetVsDefault = weaponWithChildren.Where(x => !presetResult.Preset.Items.Any(y => y.Template == x.Template));
+
+ // Add up extra mods price
+ var extraModsPrice = 0d;
+ foreach (var mod in newOrReplacedModsInPresetVsDefault) {
+ // Use handbook or trader price, whatever is higher (dont use dynamic flea price as purchased item cannot be relisted)
+ extraModsPrice += GetHighestHandbookOrTraderPriceAsRouble(mod.Template).Value;
+ }
+
+ // Only deduct cost of replaced mods if there's replaced/new mods
+ if (newOrReplacedModsInPresetVsDefault.Any())
+ {
+ // Add up cost of mods replaced
+ var modsReplacedByNewMods = newOrReplacedModsInPresetVsDefault.Where((x) =>
+ presetResult.Preset.Items.Any((y) => y.SlotId == x.SlotId));
+
+ // Add up replaced mods price
+ var replacedModsPrice = 0d;
+ foreach (var replacedMod in modsReplacedByNewMods) {
+ replacedModsPrice += GetHighestHandbookOrTraderPriceAsRouble(replacedMod.Template).Value;
+ }
+
+ // Subtract replaced mods total from extra mods total
+ extraModsPrice -= replacedModsPrice;
+ }
+
+ // return extra mods price + base gun price
+ return existingPrice + extraModsPrice;
}
///
@@ -357,9 +452,16 @@ public class RagfairPriceService(
///
/// Item to get highest price of
/// rouble cost
- protected decimal GetHighestHandbookOrTraderPriceAsRouble(string itemTpl)
+ protected double? GetHighestHandbookOrTraderPriceAsRouble(string itemTpl)
{
- throw new NotImplementedException();
+ var price = GetStaticPriceForItem(itemTpl);
+ var traderPrice = _traderHelper.GetHighestSellToTraderPrice(itemTpl);
+ if (traderPrice > price)
+ {
+ price = traderPrice;
+ }
+
+ return price;
}
///
@@ -368,8 +470,29 @@ public class RagfairPriceService(
///
/// weapon presets to choose from
/// Default preset object
- protected object GetWeaponPreset(Item weapon)
+ protected WeaponPreset GetWeaponPreset(Item weapon)
{
- throw new NotImplementedException();
+ var defaultPreset = _presetHelper.GetDefaultPreset(weapon.Template);
+ if (defaultPreset is not null)
+ {
+ return new WeaponPreset { IsDefault = true, Preset = defaultPreset };
+ }
+ var nonDefaultPresets = _presetHelper.GetPresets(weapon.Template);
+ if (nonDefaultPresets.Count == 1)
+ {
+ _logger.Debug($"Item Id: ${ weapon.Template} has no default encyclopedia entry but only one preset(${ nonDefaultPresets[0].Name}), choosing preset(${ nonDefaultPresets[0].Name})");
+ }
+ else
+ {
+ _logger.Debug($"Item Id: ${ weapon.Template} has no default encyclopedia entry, choosing first preset(${ nonDefaultPresets[0].Name}) of ${ nonDefaultPresets.Count}");
+ }
+
+ return new WeaponPreset { IsDefault = false, Preset = nonDefaultPresets[0] };
+ }
+
+ public record WeaponPreset
+ {
+ public bool IsDefault { get; set; }
+ public Preset Preset { get; set; }
}
}