using System; using Sirenix.OdinInspector; using UnityEngine; using VOID.Generic.Dict; using VOID.Generic.Objects.Abstract.Events; using VOID.Generic.Objects.Item; using VOID.Generic.Objects.Item.Events; using VOID.Generic.Objects.Unit.Events; using VOID.Generic.Util; namespace VOID.Generic.Objects.Unit { /// /// Module of which manage currently carried items by this unit. /// [Serializable] public class UnitObjectBackpack : ObjectComponent { #region Initialize protected override void SelfInitialize() { // Get empty GameObject where all items from backpack are "attached" - OR - create if missing _backpackGameObject = parent.transform.Find("_backpack")?.gameObject; if (_backpackGameObject == null) { _backpackGameObject = new GameObject("_backpack"); _backpackGameObject.transform.parent = parent.gameObject.transform; _backpackGameObject.transform.localPosition = Vector3.zero; } } protected override void Initialize() { // Instantiate all prefabs for (var i = 0; i < items.Length; i++) { if (items[i] == null) continue; // Instantiate prefab and get references to its GameObject and ItemObject FakePickOnInit(i, items[i]); } Resize(); } /// /// Only when is initializing:
/// - Instantiate item if it is prefab asset
/// - Inform it by events that it has been picked
/// - Finally just resume with
///
private void FakePickOnInit(int itemIndex, ItemObject item) { // If item is prefab asset then we need to instantiate it first if (!item.gameObject.scene.IsValid()) item = UnityEngine.Object.Instantiate(item.gameObject).GetComponent(); var pickEvent = new PickEvent { item = item, unit = parent }; item.itemEvents.onPickedBefore.Invoke(pickEvent); item.itemEvents.onPickedAfter.Invoke(pickEvent); // Make this slot empty so we can rightfully start whole "Take" machine items[itemIndex] = null; Take(itemIndex, item); } #endregion [ShowInInspector, ReadOnly] private GameObject _backpackGameObject; [SerializeField, DisableInPlayMode] private ItemObject[] _items = Array.Empty(); public ItemObject[] items => _items; /// /// Puts given item into given backpack's slot, if slot is occupied then puts it into first empty. /// public void Take(int itemIndex, ItemObject item) { if (items.Length <= itemIndex) Resize(itemIndex); if (items[itemIndex]) { Take(item); return; } items[itemIndex] = item; var backpackPickEvent = new BackpackPickEvent { unit = parent, item = item, slotIndex = itemIndex, }; parent.unitEvents.onBackpackPick.Invoke(backpackPickEvent); var itemGameObject = item.gameObject; itemGameObject.transform.parent = _backpackGameObject.transform; itemGameObject.transform.localPosition = Vector3.zero; itemGameObject.transform.localRotation = new Quaternion(); item.basicEvents.onRemove.AddListener(OnItemRemove); Resize(); } /// /// Puts given item into first empty slot. /// public void Take(ItemObject item) { // Find first free slot in backpack var firstFreeIndex = Array.IndexOf(items, null); // No free slots, we need to resize array if (firstFreeIndex == -1) { firstFreeIndex = items.Length; Resize(); } Take(firstFreeIndex, item); } /// /// Drops item from given slot, BUT! Unit still carry that.
/// This is purely for moving item between modules in UnitObject or preparing to drop it on scene. ///
public void Drop(int itemIndex) { var tempDroppedItem = items[itemIndex]; items[itemIndex].basicEvents.onRemove.RemoveListener(OnItemRemove); items[itemIndex] = null; var backpackDropEvent = new BackpackDropEvent { unit = parent, item = tempDroppedItem, slotIndex = itemIndex, }; parent.unitEvents.onBackpackDrop.Invoke(backpackDropEvent); var tempItemGameObject = tempDroppedItem.gameObject; tempItemGameObject.gameObject.transform.parent = null; tempItemGameObject.gameObject.transform.position = parent.transform.position; Resize(); } /// /// Finds given item's index and then run: ///

/// : /// ///
public void Drop(ItemObject item) { Drop(Array.IndexOf(items, item)); } /// /// Swaps content of two slots by given slot indexes. /// public void Swap(int firstItemIndex, int secondItemIndex) { Resize(Math.Max(firstItemIndex, secondItemIndex)); (items[firstItemIndex], items[secondItemIndex]) = (items[secondItemIndex], items[firstItemIndex]); var backpackSwapEvent = new BackpackSwapEvent { unit = parent, firstSlotIndex = firstItemIndex, firstItem = items[firstItemIndex], secondSlotIndex = secondItemIndex, secondItem = items[secondItemIndex], }; parent.unitEvents.onBackpackSwap.Invoke(backpackSwapEvent); Resize(); } public void Sort(Func valueGetter, SortDirectionType sortDirection) { ItemObjectSortUtil.Sort(ref _items, valueGetter, sortDirection); var backpackSortEvent = new BackpackSortEvent { unit = parent, }; parent.unitEvents.onBackpackSort.Invoke(backpackSortEvent); Resize(); } /// /// Executed when ItemObject is removed. Removes item's reference from backpack. /// private void OnItemRemove(RemoveEvent removeEvent) { var removedItem = removeEvent.obj as ItemObject; Drop(removedItem); } /// /// Resizes backpack with rule: last slots row must be empty AND each row have 10 slots /// public void Resize(int minSize = 0, int rowSize = 10) { var beforeSlotCount = items.Length; var lastItemIndex = Array.FindLastIndex(items, item => item); var afterSlotCount = Mathf.CeilToInt((Mathf.Max(lastItemIndex, minSize) + rowSize) / (float)rowSize) * rowSize; // Size not changed, do nothing if (beforeSlotCount == afterSlotCount) return; // Resize backpack's arrays var tempItems = items; Array.Resize(ref tempItems, afterSlotCount); _items = tempItems; var backpackResizeEvent = new BackpackResizeEvent { unit = parent, beforeSlotCount = beforeSlotCount, afterSlotCount = afterSlotCount, lastItemIndex = lastItemIndex, }; parent.unitEvents.onBackpackResize.Invoke(backpackResizeEvent); } } }