ScrollInventory.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Assets.HeroEditor4D.Common.Scripts.Common;
  6. using Assets.HeroEditor4D.InventorySystem.Scripts.Data;
  7. using Assets.HeroEditor4D.InventorySystem.Scripts.Enums;
  8. using UnityEngine;
  9. using UnityEngine.UI;
  10. namespace Assets.HeroEditor4D.InventorySystem.Scripts.Elements
  11. {
  12. /// <summary>
  13. /// Scrollable item container that can display item list. Automatic vertical scrolling.
  14. /// </summary>
  15. public class ScrollInventory : ItemContainer
  16. {
  17. [Tooltip("Sort items automatically using SortingFunc (can be redefined).")]
  18. public bool AutoSorting;
  19. [Tooltip("Add an extra empty row or a column at the end.")]
  20. public bool Extend;
  21. [Header("UI")]
  22. public ScrollRect ScrollRect;
  23. public GridLayoutGroup Grid;
  24. public InventoryItem ItemPrefab;
  25. public Func<Item, int> SortingFunc = item => TypePriority.IndexOf(item.Params.Type); // You can override this.
  26. public Func<Item, bool> FilterFunc; // You can override this.
  27. public Action OnRefresh;
  28. #if TAP_HEROES
  29. public TMPro.TextMeshProUGUI Gold;
  30. #endif
  31. private static readonly List<ItemType> TypePriority = new List<ItemType>
  32. {
  33. ItemType.Currency,
  34. ItemType.Container,
  35. ItemType.Booster,
  36. ItemType.Supply,
  37. ItemType.Weapon,
  38. ItemType.Helmet,
  39. ItemType.Armor,
  40. ItemType.Vest,
  41. ItemType.Bracers,
  42. ItemType.Leggings,
  43. ItemType.Shield,
  44. ItemType.Fragment,
  45. ItemType.Backpack,
  46. ItemType.Jewelry,
  47. ItemType.Loot,
  48. ItemType.Recipe,
  49. ItemType.Material
  50. };
  51. private readonly List<InventoryItem> _itemInstances = new List<InventoryItem>(); // Reusing instances to reduce Instantiate() calls.
  52. public void Initialize(ref List<Item> items, Item selected, bool reset = false)
  53. {
  54. base.Initialize(ref items, selected);
  55. }
  56. public void Initialize(ref List<Item> items)
  57. {
  58. base.Initialize(ref items);
  59. ResetNormalizedPosition();
  60. }
  61. public void SelectItem(Item item)
  62. {
  63. _itemInstances.FirstOrDefault(i => i.Item == item)?.Select(true);
  64. }
  65. public bool SelectAny()
  66. {
  67. var any = _itemInstances.FirstOrDefault(i => i.Item != null);
  68. if (any == null) return false;
  69. any.Select(true);
  70. return true;
  71. }
  72. public void SetTypeFilter(string input)
  73. {
  74. var type = input.ToEnum<ItemType>();
  75. SetTypeFilter(new List<ItemType> { type });
  76. }
  77. public void SetTypeFilter(List<ItemType> types)
  78. {
  79. FilterFunc = item => types.Contains(item.Params.Type);
  80. Refresh(null);
  81. }
  82. public void UnsetFilter()
  83. {
  84. FilterFunc = null;
  85. Refresh(null);
  86. }
  87. public override void Refresh(Item selected)
  88. {
  89. if (Items == null) return;
  90. List<Item> items;
  91. if (AutoSorting && SortingFunc != null)
  92. {
  93. items = new List<Item>();
  94. var groups = Items.OrderBy(SortingFunc).ToList().GroupBy(i => i.Params.Type);
  95. foreach (var group in groups)
  96. {
  97. items.AddRange(group.OrderBy(i => i.Params.Class).ThenBy(i => i.Params.Price));
  98. }
  99. }
  100. else
  101. {
  102. items = Items.ToList();
  103. }
  104. if (FilterFunc != null)
  105. {
  106. items.RemoveAll(i => !FilterFunc(i));
  107. }
  108. foreach (var instance in _itemInstances)
  109. {
  110. instance.Reset();
  111. instance.SetActive(false);
  112. }
  113. var toggleGroup = GetComponentInParent<ToggleGroup>(includeInactive: true);
  114. for (var i = 0; i < items.Count; i++)
  115. {
  116. var instance = GetItemInstance();
  117. instance.transform.SetSiblingIndex(i);
  118. instance.Initialize(items[i], toggleGroup);
  119. instance.Count.SetActive(Stacked);
  120. if (AutoSelect) instance.Select(items[i] == selected);
  121. }
  122. var columns = 0;
  123. var rows = 0;
  124. switch (Grid.constraint)
  125. {
  126. case GridLayoutGroup.Constraint.FixedColumnCount:
  127. {
  128. var height = Mathf.FloorToInt((ScrollRect.GetComponent<RectTransform>().rect.height + Grid.spacing.y) / (Grid.cellSize.y + Grid.spacing.y));
  129. columns = Grid.constraintCount;
  130. rows = Mathf.Max(height, Mathf.FloorToInt((float) items.Count / columns));
  131. if (Extend) rows++;
  132. break;
  133. }
  134. case GridLayoutGroup.Constraint.FixedRowCount:
  135. {
  136. var width = Mathf.FloorToInt((ScrollRect.GetComponent<RectTransform>().rect.width + Grid.spacing.x) / (Grid.cellSize.x + Grid.spacing.x));
  137. rows = Grid.constraintCount;
  138. columns = Mathf.Max(width, Mathf.FloorToInt((float) items.Count / rows));
  139. if (Extend) columns++;
  140. break;
  141. }
  142. }
  143. for (var i = items.Count; i < columns * rows; i++)
  144. {
  145. var instance = GetItemInstance();
  146. instance.Initialize(null);
  147. }
  148. OnRefresh?.Invoke();
  149. #if TAP_HEROES
  150. var gold = Items.Where(i => i.Id == "Gold").Sum(i => i.Count);
  151. Gold?.SetText($"{gold} <sprite=0>");
  152. TapHeroes.Scripts.Interface.Elements.ItemComparer.Compare(_itemInstances);
  153. #endif
  154. }
  155. private InventoryItem GetItemInstance()
  156. {
  157. var instance = _itemInstances.FirstOrDefault(i => !i.gameObject.activeSelf);
  158. if (instance == null)
  159. {
  160. instance = Instantiate(ItemPrefab, Grid.transform);
  161. _itemInstances.Add(instance);
  162. }
  163. else
  164. {
  165. instance.gameObject.SetActive(true);
  166. }
  167. return instance;
  168. }
  169. public InventoryItem FindItem(Item item)
  170. {
  171. return _itemInstances.SingleOrDefault(i => i.gameObject.activeSelf && i.Item != null && i.Item.Hash == item.Hash);
  172. }
  173. public InventoryItem FindItem(string itemId)
  174. {
  175. return _itemInstances.SingleOrDefault(i => i.gameObject.activeSelf && i.Item != null && i.Item.Id == itemId);
  176. }
  177. public void ResetNormalizedPosition()
  178. {
  179. if (ScrollRect.horizontal) ScrollRect.horizontalNormalizedPosition = 0;
  180. if (ScrollRect.vertical) ScrollRect.verticalNormalizedPosition = 1;
  181. }
  182. public IEnumerator SnapTo(RectTransform target, bool horizontal = true, bool vertical = true)
  183. {
  184. yield return null;
  185. Canvas.ForceUpdateCanvases();
  186. var pos = (Vector2) ScrollRect.transform.InverseTransformPoint(ScrollRect.content.position) - (Vector2) ScrollRect.transform.InverseTransformPoint(target.position);
  187. if (!horizontal) pos.x = ScrollRect.content.anchoredPosition.x;
  188. if (!vertical) pos.y = ScrollRect.content.anchoredPosition.y;
  189. ScrollRect.content.anchoredPosition = pos;
  190. }
  191. }
  192. }