Various ragfair fixes + implementations
This commit is contained in:
@@ -346,7 +346,7 @@ public class RagfairController
|
||||
/**
|
||||
* Called when creating an offer on flea, fills values in top right corner
|
||||
* @param getPriceRequest Client request object
|
||||
* @param ignoreTraderOffers Should trader offers be ignored in the calcualtion
|
||||
* @param ignoreTraderOffers Should trader offers be ignored in the calculation
|
||||
* @returns min/avg/max values for an item based on flea offers available
|
||||
*/
|
||||
public GetItemPriceResult GetItemMinAvgMaxFleaPriceValues(GetMarketPriceRequestData getPriceRequest, bool ignoreTraderOffers = true)
|
||||
@@ -358,7 +358,7 @@ public class RagfairController
|
||||
if (offers.Count > 0)
|
||||
{
|
||||
// These get calculated while iterating through the list below
|
||||
var minMax = new MinMax(0, int.MaxValue);
|
||||
var minMax = new MinMax(int.MaxValue, 0);
|
||||
|
||||
// Get the average offer price, excluding barter offers
|
||||
var average = GetAveragePriceFromOffers(offers, minMax, ignoreTraderOffers);
|
||||
|
||||
@@ -13,6 +13,7 @@ using Core.Utils;
|
||||
using Core.Utils.Cloners;
|
||||
using Server;
|
||||
using SptCommon.Extensions;
|
||||
using Core.Models.Eft.Player;
|
||||
|
||||
namespace Core.Generators;
|
||||
|
||||
@@ -95,7 +96,7 @@ public class RagfairOfferGenerator(
|
||||
var isTrader = ragfairServerHelper.IsTrader(userID);
|
||||
|
||||
var offerRequirements = barterScheme.Select((barter) => {
|
||||
var offerRequirement = new OfferRequirement(){
|
||||
var offerRequirement = new OfferRequirement{
|
||||
Template = barter.Template,
|
||||
Count = Math.Round((double) barter.Count, 2),
|
||||
OnlyFunctional = barter.OnlyFunctional ?? false,
|
||||
@@ -115,7 +116,7 @@ public class RagfairOfferGenerator(
|
||||
var itemStackCount = itemsClone[0].Upd?.StackObjectsCount ?? 1;
|
||||
|
||||
// Hydrate ammo boxes with cartridges + ensure only 1 item is present (ammo box)
|
||||
// On offer refresh dont re-add cartridges to ammo box that already has cartridges
|
||||
// On offer refresh don't re-add cartridges to ammo box that already has cartridges
|
||||
if (itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX) && itemsClone.Count == 1) {
|
||||
itemHelper.AddCartridgesToAmmoBox(itemsClone, itemHelper.GetItem(items[0].Template).Value);
|
||||
}
|
||||
@@ -123,7 +124,7 @@ public class RagfairOfferGenerator(
|
||||
var roubleListingPrice = Math.Round((double) ConvertOfferRequirementsIntoRoubles(offerRequirements));
|
||||
var singleItemListingPrice = isPackOffer ? roubleListingPrice / itemStackCount : roubleListingPrice;
|
||||
|
||||
var offer = new RagfairOffer() {
|
||||
var offer = new RagfairOffer {
|
||||
Id= hashUtil.Generate(),
|
||||
InternalId= offerCounter,
|
||||
User= CreateUserDataForFleaOffer(userID, isTrader),
|
||||
@@ -131,7 +132,7 @@ public class RagfairOfferGenerator(
|
||||
Items= itemsClone,
|
||||
ItemsCost= Math.Round((double) handbookHelper.GetTemplatePrice(items[0].Template)), // Handbook price
|
||||
Requirements= offerRequirements,
|
||||
RequirementsCost= Math.Round((double) singleItemListingPrice),
|
||||
RequirementsCost= Math.Round(singleItemListingPrice),
|
||||
SummaryCost= roubleListingPrice,
|
||||
StartTime= time,
|
||||
EndTime= GetOfferEndTime(userID, time),
|
||||
@@ -458,17 +459,21 @@ public class RagfairOfferGenerator(
|
||||
);
|
||||
|
||||
// Remove removable plates if % check passes
|
||||
if (itemHelper.ArmorItemCanHoldMods(itemWithChildren[0].Template)) {
|
||||
if (itemHelper.ArmorItemCanHoldMods(itemWithChildren[0].Template))
|
||||
{
|
||||
var armorConfig = ragfairConfig.Dynamic.Armor;
|
||||
|
||||
var shouldRemovePlates = randomUtil.GetChance100(armorConfig.RemoveRemovablePlateChance);
|
||||
if (shouldRemovePlates && itemHelper.ArmorItemHasRemovablePlateSlots(itemWithChildren[0].Template)) {
|
||||
var offerItemPlatesToRemove = itemWithChildren.Where((item) =>
|
||||
armorConfig.PlateSlotIdToRemovePool.Contains(item.SlotId?.ToLower())
|
||||
if (shouldRemovePlates && itemHelper.ArmorItemHasRemovablePlateSlots(itemWithChildren[0].Template))
|
||||
{
|
||||
var offerItemPlatesToRemove = itemWithChildren.Where(
|
||||
(item) =>
|
||||
armorConfig.PlateSlotIdToRemovePool.Contains(item.SlotId?.ToLower())
|
||||
);
|
||||
|
||||
// Latest first, to ensure we don't move later items off by 1 each time we remove an item below it
|
||||
var indexesToRemove = offerItemPlatesToRemove.Select(plateItem => itemWithChildren.IndexOf(plateItem)).ToList();
|
||||
var indexesToRemove = offerItemPlatesToRemove.Select(plateItem => itemWithChildren.IndexOf(plateItem))
|
||||
.ToList();
|
||||
foreach (var index in indexesToRemove.OrderByDescending(x => x))
|
||||
{
|
||||
itemWithChildren.RemoveAt(index);
|
||||
|
||||
@@ -579,7 +579,7 @@ public class ItemHelper(
|
||||
if (medkit is not null)
|
||||
{
|
||||
// Meds
|
||||
result = medkit.HpResource ?? 0 / itemDetails.Properties.MaxHpResource ?? 0;
|
||||
result = (medkit.HpResource ?? 0) / (itemDetails.Properties.MaxHpResource ?? 0);
|
||||
}
|
||||
else if (repairable is not null)
|
||||
{
|
||||
@@ -588,7 +588,7 @@ public class ItemHelper(
|
||||
else if (foodDrink is not null)
|
||||
{
|
||||
// food & drink
|
||||
result = foodDrink.HpPercent ?? 0 / itemDetails.Properties.MaxResource ?? 0;
|
||||
result = (foodDrink.HpPercent ?? 0) / (itemDetails.Properties.MaxResource ?? 0);
|
||||
}
|
||||
else if (key is not null && key.NumberOfUsages > 0 && itemDetails.Properties.MaximumNumberOfUsage > 0)
|
||||
{
|
||||
@@ -599,12 +599,12 @@ public class ItemHelper(
|
||||
else if (resource is not null && resource.UnitsConsumed > 0)
|
||||
{
|
||||
// Things like fuel tank
|
||||
result = resource.Value ?? 0 / itemDetails.Properties.MaxResource ?? 0;
|
||||
result = (resource.Value ?? 0) / (itemDetails.Properties.MaxResource ?? 0);
|
||||
}
|
||||
else if (repairKit is not null)
|
||||
{
|
||||
// Repair kits
|
||||
result = repairKit.Resource ?? 0 / itemDetails.Properties.MaxRepairResource ?? 0;
|
||||
result = (repairKit.Resource ?? 0) / (itemDetails.Properties.MaxRepairResource ?? 0);
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
|
||||
@@ -20,6 +20,7 @@ public class PaymentHelper(ConfigServer _configServer)
|
||||
var moneyTypes = new List<string>
|
||||
{
|
||||
Money.DOLLARS,
|
||||
Money.EUROS,
|
||||
Money.ROUBLES,
|
||||
Money.GP,
|
||||
|
||||
|
||||
@@ -1,24 +1,57 @@
|
||||
using SptCommon.Annotations;
|
||||
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;
|
||||
|
||||
namespace Core.Helpers;
|
||||
|
||||
[Injectable]
|
||||
public class RagfairSellHelper
|
||||
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 multipler of item being sold</param>
|
||||
/// <param name="qualityMultiplier">Quality multiplier of item being sold</param>
|
||||
/// <returns>percent value</returns>
|
||||
public double CalculateSellChance(
|
||||
double averageOfferPriceRub,
|
||||
double playerListedPriceRub,
|
||||
double qualityMultiplier)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
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>
|
||||
@@ -28,8 +61,70 @@ public class RagfairSellHelper
|
||||
/// <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)
|
||||
public List<SellResult> RollForSale(double? sellChancePercent, int itemSellCount, bool sellInOneGo = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
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}%");
|
||||
}
|
||||
|
||||
_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 });
|
||||
|
||||
_logger.Debug($"Offer will sell at: { _timeUtil.GetDateTimeFromTimeStamp(sellTimestamp).ToLocalTime().ToString()}, bought: {boughtAmount}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug($"Offer rolled not to sell, item count: { boughtAmount}");
|
||||
}
|
||||
|
||||
remainingCount -= boughtAmount;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,14 +146,18 @@ public class RagfairOfferHolder(
|
||||
}
|
||||
else
|
||||
{
|
||||
var valueMapped = new Dictionary<string, RagfairOffer>();
|
||||
valueMapped.Add(offer.Id, offer);
|
||||
var valueMapped = new Dictionary<string, RagfairOffer> { { offer.Id, offer } };
|
||||
_offersByTrader.Add(trader, valueMapped);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsStale(RagfairOffer offer, long time)
|
||||
protected bool IsStale(RagfairOffer? offer, long time)
|
||||
{
|
||||
return offer.EndTime < time || (offer.Items[0].Upd?.StackObjectsCount ?? 0) < 1;
|
||||
if (offer is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return offer.EndTime < time || (offer.Items.FirstOrDefault().Upd?.StackObjectsCount ?? 0) < 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user