add compareUtil (needs implementing), start implementing ItemHelper

This commit is contained in:
CWX
2025-01-13 12:22:34 +00:00
parent a9553aa101
commit 2d4cdce6d5
2 changed files with 256 additions and 14 deletions
+244 -14
View File
@@ -2,12 +2,74 @@
using Core.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Helpers;
[Injectable]
public class ItemHelper
{
private readonly ILogger _logger;
private readonly HashUtil _hashUtil;
private readonly JsonUtil _jsonUtil;
private readonly RandomUtil _randomUtil;
private readonly MathUtil _mathUtil;
private readonly DatabaseService _databaseService;
private readonly HandbookHelper _handbookHelper;
private readonly ItemBaseClassService _itemBaseClassService;
private readonly ItemFilterService _itemFilterService;
private readonly LocalisationService _localisationService;
private readonly LocaleService _localeService;
private readonly CompareUtil _compareUtil;
private readonly ICloner _cloner;
private readonly List<string> _defaultInvalidBaseTypes =
[
BaseClasses.LOOT_CONTAINER,
BaseClasses.MOB_CONTAINER,
BaseClasses.STASH,
BaseClasses.SORTING_TABLE,
BaseClasses.INVENTORY,
BaseClasses.STATIONARY_CONTAINER,
BaseClasses.POCKETS
];
public ItemHelper
(
ILogger logger,
HashUtil hashUtil,
JsonUtil jsonUtil,
RandomUtil randomUtil,
MathUtil mathUtil,
DatabaseService databaseService,
HandbookHelper handbookHelper,
ItemBaseClassService itemBaseClassService,
ItemFilterService itemFilterService,
LocalisationService localisationService,
LocaleService localeService,
CompareUtil compareUtil,
ICloner cloner
)
{
_logger = logger;
_hashUtil = hashUtil;
_jsonUtil = jsonUtil;
_randomUtil = randomUtil;
_mathUtil = mathUtil;
_databaseService = databaseService;
_handbookHelper = handbookHelper;
_itemBaseClassService = itemBaseClassService;
_itemFilterService = itemFilterService;
_localisationService = localisationService;
_localeService = localeService;
_compareUtil = compareUtil;
_cloner = cloner;
}
/**
* Does the provided pool of items contain the desired item
* @param itemPool Item collection to check
@@ -219,7 +281,7 @@ public class ItemHelper
* @param tpl items template id to look up
* @returns bool - is valid + template item object as array
*/
public (bool, Dictionary<string, TemplateItem>) GetItem(string tpl)
public KeyValuePair<bool, TemplateItem> GetItem(string tpl)
{
throw new NotImplementedException();
}
@@ -409,13 +471,111 @@ public class ItemHelper
/// <param name="insuredItems">Insured items that should not have their IDs replaced</param>
/// <param name="fastPanel">Quick slot panel</param>
/// <returns>List<Item></returns>
public List<Item> ReplaceIDs(
List<Item> originalItems,
PmcData pmcData = null,
List<InsuredItem> insuredItems = null,
object fastPanel = null)
public List<Item> ReplaceIDs(List<Item> originalItems, PmcData pmcData = null, List<InsuredItem> insuredItems = null,
Dictionary<string, string> fastPanel = null)
{
throw new NotImplementedException();
var items = _cloner.Clone(originalItems);
var serialisedInventory = _jsonUtil.Serialize(items);
var hideoutAreaStashes = pmcData?.Inventory?.HideoutAreaStashes ?? new();
foreach (var item in items)
{
if (pmcData != null)
{
// Insured items should not be renamed. Only works for PMCs.
if (insuredItems?.FirstOrDefault(i => i.ItemId == item.Id) != null)
continue;
// Do not replace the IDs of specific types of items.
if (item.Id == pmcData?.Inventory?.Equipment ||
item.Id == pmcData?.Inventory?.QuestRaidItems ||
item.Id == pmcData?.Inventory?.QuestStashItems ||
item.Id == pmcData?.Inventory?.SortingTable ||
item.Id == pmcData?.Inventory?.Stash ||
item.Id == pmcData?.Inventory?.HideoutCustomizationStashId ||
(hideoutAreaStashes?.ContainsKey(item.Id) ?? false))
{
continue;
}
}
// Replace the ID of the item in the serialised inventory using a regular expression.
var oldId = item.Id;
var newId = _hashUtil.Generate();
serialisedInventory = serialisedInventory.Replace(oldId, newId); // Node uses regex with "g" flag to replace all instances
// Also replace in quick slot if the old ID exists.
if (fastPanel != null)
{
foreach (var itemSlot in fastPanel)
{
if (fastPanel[itemSlot.Key] == oldId)
fastPanel[itemSlot.Key] = fastPanel[itemSlot.Key].Replace(oldId, newId); // Node uses regex with "g" flag to replace all instances
}
}
}
items = _jsonUtil.Deserialize<List<Item>>(serialisedInventory);
// fix dupe id's
var dupes = new Dictionary<string, double?>();
var newParents = new Dictionary<string, List<Item>>();
var childrenMapping = new Dictionary<string, Dictionary<string, double?>>();
var oldToNewIds = new Dictionary<string, List<string>>();
// Finding duplicate IDs involves scanning the item three times.
// First scan - Check which ids are duplicated.
// Second scan - Map parents to items.
// Third scan - Resolve IDs.
foreach (var item in items)
dupes[item.Id] = (dupes[item.Id] ?? 0) + 1;
foreach (var item in items)
{
// register the parents
if (dupes[item.Id] > 1)
{
var newId = _hashUtil.Generate();
newParents.Add(item.ParentId, newParents[item.ParentId] ?? new());
newParents[item.ParentId].Add(item);
oldToNewIds[item.Id] = oldToNewIds[item.Id] ?? new();
oldToNewIds[item.Id].Add(newId);
}
}
foreach (var item in items)
{
if (dupes[item.Id] > 1)
{
var oldId = item.Id;
oldToNewIds[oldId].RemoveAt(0);
var newId = oldToNewIds[oldId][0];
item.Id = newId;
// Extract one of the children that's also duplicated.
if (newParents.ContainsKey(oldId) && newParents[oldId].Count > 0)
{
childrenMapping[newId] = new();
for (int i = 0; i < newParents[oldId].Count; i++)
{
// Make sure we haven't already assigned another duplicate child of
// same slot and location to this parent.
var childId = GetChildId(newParents[oldId][i]);
if (!childrenMapping.ContainsKey(childId))
{
childrenMapping[newId][childId] = 1;
newParents[oldId][i].ParentId = newId;
// Some very fucking sketchy stuff on this childIndex
// No clue wth was that childIndex supposed to be, but its not
newParents[oldId].RemoveAt(i);
}
}
}
}
}
return items;
}
/// <summary>
@@ -425,7 +585,13 @@ public class ItemHelper
/// <param name="items">The list of items to mark as FiR</param>
public void SetFoundInRaid(List<Item> items)
{
throw new NotImplementedException();
foreach (var item in items)
{
if (item.Upd == null)
item.Upd = new();
item.Upd.SpawnedInSession = true;
}
}
/// <summary>
@@ -436,7 +602,27 @@ public class ItemHelper
/// <returns>bool Match found</returns>
public bool DoesItemOrParentsIdMatch(string tpl, List<string> tplsToCheck)
{
throw new NotImplementedException();
var itemDetails = GetItem(tpl);
var itemExists = itemDetails.Key;
var item = itemDetails.Value;
// not an item, drop out
if (!itemExists)
return false;
// no parent to check
if (item.Parent == null)
return false;
// Does templateId match any values in tplsToCheck array
if (tplsToCheck.Contains(item.Id))
return true;
// check items parent with same method
if (tplsToCheck.Contains(item.Parent))
return true;
return DoesItemOrParentsIdMatch(item.Parent, tplsToCheck);
}
/// <summary>
@@ -446,7 +632,11 @@ public class ItemHelper
/// <returns>true if item is flagged as quest item</returns>
public bool IsQuestItem(string tpl)
{
throw new NotImplementedException();
var itemDetails = GetItem(tpl);
if (itemDetails.Key && itemDetails.Value.Properties.QuestItem != null)
return true;
return false;
}
/// <summary>
@@ -459,18 +649,56 @@ public class ItemHelper
/// <returns>True if the item is actually moddable, false if it is not, and null if the check cannot be performed.</returns>
public bool? IsRaidModdable(Item item, Item parent)
{
throw new NotImplementedException();
// This check requires the item to have the slotId property populated.
if (item.SlotId == null)
return null;
var itemTemplate = GetItem(item.Template);
var parentTemplate = GetItem(parent.Template);
// Check for RaidModdable property on the item template.
var isNotRaidModdable = false;
if (itemTemplate.Key)
isNotRaidModdable = itemTemplate.Value?.Properties?.RaidModdable == false;
// Check to see if the slot that the item is attached to is marked as required in the parent item's template.
var isRequiredSlot = false;
if (parentTemplate.Key && parentTemplate.Value?.Properties?.Slots != null)
isRequiredSlot = parentTemplate.Value?.Properties?.Slots?.Any(slot =>
slot?.Name == item?.SlotId &&
(slot?.Required ?? false)
) ?? false;
return itemTemplate.Key && parentTemplate.Key && (isNotRaidModdable || isRequiredSlot);
}
/// <summary>
/// Retrieves the main parent item for a given attachment item.
///
/// This method traverses up the hierarchy of items starting from a given `itemId`, until it finds the main parent
/// item that is not an attached attachment itself. In other words, if you pass it an item id of a suppressor, it
/// will traverse up the muzzle brake, barrel, upper receiver, and return the gun that the suppressor is ultimately
/// attached to, even if that gun is located within multiple containers.
///
/// It's important to note that traversal is expensive, so this method requires that you pass it a Map of the items
/// to traverse, where the keys are the item IDs and the values are the corresponding Item objects. This alleviates
/// some of the performance concerns, as it allows for quick lookups of items by ID.
/// </summary>
/// <param name="itemId">The unique identifier of the item for which to find the main parent.</param>
/// <param name="itemsMap">A Dictionary containing item IDs mapped to their corresponding Item objects for quick lookup.</param>
/// <returns>The Item object representing the top-most parent of the given item, or null if no such parent exists.</returns>
public Item GetAttachmentMainParent(string itemId, Dictionary<string, Item> itemsMap)
{
throw new NotImplementedException();
var currentItem = itemsMap.FirstOrDefault(x => x.Key == itemId).Value;
while (currentItem != null && IsAttachmentAttached(currentItem))
{
currentItem = itemsMap.FirstOrDefault(x => x.Key == currentItem.ParentId).Value;
if (currentItem == null)
return null;
}
return currentItem;
}
/**
@@ -481,7 +709,9 @@ public class ItemHelper
*/
public bool IsAttachmentAttached(Item item)
{
throw new NotImplementedException();
// TODO: actually implement
return true;
}
/**
@@ -806,7 +1036,7 @@ public class ItemSize
{
[JsonPropertyName("width")]
public double Width { get; set; }
[JsonPropertyName("height")]
public double Height { get; set; }
}
+12
View File
@@ -0,0 +1,12 @@
using Core.Annotations;
namespace Core.Utils;
[Injectable]
public class CompareUtil
{
public bool RecursiveCompare(object v1, object v2)
{
throw new NotImplementedException();
}
}