Various ragfair fixes + implementations

This commit is contained in:
Chomp
2025-01-27 17:23:40 +00:00
parent 2617cbd0e5
commit fa669f3e05
6 changed files with 130 additions and 25 deletions
@@ -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);
+4 -4
View File
@@ -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)
+1
View File
@@ -20,6 +20,7 @@ public class PaymentHelper(ConfigServer _configServer)
var moneyTypes = new List<string>
{
Money.DOLLARS,
Money.EUROS,
Money.ROUBLES,
Money.GP,
+101 -6
View File
@@ -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;
}
}
+8 -4
View File
@@ -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;
}
}