diff --git a/Libraries/Core/Helpers/RagfairHelper.cs b/Libraries/Core/Helpers/RagfairHelper.cs
index 8c49d730..df0c54fd 100644
--- a/Libraries/Core/Helpers/RagfairHelper.cs
+++ b/Libraries/Core/Helpers/RagfairHelper.cs
@@ -1,54 +1,166 @@
using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Ragfair;
+using Core.Models.Enums;
+using Core.Models.Spt.Config;
+using Core.Models.Utils;
+using Core.Servers;
+using Core.Services;
+using Core.Utils.Cloners;
namespace Core.Helpers;
[Injectable]
-public class RagfairHelper
+public class RagfairHelper(
+ TraderAssortHelper traderAssortHelper,
+ DatabaseService databaseService,
+ HandbookHelper handbookHelper,
+ ItemHelper itemHelper,
+ RagfairLinkedItemService ragfairLinkedItemService,
+ UtilityHelper utilityHelper,
+ ConfigServer configServer,
+ ICloner cloner
+)
{
- ///
- /// Gets currency TAG from TPL
- ///
- /// currency
- /// string
+ protected RagfairConfig ragfairConfig = configServer.GetConfig();
+
+ /**
+ * Gets currency TAG from TPL
+ * @param {string} currency
+ * @returns string
+ */
public string GetCurrencyTag(string currency)
{
- throw new NotImplementedException();
+ switch (currency) {
+ case Money.EUROS:
+ return "EUR";
+ case Money.DOLLARS:
+ return "USD";
+ case Money.ROUBLES:
+ return "RUB";
+ case Money.GP:
+ return "GP";
+ default:
+ return "";
+ }
}
public List FilterCategories(string sessionID, SearchRequestData request)
{
- throw new NotImplementedException();
+ var result = new List();
+
+ // Case: weapon builds
+ if (request.BuildCount != null) {
+ return request.BuildItems.Keys.ToList();
+ }
+
+ // Case: search
+ if (request.LinkedSearchId != null) {
+ var data = ragfairLinkedItemService.GetLinkedItems(request.LinkedSearchId);
+ result = data == null ? [] : [..data];
+ }
+
+ // Case: category
+ if (request.HandbookId != null) {
+ var handbook = GetCategoryList(request.HandbookId);
+
+ if (result.Count != null && result.Count > 0) {
+ result = utilityHelper.ArrayIntersect(result, handbook);
+ } else {
+ result = handbook;
+ }
+ }
+
+ return result;
}
public Dictionary GetDisplayableAssorts(string sessionID)
{
- throw new NotImplementedException();
+ var result = new Dictionary();
+
+ foreach (var traderID in databaseService.GetTraders().Keys) {
+ if (ragfairConfig.Traders.ContainsKey(traderID)) {
+ result[traderID] = traderAssortHelper.GetAssort(sessionID, traderID, true);
+ }
+ }
+
+ return result;
}
protected List GetCategoryList(string handbookId)
{
- throw new NotImplementedException();
+ var result = new List();
+
+ // if its "mods" great-parent category, do double recursive loop
+ if (handbookId == "5b5f71a686f77447ed5636ab") {
+ foreach (var categ in handbookHelper.ChildrenCategories(handbookId)) {
+ foreach (var subcateg in handbookHelper.ChildrenCategories(categ)) {
+ result = [..result, ..handbookHelper.TemplatesWithParent(subcateg)];
+ }
+ }
+
+ return result;
+ }
+
+ // item is in any other category
+ if (handbookHelper.IsCategory(handbookId)) {
+ // list all item of the category
+ result = handbookHelper.TemplatesWithParent(handbookId);
+
+ foreach (var categ in handbookHelper.ChildrenCategories(handbookId)) {
+ result = [..result, ..handbookHelper.TemplatesWithParent(categ)];
+ }
+
+ return result;
+ }
+
+ // its a specific item searched
+ result.Add(handbookId);
+ return result;
}
- ///
- /// Iterate over array of identical items and merge stack count
- /// Ragfair allows abnormally large stacks.
- ///
+ /**
+ * Iterate over array of identical items and merge stack count
+ * Ragfair allows abnormally large stacks.
+ */
public List- MergeStackable(List
- items)
{
- throw new NotImplementedException();
+ var list = new List
- ();
+ Item rootItem = null;
+
+ foreach (var item in items) {
+ var itemFixed = itemHelper.FixItemStackCount(item);
+
+ var isChild = items.Any(it => it.Id == itemFixed.ParentId);
+ if (!isChild) {
+ if (rootItem == null) {
+ rootItem = cloner.Clone(itemFixed);
+ rootItem.Upd.OriginalStackObjectsCount = rootItem.Upd.StackObjectsCount;
+ } else {
+ rootItem.Upd.StackObjectsCount += itemFixed.Upd.StackObjectsCount;
+ list.Add(itemFixed);
+ }
+ } else {
+ list.Add(itemFixed);
+ }
+ }
+
+ return [rootItem, ..list];
}
- ///
- /// Return the symbol for a currency
- /// e.g. 5449016a4bdc2d6f028b456f return ₽
- ///
- /// currency to get symbol for
- /// symbol of currency
+ /**
+ * Return the symbol for a currency
+ * e.g. 5449016a4bdc2d6f028b456f return ₽
+ * @param currencyTpl currency to get symbol for
+ * @returns symbol of currency
+ */
public string GetCurrencySymbol(string currencyTpl)
{
- throw new NotImplementedException();
+ return currencyTpl switch
+ {
+ Money.EUROS => "€",
+ Money.DOLLARS => "$",
+ _ => "₽"
+ };
}
}
diff --git a/Libraries/Core/Models/Eft/Common/Tables/Item.cs b/Libraries/Core/Models/Eft/Common/Tables/Item.cs
index 2a374f05..b15df3c0 100644
--- a/Libraries/Core/Models/Eft/Common/Tables/Item.cs
+++ b/Libraries/Core/Models/Eft/Common/Tables/Item.cs
@@ -49,7 +49,7 @@ public record ItemLocation
public record Upd
{
public UpdBuff? Buff { get; set; }
- public int? OriginalStackObjectsCount { get; set; }
+ public double? OriginalStackObjectsCount { get; set; }
public UpdTogglable? Togglable { get; set; }
public UpdMap? Map { get; set; }
public UpdTag? Tag { get; set; }
diff --git a/Libraries/Core/Models/Eft/Ragfair/SearchRequestData.cs b/Libraries/Core/Models/Eft/Ragfair/SearchRequestData.cs
index 9a590696..05698e9f 100644
--- a/Libraries/Core/Models/Eft/Ragfair/SearchRequestData.cs
+++ b/Libraries/Core/Models/Eft/Ragfair/SearchRequestData.cs
@@ -64,7 +64,7 @@ public record SearchRequestData : IRequestData
public string? NeededSearchId { get; set; }
[JsonPropertyName("buildItems")]
- public BuildItems? BuildItems { get; set; }
+ public Dictionary? BuildItems { get; set; }
[JsonPropertyName("buildCount")]
public int? BuildCount { get; set; }
@@ -82,8 +82,3 @@ public enum OfferOwnerType
TRADEROWNERTYPE = 1,
PLAYEROWNERTYPE = 2
}
-
-public record BuildItems
-{
- // Define properties for BuildItems here if needed
-}
diff --git a/Libraries/Core/Services/RagfairLinkedItemService.cs b/Libraries/Core/Services/RagfairLinkedItemService.cs
index 5a76487d..3eb7d433 100644
--- a/Libraries/Core/Services/RagfairLinkedItemService.cs
+++ b/Libraries/Core/Services/RagfairLinkedItemService.cs
@@ -1,54 +1,137 @@
-using SptCommon.Annotations;
+using Core.Helpers;
+using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
+using Core.Models.Enums;
+using Core.Models.Utils;
+using SptCommon.Extensions;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
-public class RagfairLinkedItemService
+public class RagfairLinkedItemService(
+ DatabaseService databaseService,
+ ItemHelper itemHelper,
+ ISptLogger logger
+)
{
+ protected Dictionary> linkedItemsCache = new();
+
public HashSet GetLinkedItems(string linkedSearchId)
{
- throw new NotImplementedException();
+ if (!linkedItemsCache.Keys.Any()) {
+ BuildLinkedItemTable();
+ }
+
+ return linkedItemsCache[linkedSearchId];
}
- ///
- /// Use ragfair linked item service to get an array of items that can fit on or in designated itemtpl
- ///
- /// Item to get sub-items for
- /// TemplateItem array
+ /**
+ * Use ragfair linked item service to get an array of items that can fit on or in designated itemtpl
+ * @param itemTpl Item to get sub-items for
+ * @returns ITemplateItem array
+ */
public List GetLinkedDbItems(string itemTpl)
{
- throw new NotImplementedException();
+ var linkedItemsToWeaponTpls = GetLinkedItems(itemTpl);
+ return linkedItemsToWeaponTpls.Aggregate(new List(), (result, linkedTpl) => {
+ var itemDetails = itemHelper.GetItem(linkedTpl);
+ if (itemDetails.Key) {
+ result.Add(itemDetails.Value);
+ } else {
+ logger.Warning($"Item {itemTpl} has invalid linked item {linkedTpl}");
+ }
+ return result;
+ });
}
- ///
- /// Create Dictionary of every item and the items associated with it
- ///
+ /**
+ * Create Dictionary of every item and the items associated with it
+ */
protected void BuildLinkedItemTable()
{
- throw new NotImplementedException();
+ var linkedItems = new Dictionary>();
+
+ foreach (var item in databaseService.GetItems().Values) {
+ var itemLinkedSet = GetLinkedItems(linkedItems, item.Id);
+
+ ApplyLinkedItems(GetFilters(item, "Slots"), item, itemLinkedSet);
+ ApplyLinkedItems(GetFilters(item, "Chambers"), item, itemLinkedSet);
+ ApplyLinkedItems(GetFilters(item, "Cartridges"), item, itemLinkedSet);
+
+ // Edge case, ensure ammo for revolves is included
+ if (item.Parent == BaseClasses.REVOLVER) {
+ // Find magazine for revolver
+ AddRevolverCylinderAmmoToLinkedItems(item, itemLinkedSet);
+ }
+ }
+
+ linkedItemsCache = linkedItems;
}
- ///
- /// Add ammo to revolvers linked item dictionary
- ///
- /// Revolvers cylinder
- ///
- protected void AddRevolverCylinderAmmoToLinkedItems(
- TemplateItem cylinder,
- Action
> applyLinkedItems)
+ protected void ApplyLinkedItems(List items, TemplateItem item, HashSet itemLinkedSet)
{
- throw new NotImplementedException();
+ foreach (var linkedItemId in items) {
+ itemLinkedSet.Add(linkedItemId);
+ GetLinkedItems(linkedItemId).Add(item.Id);
+ }
}
- ///
- /// Scans a given slot type for filters and returns them as a List
- ///
- ///
- ///
- /// List of ids
+ protected HashSet GetLinkedItems(Dictionary> linkedItems, string id)
+ {
+ if (!linkedItems.ContainsKey(id)) {
+ linkedItems.Add(id, []);
+ }
+ return linkedItems[id];
+ }
+
+ /**
+ * Add ammo to revolvers linked item dictionary
+ * @param cylinder Revolvers cylinder
+ * @param applyLinkedItems
+ */
+ protected void AddRevolverCylinderAmmoToLinkedItems(TemplateItem cylinder, HashSet itemLinkedSet)
+ {
+ var cylinderMod = cylinder.Properties.Slots?.FirstOrDefault((x) => x.Name == "mod_magazine");
+ if (cylinderMod != null) {
+ // Get the first cylinder filter tpl
+ var cylinderTpl = cylinderMod.Props?.Filters?[0].Filter?[0];
+ if (!string.IsNullOrEmpty(cylinderTpl)) {
+ // Get db data for cylinder tpl, add found slots info (camora_xxx) to linked items on revolver weapon
+ var cylinderItem = itemHelper.GetItem(cylinderTpl).Value;
+ ApplyLinkedItems(GetFilters(cylinderItem, "Slots"), cylinder, itemLinkedSet);
+ }
+ }
+ }
+
+ /**
+ * Scans a given slot type for filters and returns them as a Set
+ * @param item
+ * @param slot
+ * @returns array of ids
+ */
protected List GetFilters(TemplateItem item, string slot)
{
- throw new NotImplementedException();
+ var properties = item.Properties.GetAllPropsAsDict();
+ if (!properties.TryGetValue(slot, out var value) || value == null) {
+ // item slot doesnt exist
+ return [];
+ }
+/*
+ var filters = new List();
+ // I have no fucking clue wtf is happening here... god help us all and anyone who has to read this code
+ foreach (var sub in properties[slot].GetAllPropsAsDict()) {
+ if (!("_props" in sub && "filters" in sub._props)) {
+ // not a filter
+ continue;
+ }
+
+ for (var filter of sub._props.filters) {
+ for (var f of filter.Filter) {
+ filters.push(f);
+ }
+ }
+ }
+*/
+ return new List();
}
}
diff --git a/SptCommon/Extensions/ObjectExtensions.cs b/SptCommon/Extensions/ObjectExtensions.cs
index a481be01..9d31f759 100644
--- a/SptCommon/Extensions/ObjectExtensions.cs
+++ b/SptCommon/Extensions/ObjectExtensions.cs
@@ -65,9 +65,9 @@ namespace SptCommon.Extensions
return result;
}
- public static Dictionary GetAllPropsAsDict(this object? obj)
+ public static Dictionary GetAllPropsAsDict(this object? obj)
{
- var result = new Dictionary();
+ var result = new Dictionary();
var props = obj.GetType().GetProperties();
foreach (var prop in props)