141 lines
5.5 KiB
C#
141 lines
5.5 KiB
C#
using SptCommon.Annotations;
|
|
using Core.Models.Eft.Ragfair;
|
|
using Core.Models.Spt.Config;
|
|
using Core.Servers;
|
|
using Core.Models.Utils;
|
|
using Core.Services;
|
|
using Core.Utils;
|
|
using LogLevel = Core.Models.Spt.Logging.LogLevel;
|
|
|
|
namespace Core.Helpers;
|
|
|
|
[Injectable]
|
|
public class RagfairSellHelper(
|
|
ISptLogger<RagfairSellHelper> _logger,
|
|
TimeUtil _timeUtil,
|
|
RandomUtil _randomUtil,
|
|
DatabaseService _databaseService,
|
|
ConfigServer _configServer)
|
|
{
|
|
|
|
protected 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.Value);
|
|
|
|
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}items(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 * 60);
|
|
var minimumTime = _ragfairConfig.Sell.Time.Min * 60;
|
|
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.Value) + minimumTime.Value);
|
|
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()}, bought: {boughtAmount}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_logger.IsLogEnabled(LogLevel.Debug))
|
|
{
|
|
_logger.Debug($"Offer rolled not to sell, item count: { boughtAmount}");
|
|
}
|
|
}
|
|
|
|
remainingCount -= boughtAmount;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|