more stuff fixed
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user