using SPTarkov.Server.Core.Models.Eft.Common.Tables; namespace SPTarkov.Server.Core.Extensions { public static class ItemExtensions { /// /// This method will compare two items and see if they are equivalent /// This method will NOT compare IDs on the items /// /// first item to compare /// second item to compare /// Upd properties to compare between the items /// true if they are the same public static bool IsSameItem( this Item item1, Item item2, HashSet? compareUpdProperties = null ) { // Different tpl == different item if (item1.Template != item2.Template) { return false; } // Both lack upd object + same tpl = same if (item1.Upd is null && item2.Upd is null) { return true; } // item1 lacks upd, item2 has one if (item1.Upd is null && item2.Upd is not null) { return false; } // item1 has upd, item2 lacks one if (item1.Upd is not null && item2.Upd is null) { return false; } // key = Upd property Type as string, value = comparison function that returns bool var comparers = new Dictionary> { { "Key", (upd1, upd2) => upd1.Key?.NumberOfUsages == upd2.Key?.NumberOfUsages }, { "Buff", (upd1, upd2) => upd1.Buff?.Value == upd2.Buff?.Value && upd1.Buff?.BuffType == upd2.Buff?.BuffType }, { "CultistAmulet", (upd1, upd2) => upd1.CultistAmulet?.NumberOfUsages == upd2.CultistAmulet?.NumberOfUsages }, { "Dogtag", (upd1, upd2) => upd1.Dogtag?.ProfileId == upd2.Dogtag?.ProfileId }, { "FaceShield", (upd1, upd2) => upd1.FaceShield?.Hits == upd2.FaceShield?.Hits }, { "Foldable", (upd1, upd2) => upd1.Foldable?.Folded.GetValueOrDefault(false) == upd2.Foldable?.Folded.GetValueOrDefault(false) }, { "FoodDrink", (upd1, upd2) => upd1.FoodDrink?.HpPercent == upd2.FoodDrink?.HpPercent }, { "MedKit", (upd1, upd2) => upd1.MedKit?.HpResource == upd2.MedKit?.HpResource }, { "RecodableComponent", (upd1, upd2) => upd1.RecodableComponent?.IsEncoded == upd2.RecodableComponent?.IsEncoded }, { "RepairKit", (upd1, upd2) => upd1.RepairKit?.Resource == upd2.RepairKit?.Resource }, { "Resource", (upd1, upd2) => upd1.Resource?.UnitsConsumed == upd2.Resource?.UnitsConsumed }, }; // Choose above keys or passed in keys to compare items with var valuesToCompare = compareUpdProperties?.Count > 0 ? compareUpdProperties : comparers.Keys.ToHashSet(); foreach (var propertyName in valuesToCompare) { if (!comparers.TryGetValue(propertyName, out var comparer)) // Key not found, skip { continue; } if (!comparer(item1.Upd, item2.Upd)) { return false; } } return true; } /// /// Check if item is stored inside a container /// /// Item to check is inside of container /// Name of slot to check item is in e.g. SecuredContainer/Backpack /// Inventory with child parent items to check /// True when item is in container public static bool ItemIsInsideContainer( this Item itemToCheck, string desiredContainerSlotId, IEnumerable items ) { // Get items parent var parent = items.FirstOrDefault(item => item.Id.Equals(itemToCheck.ParentId, StringComparison.OrdinalIgnoreCase) ); if (parent is null) // No parent, end of line, not inside container { return false; } if (parent.SlotId == desiredContainerSlotId) { return true; } return parent.ItemIsInsideContainer(desiredContainerSlotId, items); } /// /// Get the size of a stack, return 1 if no stack object count property found /// /// Item to get stack size of /// size of stack public static int GetItemStackSize(this Item item) { if (item.Upd?.StackObjectsCount is not null) { return (int)item.Upd.StackObjectsCount; } return 1; } /// /// Create a dictionary from a collection of items, keyed by item id /// /// Collection of items /// Dictionary of items public static Dictionary GenerateItemsMap(this IEnumerable items) { // Convert list to dictionary, keyed by items Id return items.ToDictionary(item => item.Id); } /// /// Adopts orphaned items by resetting them as root "hideout" items. Helpful in situations where a parent has been /// deleted from a group of items and there are children still referencing the missing parent. This method will /// remove the reference from the children to the parent and set item properties to root values. /// /// The ID of the "root" of the container /// Array of Items that should be adjusted /// Returns Array of Items that have been adopted public static List AdoptOrphanedItems(this List items, string rootId) { foreach (var item in items) { // Check if the item's parent exists. var parentExists = items.Any(parentItem => parentItem.Id.Equals(item.ParentId, StringComparison.OrdinalIgnoreCase) ); // If the parent does not exist and the item is not already a 'hideout' item, adopt the orphaned item by // setting the parent ID to the PMCs inventory equipment ID, the slot ID to 'hideout', and remove the location. if (!parentExists && item.ParentId != rootId && item.SlotId != "hideout") { item.ParentId = rootId; item.SlotId = "hideout"; item.Location = null; } } return items; } } }