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);
}
}
}