246 lines
8.3 KiB
C#
246 lines
8.3 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Module of <see cref="UnitObject"/> which manage currently carried items by this unit.
|
|
/// </summary>
|
|
[Serializable]
|
|
public class UnitObjectBackpack : ObjectComponent<UnitObject>
|
|
{
|
|
#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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only when <see cref="UnitObject"/> is initializing:<br/>
|
|
/// - Instantiate item if it is prefab asset<br/>
|
|
/// - Inform it by events that it has been picked<br/>
|
|
/// - Finally just resume with <see cref="Take(int,VOID.Generic.Objects.Item.ItemObject)"/><br/>
|
|
/// </summary>
|
|
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<ItemObject>();
|
|
|
|
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<ItemObject>();
|
|
public ItemObject[] items => _items;
|
|
|
|
/// <summary>
|
|
/// Puts given item into given backpack's slot, if slot is occupied then puts it into first empty.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Puts given item into first empty slot.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Drops item from given slot, BUT! Unit still carry that.<br/>
|
|
/// This is purely for moving item between modules in UnitObject or preparing to drop it on scene.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds given item's index and then run: <see cref="Drop(int)"/>
|
|
/// <br/><br/>
|
|
/// <see cref="Drop(int)"/>:
|
|
/// <inheritdoc cref="Drop(int)"/>
|
|
/// </summary>
|
|
public void Drop(ItemObject item)
|
|
{
|
|
Drop(Array.IndexOf(items, item));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Swaps content of two slots by given slot indexes.
|
|
/// </summary>
|
|
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<ItemObject, IComparable> valueGetter, SortDirectionType sortDirection) {
|
|
ItemObjectSortUtil.Sort(ref _items, valueGetter, sortDirection);
|
|
|
|
var backpackSortEvent = new BackpackSortEvent {
|
|
unit = parent,
|
|
};
|
|
|
|
parent.unitEvents.onBackpackSort.Invoke(backpackSortEvent);
|
|
|
|
Resize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executed when ItemObject is removed. Removes item's reference from backpack.
|
|
/// </summary>
|
|
private void OnItemRemove(RemoveEvent removeEvent)
|
|
{
|
|
var removedItem = removeEvent.obj as ItemObject;
|
|
Drop(removedItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resizes backpack with rule: last slots row must be empty AND each row have 10 slots
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|