using System; using System.Collections.Generic; using VOID.Generic.Dict; using System.Linq; using Sirenix.Utilities; using UnityEngine; using VOID.Generic.Objects.Item; namespace VOID.Generic.Util { public static class ItemObjectSortUtil { public static void Sort(ref ItemObject[] items, Func valueGetter, SortDirectionType sortDirection) { if (items.IsNullOrEmpty()) return; // Find items that can be stacked and then merge them (empty leftovers will be destroyed and removed) StackGroups(GetStackGroups(items)); Array.Sort(items, (item1, item2) => { // Empty item slots always to the end if (!item1 && !item2) return 0; if (!item1) return 1; if (!item2) return -1; // Existing items sort by given value getter with given sort direction var result = valueGetter(item1).CompareTo(valueGetter(item2)); if (sortDirection is SortDirectionType.Descending) result *= - 1; return result; }); } /// Returns list of item groups that can be stacked together private static List> GetStackGroups(ItemObject[] items) { var groups = new List>(); foreach (var item in items) { // Ignore: empty, non-stackable, those with max stack size already if (!item) continue; if (!item.itemData.stackable) continue; if (item.itemData.stackSize >= item.itemData.stackSizeMax) continue; // Each group has items that can be stacked together var group = groups.FirstOrDefault(group => group.First().CanStackWith(item)); if (group == null) { group = new List(); groups.Add(group); } group.Add(item); } // Ignore groups with only 1 item return groups.Where(group => group.Count > 1).ToList(); } /// /// 1. Stacks items together as tight as possible /// 2. Destroys and removes items that have no more stacks inside (by invoking event with Remove()) /// private static void StackGroups(List> groups) { foreach (var group in groups) { var stackSize = group.Sum(item => item.itemData.stackSize); var maxStackSize = group.First().itemData.stackSizeMax; var maxStackSizeItems = Mathf.FloorToInt((float)stackSize / maxStackSize); var remainingStackSize = stackSize % maxStackSize; // Items that will have 100% of its stack size group.Take(maxStackSizeItems).ForEach(item => item.itemData.stackSize = maxStackSize); // Remaining of stack will go to next item if (remainingStackSize > 0) group.Skip(maxStackSizeItems).First().itemData.stackSizeMax = maxStackSize; // IMPORTANT! Because we got ref to items here, event caused by Remove() should remove items from list itself // All other items are left without stacks, so they're "empty" - destroy and remove from list group.Skip(maxStackSizeItems).Skip(remainingStackSize > 0 ? 1 : 0).ForEach(item => item.Remove()); } } } }