Files
SPT-Server-Build/Libraries/SPTarkov.Server.Core/Helpers/RagfairSellHelper.cs
T
Chomp 99c14de7af String to Mongo Id conversions
Cleaned up global class null values
2025-07-16 11:40:38 +01:00

162 lines
5.8 KiB
C#

using System.Globalization;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Eft.Ragfair;
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 LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Helpers;
[Injectable]
public class RagfairSellHelper(
ISptLogger<RagfairSellHelper> logger,
TimeUtil timeUtil,
RandomUtil randomUtil,
DatabaseService databaseService,
ConfigServer configServer
)
{
protected readonly RagfairConfig _ragfairConfig = configServer.GetConfig<RagfairConfig>();
/// <summary>
/// Get the percent chance to sell an item based on its average listed price vs player chosen listing price
/// </summary>
/// <param name="averageOfferPriceRub">Price of average offer in roubles</param>
/// <param name="playerListedPriceRub">Price player listed item for in roubles</param>
/// <param name="qualityMultiplier">Quality multiplier of item being sold</param>
/// <returns>percent value</returns>
public double CalculateSellChance(
double averageOfferPriceRub,
double playerListedPriceRub,
double qualityMultiplier
)
{
var sellConfig = _ragfairConfig.Sell.Chance;
// Base sell chance modified by items quality
var baseSellChancePercent = sellConfig.Base * qualityMultiplier;
// Modifier gets applied twice to either penalize or incentivize over/under pricing (Probably a cleaner way to do this)
var sellModifier = averageOfferPriceRub / playerListedPriceRub * sellConfig.SellMultiplier;
var sellChance = Math.Round(
baseSellChancePercent * sellModifier * Math.Pow(sellModifier, 3) + 10
); // Power of 3
// Adjust sell chance if below config value
if (sellChance < sellConfig.MinSellChancePercent)
{
sellChance = sellConfig.MinSellChancePercent;
}
// Adjust sell chance if above config value
if (sellChance > sellConfig.MaxSellChancePercent)
{
sellChance = sellConfig.MaxSellChancePercent;
}
return sellChance;
}
/// <summary>
/// Get list of item count and sell time (empty list = no sell)
/// </summary>
/// <param name="sellChancePercent">chance item will sell</param>
/// <param name="itemSellCount">count of items to sell</param>
/// <param name="sellInOneGo">All items listed get sold at once</param>
/// <returns>List of purchases of item(s) listed</returns>
public List<SellResult> RollForSale(
double? sellChancePercent,
int itemSellCount,
bool sellInOneGo = false
)
{
var startTimestamp = timeUtil.GetTimeStamp();
// Get a time in future to stop simulating sell chances at
var endTime =
startTimestamp
+ timeUtil.GetHoursAsSeconds(
(int)databaseService.GetGlobals().Configuration.RagFair.OfferDurationTimeInHour
);
var sellTimestamp = startTimestamp;
var remainingCount = itemSellCount;
var result = new List<SellResult>();
var effectiveSellChance = sellChancePercent;
if (sellChancePercent is null)
{
effectiveSellChance = _ragfairConfig.Sell.Chance.Base;
logger.Warning(
$"Sell chance was not a number: {sellChancePercent}, defaulting to {_ragfairConfig.Sell.Chance.Base}%"
);
}
if (logger.IsLogEnabled(LogLevel.Debug))
{
logger.Debug(
$"Rolling to sell: {itemSellCount} item(s) - (chance: {effectiveSellChance}%)"
);
}
// No point rolling for a sale on a 0% chance item, exit early
if (effectiveSellChance == 0)
{
return result;
}
while (remainingCount > 0 && sellTimestamp < endTime)
{
var boughtAmount = sellInOneGo ? remainingCount : randomUtil.GetInt(1, remainingCount);
if (randomUtil.GetChance100(effectiveSellChance))
{
// Passed roll check, item will be sold
// Weight time to sell towards selling faster based on how cheap the item sold
var weighting = (100 - effectiveSellChance) / 100;
var maximumTime = weighting * _ragfairConfig.Sell.Time.Max * 60d;
var minimumTime = _ragfairConfig.Sell.Time.Min * 60d;
if (maximumTime < minimumTime)
{
maximumTime = minimumTime + 5;
}
// Sell time will be random between min/max
var random = new Random();
var newSellTime = Math.Floor(
random.NextDouble() * (maximumTime.Value - minimumTime) + minimumTime
);
if (newSellTime == 0)
// Ensure all sales don't occur the same exact time
{
newSellTime += 1;
}
sellTimestamp += (long)newSellTime;
result.Add(new SellResult { SellTime = sellTimestamp, Amount = boughtAmount });
if (logger.IsLogEnabled(LogLevel.Debug))
{
logger.Debug(
$"Offer will sell at: {timeUtil.GetDateTimeFromTimeStamp(sellTimestamp).ToLocalTime().ToString(CultureInfo.InvariantCulture)}, bought: {boughtAmount}"
);
}
}
else
{
if (logger.IsLogEnabled(LogLevel.Debug))
{
logger.Debug($"Offer rolled not to sell, item count: {boughtAmount}");
}
}
remainingCount -= boughtAmount;
}
return result;
}
}