more stuff fixed

This commit is contained in:
Alex
2025-01-24 13:35:28 +00:00
parent bc76153e3e
commit 48228c6d2e
9 changed files with 680 additions and 251 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ public class RagfairCallbacks(
public Task OnLoad()
{
// await _ragfairServer.Load();
_ragfairServer.Load();
return Task.CompletedTask;
}
@@ -1,63 +1,338 @@
using SptCommon.Annotations;
using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Servers;
using Core.Services;
using Core.Utils;
namespace Core.Generators;
[Injectable]
public class FenceBaseAssortGenerator(
ConfigServer _configServer
ISptLogger<FenceBaseAssortGenerator> logger,
HashUtil hashUtil,
DatabaseService databaseService,
HandbookHelper handbookHelper,
ItemHelper itemHelper,
PresetHelper presetHelper,
ItemFilterService itemFilterService,
SeasonalEventService seasonalEventService,
LocalisationService localisationService,
ConfigServer configServer,
FenceService fenceService
)
{
protected TraderConfig _traderConfig = _configServer.GetConfig<TraderConfig>();
protected TraderConfig traderConfig = configServer.GetConfig<TraderConfig>();
/// <summary>
/// Create base fence assorts dynamically and store in memory
/// </summary>
/**
* Create base fence assorts dynamically and store in memory
*/
public void GenerateFenceBaseAssorts()
{
// TODO: actually implement
return;
var blockedSeasonalItems = seasonalEventService.GetInactiveSeasonalEventItems();
var baseFenceAssort = databaseService.GetTrader(Traders.FENCE).Assort;
foreach (var rootItemDb in itemHelper.GetItems().Where((item) => IsValidFenceItem(item)))
{
// Skip blacklisted items
if (itemFilterService.IsItemBlacklisted(rootItemDb.Id))
{
continue;
}
// Skip reward item blacklist
if (itemFilterService.IsItemRewardBlacklisted(rootItemDb.Id))
{
continue;
}
// Invalid
if (!itemHelper.IsValidItem(rootItemDb.Id))
{
continue;
}
// Item base type blacklisted
if (traderConfig.Fence.Blacklist.Count > 0)
{
if (traderConfig.Fence.Blacklist.Contains(rootItemDb.Id) ||
itemHelper.IsOfBaseclasses(rootItemDb.Id, traderConfig.Fence.Blacklist)
)
{
continue;
}
}
// Only allow rigs with no slots (carrier rigs)
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.VEST) &&
(rootItemDb.Properties?.Slots?.Count ?? 0) > 0
)
{
continue;
}
// Skip seasonal event items when not in seasonal event
if (traderConfig.Fence.BlacklistSeasonalItems && blockedSeasonalItems.Contains(rootItemDb.Id))
{
continue;
}
// Create item object in array
var itemWithChildrenToAdd = new List<Item>
{
new()
{
Id = hashUtil.Generate(),
Template = rootItemDb.Id,
ParentId = "hideout",
SlotId = "hideout",
Upd = new Upd { StackObjectsCount = 9999999 },
}
};
// Ensure ammo is not above penetration limit value
if (itemHelper.IsOfBaseclasses(rootItemDb.Id, [BaseClasses.AMMO_BOX, BaseClasses.AMMO]))
{
if (IsAmmoAbovePenetrationLimit(rootItemDb))
{
continue;
}
}
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO_BOX))
{
// Only add cartridges to box if box has no children
if (itemWithChildrenToAdd.Count == 1)
{
itemHelper.AddCartridgesToAmmoBox(itemWithChildrenToAdd, rootItemDb);
}
}
// Ensure IDs are unique
itemHelper.RemapRootItemId(itemWithChildrenToAdd);
if (itemWithChildrenToAdd.Count > 1)
{
itemHelper.ReparentItemAndChildren(itemWithChildrenToAdd[0], itemWithChildrenToAdd);
itemWithChildrenToAdd[0].ParentId = "hideout";
}
// Create barter scheme (price)
var barterSchemeToAdd = new BarterScheme()
{
Count = Math.Round((double)fenceService.GetItemPrice(rootItemDb.Id, itemWithChildrenToAdd)),
Template = Money.ROUBLES
};
// Add barter data to base
baseFenceAssort.BarterScheme[itemWithChildrenToAdd[0].Id] = [[barterSchemeToAdd]];
// Add item to base
baseFenceAssort.Items.AddRange(itemWithChildrenToAdd);
// Add loyalty data to base
baseFenceAssort.LoyalLevelItems[itemWithChildrenToAdd[0].Id] = 1;
}
// Add all default presets to base fence assort
var defaultPresets = presetHelper.GetDefaultPresets().Values;
foreach (var defaultPreset in defaultPresets)
{
// Skip presets we've already added
if (baseFenceAssort.Items.Any((item) => item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id))
{
continue;
}
// Construct preset + mods
var itemAndChildren = itemHelper.ReplaceIDs(defaultPreset.Items);
// Find root item and add some properties to it
for (var i = 0; i < itemAndChildren.Count; i++)
{
var mod = itemAndChildren[i];
// Build root Item info
if (string.IsNullOrEmpty(mod.ParentId))
{
mod.ParentId = "hideout";
mod.SlotId = "hideout";
mod.Upd = new Upd()
{
StackObjectsCount = 1,
SptPresetId =
defaultPreset.Id, // Store preset id here so we can check it later to prevent preset dupes
};
// Updated root item, exit loop
break;
}
}
// Add constructed preset to assorts
baseFenceAssort.Items.AddRange(itemAndChildren);
// Calculate preset price (root item + child items)
var price = handbookHelper.GetTemplatePriceForItems(itemAndChildren);
var itemQualityModifier = itemHelper.GetItemQualityModifierForItems(itemAndChildren);
// Multiply weapon+mods rouble price by quality modifier
baseFenceAssort.BarterScheme[itemAndChildren[0].Id] = [[]];
baseFenceAssort.BarterScheme[itemAndChildren[0].Id][0][0] = new BarterScheme()
{
Template = Money.ROUBLES,
Count = Math.Round(price * itemQualityModifier),
};
baseFenceAssort.LoyalLevelItems[itemAndChildren[0].Id] = 1;
}
}
/// <summary>
/// Check ammo in boxes and loose ammos has a penetration value above the configured value in trader.json / ammoMaxPenLimit
/// </summary>
/// <param name="rootItemDb">Ammo box or ammo item from items.db</param>
/// <returns>True if penetration value is above limit set in config</returns>
/**
* Check ammo in boxes + loose ammos has a penetration value above the configured value in trader.json / ammoMaxPenLimit
* @param rootItemDb Ammo box or ammo item from items.db
* @returns True if penetration value is above limit set in config
*/
protected bool IsAmmoAbovePenetrationLimit(TemplateItem rootItemDb)
{
throw new NotImplementedException();
var ammoPenetrationPower = GetAmmoPenetrationPower(rootItemDb);
if (ammoPenetrationPower == null)
{
logger.Warning(localisationService.GetText("fence-unable_to_get_ammo_penetration_value", rootItemDb.Id));
return false;
}
return ammoPenetrationPower > traderConfig.Fence.AmmoMaxPenLimit;
}
/// <summary>
/// Gets the penetration power value of an ammo, works with ammo boxes and raw ammos.
/// </summary>
/// <param name="rootItemDb">Ammo box or ammo item from items.db</param>
/// <returns>Penetration power of passed in item, undefined if it doesnt have a power</returns>
/**
* Get the penetration power value of an ammo, works with ammo boxes and raw ammos
* @param rootItemDb Ammo box or ammo item from items.db
* @returns Penetration power of passed in item, undefined if it doesnt have a power
*/
protected double? GetAmmoPenetrationPower(TemplateItem rootItemDb)
{
throw new NotImplementedException();
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO_BOX))
{
// Get the cartridge tpl found inside ammo box
var cartridgeTplInBox = rootItemDb.Properties.StackSlots[0].Props.Filters[0].Filter[0];
// Look up cartridge tpl in db
var ammoItemDb = itemHelper.GetItem(cartridgeTplInBox);
if (!ammoItemDb.Key)
{
logger.Warning(localisationService.GetText("fence-ammo_not_found_in_db", cartridgeTplInBox));
return null;
}
return ammoItemDb.Value.Properties.PenetrationPower;
}
// Plain old ammo, get its pen property
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO))
{
return rootItemDb.Properties.PenetrationPower;
}
// Not an ammobox or ammo
return null;
}
/// <summary>
/// Add soft inserts and armor plates to an armor.
/// </summary>
/// <param name="armor">Armor item list to add mods into.</param>
/// <param name="itemDbDetails">Armor items db template.</param>
/**
* Add soft inserts + armor plates to an armor
* @param armor Armor item array to add mods into
* @param itemDbDetails Armor items db template
*/
protected void AddChildrenToArmorModSlots(List<Item> armor, TemplateItem itemDbDetails)
{
throw new NotImplementedException();
// Armor has no mods, make no additions
var hasMods = itemDbDetails.Properties.Slots.Count > 0;
if (!hasMods)
{
return;
}
// Check for and add required soft inserts to armors
var requiredSlots = itemDbDetails.Properties.Slots.Where((slot) => slot.Required ?? false).ToList();
var hasRequiredSlots = requiredSlots.Count > 0;
if (hasRequiredSlots)
{
foreach (var requiredSlot in requiredSlots)
{
var modItemDbDetails = itemHelper.GetItem(requiredSlot.Props.Filters[0].Plate).Value;
var plateTpl =
requiredSlot.Props.Filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
if (string.IsNullOrEmpty(plateTpl))
{
// Some bsg plate properties are empty, skip mod
continue;
}
var mod = new Item
{
Id = hashUtil.Generate(),
Template = plateTpl,
ParentId = armor[0].Id,
SlotId = requiredSlot.Name,
Upd = new()
{
Repairable = new()
{
Durability = modItemDbDetails.Properties.MaxDurability,
MaxDurability = modItemDbDetails.Properties.MaxDurability,
},
},
};
armor.Add(mod);
}
}
// Check for and add plate items
var plateSlots = itemDbDetails.Properties.Slots.Where((slot) => itemHelper.IsRemovablePlateSlot(slot.Name))
.ToList();
if (plateSlots.Count > 0)
{
foreach (var plateSlot in plateSlots)
{
var plateTpl = plateSlot.Props.Filters[0].Plate;
if (string.IsNullOrEmpty(plateTpl))
{
// Bsg data lacks a default plate, skip adding mod
continue;
}
var modItemDbDetails = itemHelper.GetItem(plateTpl).Value;
armor.Add(
new Item()
{
Id = hashUtil.Generate(),
Template = plateSlot.Props.Filters[0].Plate, // `Plate` property appears to be the 'default' item for slot
ParentId = armor[0].Id,
SlotId = plateSlot.Name,
Upd = new()
{
Repairable = new()
{
Durability = modItemDbDetails.Properties.MaxDurability,
MaxDurability = modItemDbDetails.Properties.MaxDurability,
},
},
}
);
}
}
}
/// <summary>
/// Check if item is valid for being added to fence assorts
/// </summary>
/// <param name="item">Item to check</param>
/// <returns>true if valid fence item</returns>
/**
* Check if item is valid for being added to fence assorts
* @param item Item to check
* @returns true if valid fence item
*/
protected bool IsValidFenceItem(TemplateItem item)
{
throw new NotImplementedException();
return item.Type == "Item";
}
}
@@ -71,7 +71,7 @@ public class PMCLootGenerator
var itemsToAdd = items.Where(
(item) =>
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
_itemHelper.isValidItem(item.Value.Id) &&
_itemHelper.IsValidItem(item.Value.Id) &&
!blacklist.Contains(item.Value.Id) &&
!blacklist.Contains(item.Value.Parent) &&
ItemFitsInto1By2Slot(item.Value)
@@ -151,7 +151,7 @@ public class PMCLootGenerator
var itemsToAdd = items.Where(
(item) =>
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
_itemHelper.isValidItem(item.Value.Id) &&
_itemHelper.IsValidItem(item.Value.Id) &&
!blacklist.Contains(item.Value.Id) &&
!blacklist.Contains(item.Value.Parent) &&
ItemFitsInto2By2Slot(item.Value));
@@ -233,7 +233,7 @@ public class PMCLootGenerator
var itemsToAdd = items.Where(
(item) =>
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
_itemHelper.isValidItem(item.Value.Id) &&
_itemHelper.IsValidItem(item.Value.Id) &&
!blacklist.Contains(item.Value.Id) &&
!blacklist.Contains(item.Value.Parent));
@@ -695,7 +695,7 @@ public class RepeatableQuestRewardGenerator(
List<string>? itemBaseWhitelist = null)
{
// Return early if not valid item to give as reward
if (!_itemHelper.isValidItem(tpl))
if (!_itemHelper.IsValidItem(tpl))
{
return false;
}
+1 -1
View File
@@ -243,7 +243,7 @@ public class ItemHelper(
* @param tpl the template id / tpl
* @returns boolean; true for items that may be in player possession and not quest items
*/
public bool isValidItem(string tpl, List<string> invalidBaseTypes = null)
public bool IsValidItem(string tpl, List<string> invalidBaseTypes = null)
{
var baseTypes = invalidBaseTypes ?? _defaultInvalidBaseTypes;
var itemDetails = GetItem(tpl);
@@ -44,7 +44,7 @@ public record RagfairOffer
/** Rouble price - same as requirementsCost */
[JsonPropertyName("summaryCost")]
public decimal? SummaryCost { get; set; }
public double? SummaryCost { get; set; }
[JsonPropertyName("user")]
public RagfairOfferUser? User { get; set; }
+101 -112
View File
@@ -6,130 +6,119 @@ using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Services;
namespace Core.Servers
namespace Core.Servers;
[Injectable]
public class RagfairServer(
ISptLogger<RagfairServer> _logger,
RagfairOfferService _ragfairOfferService,
RagfairCategoriesService _ragfairCategoriesService,
RagfairRequiredItemsService _ragfairRequiredItemsService,
LocalisationService _localisationService,
RagfairOfferGenerator _ragfairOfferGenerator,
ConfigServer _configServer
)
{
[Injectable]
public class RagfairServer
protected RagfairConfig _ragfairConfig = _configServer.GetConfig<RagfairConfig>();
public void Load()
{
protected ISptLogger<RagfairServer> _logger;
protected RagfairOfferService _ragfairOfferService;
protected RagfairCategoriesService _ragfairCategoriesService;
protected RagfairRequiredItemsService _ragfairRequiredItemsService;
protected LocalisationService _localisationService;
protected RagfairOfferGenerator _ragfairOfferGenerator;
protected ConfigServer _configServer;
protected RagfairConfig _ragfairConfig;
_logger.Info(_localisationService.GetText("ragfair-generating_offers"));
_ragfairOfferGenerator.GenerateDynamicOffers();
Update();
}
public RagfairServer(
ISptLogger<RagfairServer> logger,
RagfairOfferService ragfairOfferService,
RagfairCategoriesService ragfairCategoriesService,
RagfairRequiredItemsService ragfairRequiredItemsService,
LocalisationService localisationService,
RagfairOfferGenerator ragfairOfferGenerator,
ConfigServer configServer)
public void Update()
{
_ragfairOfferService.ExpireStaleOffers();
// Generate trader offers
var traders = GetUpdateableTraders();
foreach (var traderId in traders)
{
_logger = logger;
_ragfairOfferService = ragfairOfferService;
_ragfairCategoriesService = ragfairCategoriesService;
_ragfairRequiredItemsService = ragfairRequiredItemsService;
_localisationService = localisationService;
_ragfairOfferGenerator = ragfairOfferGenerator;
_configServer = configServer;
_ragfairConfig = _configServer.GetConfig<RagfairConfig>();
}
public void Load(){
_logger.Info(_localisationService.GetText("ragfair-generating_offers"));
_ragfairOfferGenerator.GenerateDynamicOffers();
Update();
}
public void Update()
{
_logger.Info($"reimplement me: Ragfairserver.Update()");
// _ragfairOfferService.ExpireStaleOffers();
//
// // Generate trader offers
// var traders = GetUpdateableTraders();
// foreach (var traderId in traders) {
// // Edge case - skip generating fence offers
// if (traderId == Traders.FENCE)
// {
// continue;
// }
//
// if (_ragfairOfferService.TraderOffersNeedRefreshing(traderId))
// {
// _ragfairOfferGenerator.GenerateFleaOffersForTrader(traderId);
// }
// }
//
// // Regenerate expired offers when over threshold limit
// if (_ragfairOfferService.GetExpiredOfferCount() >= _ragfairConfig.Dynamic.ExpiredOfferThreshold)
// {
// var expiredAssortsWithChildren = _ragfairOfferService.GetExpiredOfferAssorts();
// _ragfairOfferGenerator.GenerateDynamicOffers(expiredAssortsWithChildren);
//
// // Clear out expired offers now we've generated them
// _ragfairOfferService.ResetExpiredOffers();
// }
//
// _ragfairRequiredItemsService.BuildRequiredItemTable();
}
/**
* Get traders who need to be periodically refreshed
* @returns string array of traders
*/
public List<string> GetUpdateableTraders()
{
return _ragfairConfig.Traders.Keys.ToList();
}
public Dictionary<string, int> GetAllActiveCategories(
bool fleaUnlocked,
SearchRequestData searchRequestData,
List<RagfairOffer> offers){
return _ragfairCategoriesService.GetCategoriesFromOffers(offers, searchRequestData, fleaUnlocked);
}
/**
* Disable/Hide an offer from flea
* @param offerId
*/
public void HideOffer(string offerId){
var offers = _ragfairOfferService.GetOffers();
var offer = offers.FirstOrDefault((x) => x.Id == offerId);
if (offer is null) {
_logger.Error(_localisationService.GetText("ragfair-offer_not_found_unable_to_hide", offerId));
return;
// Edge case - skip generating fence offers
if (traderId == Traders.FENCE)
{
continue;
}
offer.Locked = true;
if (_ragfairOfferService.TraderOffersNeedRefreshing(traderId))
{
_ragfairOfferGenerator.GenerateFleaOffersForTrader(traderId);
}
}
public RagfairOffer? GetOffer(string offerId) {
return _ragfairOfferService.GetOfferByOfferId(offerId);
// Regenerate expired offers when over threshold limit
if (_ragfairOfferService.GetExpiredOfferCount() >= _ragfairConfig.Dynamic.ExpiredOfferThreshold)
{
var expiredAssortsWithChildren = _ragfairOfferService.GetExpiredOfferAssorts();
_ragfairOfferGenerator.GenerateDynamicOffers(expiredAssortsWithChildren);
// Clear out expired offers now we've generated them
_ragfairOfferService.ResetExpiredOffers();
}
public List<RagfairOffer> GetOffers() {
return _ragfairOfferService.GetOffers();
_ragfairRequiredItemsService.BuildRequiredItemTable();
}
/**
* Get traders who need to be periodically refreshed
* @returns string array of traders
*/
public List<string> GetUpdateableTraders()
{
return _ragfairConfig.Traders.Keys.ToList();
}
public Dictionary<string, int> GetAllActiveCategories(
bool fleaUnlocked,
SearchRequestData searchRequestData,
List<RagfairOffer> offers
)
{
return _ragfairCategoriesService.GetCategoriesFromOffers(offers, searchRequestData, fleaUnlocked);
}
/**
* Disable/Hide an offer from flea
* @param offerId
*/
public void HideOffer(string offerId)
{
var offers = _ragfairOfferService.GetOffers();
var offer = offers.FirstOrDefault((x) => x.Id == offerId);
if (offer is null)
{
_logger.Error(_localisationService.GetText("ragfair-offer_not_found_unable_to_hide", offerId));
return;
}
public void RemoveOfferStack(string offerId, int amount) {
_ragfairOfferService.RemoveOfferStack(offerId, amount);
}
offer.Locked = true;
}
public bool DoesOfferExist(string offerId) {
return _ragfairOfferService.DoesOfferExist(offerId);
}
public RagfairOffer? GetOffer(string offerId)
{
return _ragfairOfferService.GetOfferByOfferId(offerId);
}
public void AddPlayerOffers() {
_ragfairOfferService.AddPlayerOffers();
}
}
public List<RagfairOffer> GetOffers()
{
return _ragfairOfferService.GetOffers();
}
public void RemoveOfferStack(string offerId, int amount)
{
_ragfairOfferService.RemoveOfferStack(offerId, amount);
}
public bool DoesOfferExist(string offerId)
{
return _ragfairOfferService.DoesOfferExist(offerId);
}
public void AddPlayerOffers()
{
_ragfairOfferService.AddPlayerOffers();
}
}
+255 -79
View File
@@ -2,168 +2,344 @@ using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Ragfair;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Routers;
using Core.Servers;
using Core.Utils;
using Core.Utils.Cloners;
using SptCommon.Extensions;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class RagfairOfferService(
ISptLogger<RagfairOfferService> _logger,
TimeUtil _timeUtil,
DatabaseService _databaseService,
RagfairOfferHelper _ragfairOfferHelper ,
RagfairOfferHolder _ragfairOfferHolder,
LocalisationService _localisationService)
ISptLogger<RagfairOfferService> logger,
TimeUtil timeUtil,
HashUtil hashUtil,
DatabaseService databaseService,
SaveServer saveServer,
RagfairServerHelper ragfairServerHelper,
ItemHelper itemHelper,
ProfileHelper profileHelper,
LocalisationService localisationService,
ConfigServer configServer,
ICloner cloner,
RagfairOfferHolder ragfairOfferHolder
)
{
/// <summary>
/// Get all offers
/// </summary>
/// <returns>List of RagfairOffer</returns>
protected bool playerOffersLoaded;
/** Offer id + offer object */
protected Dictionary<string, RagfairOffer> expiredOffers = new();
protected RagfairConfig ragfairConfig = configServer.GetConfig<RagfairConfig>();
/**
* Get all offers
* @returns IRagfairOffer array
*/
public List<RagfairOffer> GetOffers()
{
throw new NotImplementedException();
return ragfairOfferHolder.GetOffers();
}
public RagfairOffer? GetOfferByOfferId(string offerId)
{
throw new NotImplementedException();
return ragfairOfferHolder.GetOfferById(offerId);
}
public List<RagfairOffer> GetOffersOfType(string templateId)
public List<RagfairOffer>? GetOffersOfType(string templateId)
{
throw new NotImplementedException();
return ragfairOfferHolder.GetOffersByTemplate(templateId);
}
public void AddOffer(RagfairOffer offer)
{
throw new NotImplementedException();
ragfairOfferHolder.AddOffer(offer);
}
public void AddOfferToExpired(RagfairOffer staleOffer)
{
throw new NotImplementedException();
expiredOffers[staleOffer.Id] = staleOffer;
}
/// <summary>
/// Get total count of current expired offers
/// </summary>
/// <returns>Number of expired offers</returns>
/**
* Get total count of current expired offers
* @returns Number of expired offers
*/
public int GetExpiredOfferCount()
{
Console.WriteLine($"actually implement me plz: owo: GetExpiredOfferCount");
return 0;
return expiredOffers.Keys.Count;
}
/// <summary>
/// Get a list of lists of expired offer items + children
/// </summary>
/// <returns>Expired offer assorts</returns>
/**
* Get an array of arrays of expired offer items + children
* @returns Expired offer assorts
*/
public List<List<Item>> GetExpiredOfferAssorts()
{
Console.WriteLine($"actually implement me plz: owo: GetExpiredOfferAssorts");
return new List<List<Item>>();
var expiredItems = new List<List<Item>>();
foreach (var expiredOfferId in expiredOffers.Keys)
{
var expiredOffer = expiredOffers[expiredOfferId];
expiredItems.Add(expiredOffer.Items);
}
return expiredItems;
}
/// <summary>
/// Clear out internal expiredOffers dictionary of all items
/// </summary>
/**
* Clear out internal expiredOffers dictionary of all items
*/
public void ResetExpiredOffers()
{
Console.WriteLine($"actually implement me plz: owo: ResetExpiredOffers");
expiredOffers = new();
}
/// <summary>
/// Does the offer exist on the ragfair
/// </summary>
/// <param name="offerId">offer id to check for</param>
/// <returns>offer exists - true</returns>
/**
* Does the offer exist on the ragfair
* @param offerId offer id to check for
* @returns offer exists - true
*/
public bool DoesOfferExist(string offerId)
{
throw new NotImplementedException();
return ragfairOfferHolder.GetOfferById(offerId) != null;
}
/// <summary>
/// Remove an offer from ragfair by offer id
/// </summary>
/// <param name="offerId">Offer id to remove</param>
/**
* Remove an offer from ragfair by offer id
* @param offerId Offer id to remove
*/
public void RemoveOfferById(string offerId)
{
throw new NotImplementedException();
var offer = ragfairOfferHolder.GetOfferById(offerId);
if (offer == null)
{
logger.Warning(localisationService.GetText("ragfair-unable_to_remove_offer_doesnt_exist", offerId));
return;
}
ragfairOfferHolder.RemoveOffer(offer);
}
/// <summary>
/// Reduce size of an offer stack by specified amount
/// </summary>
/// <param name="offerId">Offer to adjust stack size of</param>
/// <param name="amount">How much to deduct from offers stack size</param>
/**
* Reduce size of an offer stack by specified amount
* @param offerId Offer to adjust stack size of
* @param amount How much to deduct from offers stack size
*/
public void RemoveOfferStack(string offerId, int amount)
{
throw new NotImplementedException();
var offer = ragfairOfferHolder.GetOfferById(offerId);
if (offer != null)
{
offer.Items[0].Upd.StackObjectsCount -= amount;
if (offer.Items[0].Upd.StackObjectsCount <= 0)
{
ProcessStaleOffer(offer);
}
}
}
public void RemoveAllOffersByTrader(string traderId)
{
throw new NotImplementedException();
ragfairOfferHolder.RemoveAllOffersByTrader(traderId);
}
/// <summary>
/// Do the trader offers on flea need to be refreshed
/// </summary>
/// <param name="traderId">Trader to check</param>
/// <returns>true if they do</returns>
public bool TraderOffersNeedRefreshing(string traderId)
/**
* Do the trader offers on flea need to be refreshed
* @param traderID Trader to check
* @returns true if they do
*/
public bool TraderOffersNeedRefreshing(string traderID)
{
var trader = _databaseService.GetTrader(traderId);
if (trader?.Base == null) {
_logger.Error(_localisationService.GetText("ragfair-trader_missing_base_file", traderId));
var trader = databaseService.GetTrader(traderID);
if (trader?.Base == null)
{
logger.Error(localisationService.GetText("ragfair-trader_missing_base_file", traderID));
return false;
}
// No value, occurs when first run, trader offers need to be added to flea
trader.Base.RefreshTraderRagfairOffers ??= true;
return trader.Base.RefreshTraderRagfairOffers.GetValueOrDefault(false);
return trader.Base.RefreshTraderRagfairOffers.Value;
}
public void AddPlayerOffers()
{
Console.WriteLine($"actually implement me plz: owo: AddPlayerOffers");
// throw new NotImplementedException();
if (!playerOffersLoaded)
{
foreach (var sessionID in saveServer.GetProfiles().Keys)
{
var pmcData = saveServer.GetProfile(sessionID)?.CharacterData?.PmcData;
if (pmcData?.RagfairInfo == null || pmcData.RagfairInfo.Offers == null)
{
// Profile is wiped
continue;
}
ragfairOfferHolder.AddOffers(pmcData.RagfairInfo.Offers);
}
playerOffersLoaded = true;
}
}
public void ExpireStaleOffers()
{
var time = _timeUtil.GetTimeStamp();
foreach (var staleOffer in _ragfairOfferHolder.GetStaleOffers(time)) {
var time = timeUtil.GetTimeStamp();
foreach (var staleOffer in ragfairOfferHolder.GetStaleOffers(time))
{
ProcessStaleOffer(staleOffer);
}
}
/// <summary>
/// Remove stale offer from flea
/// </summary>
/// <param name="staleOffer">Stale offer to process</param>
/**
* Remove stale offer from flea
* @param staleOffer Stale offer to process
*/
protected void ProcessStaleOffer(RagfairOffer staleOffer)
{
throw new NotImplementedException();
var staleOfferUserId = staleOffer.User.Id;
var isTrader = ragfairServerHelper.IsTrader(staleOfferUserId);
var isPlayer = profileHelper.IsPlayer(staleOfferUserId.RegexReplace("^pmc", ""));
// Skip trader offers, managed by RagfairServer.update()
if (isTrader)
{
return;
}
// Handle dynamic offer
if (!(isTrader || isPlayer))
{
// Dynamic offer
AddOfferToExpired(staleOffer);
}
// Handle player offer - items need returning/XP adjusting. Checking if offer has actually expired or not.
if (isPlayer && staleOffer.EndTime <= timeUtil.GetTimeStamp())
{
ReturnPlayerOffer(staleOffer);
return;
}
// Remove expired existing offer from global offers
RemoveOfferById(staleOffer.Id);
}
protected void ReturnPlayerOffer(RagfairOffer playerOffer)
{
throw new NotImplementedException();
var pmcId = playerOffer.User.Id;
var profile = profileHelper.GetProfileByPmcId(pmcId);
if (profile == null)
{
logger.Error($"Unable to return flea offer {playerOffer.Id} as the profile: {pmcId} could not be found");
return;
}
var offerinProfileIndex = profile.RagfairInfo.Offers.FindIndex((o) => o.Id == playerOffer.Id);
if (offerinProfileIndex == -1)
{
logger.Warning(localisationService.GetText("ragfair-unable_to_find_offer_to_remove", playerOffer.Id));
return;
}
// Reduce player ragfair rep
profile.RagfairInfo.Rating -= databaseService.GetGlobals().Configuration.RagFair.RatingDecreaseCount;
profile.RagfairInfo.IsRatingGrowing = false;
// Increment players 'notSellSum' value
profile.RagfairInfo.NotSellSum ??= 0;
profile.RagfairInfo.NotSellSum += playerOffer.SummaryCost;
var firstOfferItem = playerOffer.Items[0];
if (firstOfferItem.Upd.StackObjectsCount > firstOfferItem.Upd.OriginalStackObjectsCount)
{
playerOffer.Items[0].Upd.StackObjectsCount = firstOfferItem.Upd.OriginalStackObjectsCount;
}
playerOffer.Items[0].Upd.OriginalStackObjectsCount = null;
// Remove player offer from flea
ragfairOfferHolder.RemoveOffer(playerOffer);
// Send failed offer items to player in mail
var unstackedItems = UnstackOfferItems(playerOffer.Items);
// Need to regenerate Ids to ensure returned item(s) have correct parent values
var newParentId = hashUtil.Generate();
foreach (var item in unstackedItems)
{
// Refresh root items' parentIds
if (item.ParentId == "hideout")
{
item.ParentId = newParentId;
}
}
ragfairServerHelper.ReturnItems(profile.SessionId, unstackedItems);
profile.RagfairInfo.Offers.Splice(offerinProfileIndex, 1);
}
/// <summary>
/// Flea offer items are stacked up often beyond the StackMaxSize limit
/// Unstack the items into a list of root items and their children
/// Will create new items equal to the
/// </summary>
/// <param name="items">Offer items to unstack</param>
/// <returns>Unstacked list of items</returns>
/**
* Flea offer items are stacked up often beyond the StackMaxSize limit
* Un stack the items into an array of root items and their children
* Will create new items equal to the
* @param items Offer items to unstack
* @returns Unstacked array of items
*/
protected List<Item> UnstackOfferItems(List<Item> items)
{
throw new NotImplementedException();
var result = new List<Item>();
var rootItem = items[0];
var itemDetails = itemHelper.GetItem(rootItem.Template);
var itemMaxStackSize = itemDetails.Value?.Properties?.StackMaxSize ?? 1;
var totalItemCount = rootItem.Upd?.StackObjectsCount ?? 1;
// Items within stack tolerance, return existing data - no changes needed
if (totalItemCount <= itemMaxStackSize)
{
// Edge case - Ensure items stack count isnt < 1
if (items[0]?.Upd?.StackObjectsCount < 1)
{
items[0].Upd.StackObjectsCount = 1;
}
return items;
}
// Single item with no children e.g. ammo, use existing de-stacking code
if (items.Count == 1)
{
return itemHelper.SplitStack(rootItem);
}
// Item with children, needs special handling
// Force new item to have stack size of 1
for (var index = 0; index < totalItemCount; index++)
{
var itemAndChildrenClone = cloner.Clone(items);
// Ensure upd object exits
itemAndChildrenClone[0].Upd ??= new();
// Force item to be singular
itemAndChildrenClone[0].Upd.StackObjectsCount = 1;
// Ensure items IDs are unique to prevent collisions when added to player inventory
var reparentedItemAndChildren = itemHelper.ReparentItemAndChildren(
itemAndChildrenClone[0],
itemAndChildrenClone
);
itemHelper.RemapRootItemId(reparentedItemAndChildren);
result.AddRange(reparentedItemAndChildren);
}
return result;
}
}
+10 -21
View File
@@ -1,5 +1,7 @@
using Core.Helpers;
using Core.Models.Eft.Ragfair;
using Core.Models.Spt.Config;
using Core.Servers;
using SptCommon.Annotations;
namespace Core.Utils;
@@ -7,36 +9,23 @@ namespace Core.Utils;
[Injectable(InjectionType.Singleton)]
public class RagfairOfferHolder(
RagfairServerHelper ragfairServerHelper,
ProfileHelper profileHelper)
ProfileHelper profileHelper,
ConfigServer configServer)
{
protected Dictionary<string, RagfairOffer> _offersById;
protected Dictionary<string, Dictionary<string, RagfairOffer>> _offersByTemplate;
protected Dictionary<string, Dictionary<string, RagfairOffer>> _offersByTrader;
protected int _maxOffersPerTemplate; //TODO - set from config?
public void SetMaxOffersPerTemplate(int max)
{
_maxOffersPerTemplate = max;
}
protected int _maxOffersPerTemplate = (int) configServer.GetConfig<RagfairConfig>().Dynamic.OfferItemCount.Max;
public RagfairOffer? GetOfferById(string id)
{
if (_offersById.ContainsKey(id))
{
return _offersById[id];
}
return null;
return _offersById.GetValueOrDefault(id);
}
public List<RagfairOffer> GetOffersByTemplate(string templateId)
public List<RagfairOffer>? GetOffersByTemplate(string templateId)
{
if (_offersByTemplate.ContainsKey(templateId))
{
return _offersByTemplate[templateId].Values.ToList();
}
return null;
return _offersByTemplate.TryGetValue(templateId, out var value) ? value.Values.ToList() : null;
}
public List<RagfairOffer> GetOffersByTrader(string traderId)