599 lines
22 KiB
C#
599 lines
22 KiB
C#
using System;
|
|
using Invector;
|
|
using Invector.vItemManager;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.UI;
|
|
using System.Linq;
|
|
|
|
namespace Beyond
|
|
{
|
|
[vClassHeader("Equip Area", openClose = false)]
|
|
public class bEquipArea : vMonoBehaviour
|
|
{
|
|
public delegate void OnPickUpItem(bEquipArea area, bItemSlot slot);
|
|
|
|
private const int powerChargesMaxValue = 100;
|
|
public OnPickUpItem onPickUpItemCallBack;
|
|
|
|
public bInventory inventory;
|
|
public bItemManager itemManager;
|
|
public bItemWindow itemPicker;
|
|
|
|
[Tooltip("Set current equiped slot when submit an slot of this area")]
|
|
public bool setEquipSlotWhenSubmit;
|
|
|
|
[Tooltip("Skip empty slots when switching between slots")]
|
|
public bool skipEmptySlots;
|
|
|
|
public List<bEquipSlot> equipSlots;
|
|
public string equipPointName;
|
|
|
|
public Text displayNameText;
|
|
public Text displayTypeText;
|
|
public Text displayAmountText;
|
|
public Text displayDescriptionText;
|
|
public Text displayAttributesText;
|
|
|
|
[vHelpBox("You can ignore display Attributes using this property")]
|
|
public List<bItemAttributes> ignoreAttributes;
|
|
|
|
// --- FIX 1: Initialize Events inline to prevent NullReferenceException ---
|
|
public UnityEngine.Events.UnityEvent onInitPickUpItem = new UnityEngine.Events.UnityEvent();
|
|
public UnityEngine.Events.UnityEvent onFinishPickUpItem = new UnityEngine.Events.UnityEvent();
|
|
|
|
public InputField.OnChangeEvent onChangeName;
|
|
public InputField.OnChangeEvent onChangeType;
|
|
public InputField.OnChangeEvent onChangeAmount;
|
|
public InputField.OnChangeEvent onChangeDescription;
|
|
public InputField.OnChangeEvent onChangeAttributes;
|
|
|
|
public OnChangeEquipmentEvent onEquipItem = new OnChangeEquipmentEvent();
|
|
public OnChangeEquipmentEvent onUnequipItem = new OnChangeEquipmentEvent();
|
|
public OnSelectEquipArea onSelectEquipArea = new OnSelectEquipArea();
|
|
// ------------------------------------------------------------------------
|
|
|
|
public event Action OnItemUsed;
|
|
|
|
public UnityEngine.UI.Toggle.ToggleEvent onSetLockToEquip = new UnityEngine.UI.Toggle.ToggleEvent();
|
|
|
|
[HideInInspector]
|
|
public bEquipSlot currentSelectedSlot;
|
|
|
|
public bEquipSlot lastSelectedSlot;
|
|
|
|
[HideInInspector]
|
|
public int indexOfEquippedItem;
|
|
|
|
public bItem lastEquipedItem;
|
|
|
|
protected bool _isLockedToEquip;
|
|
|
|
[vHelpBox("Select what ItemType this EquipSlot will equip", vHelpBoxAttribute.MessageType.Warning)]
|
|
public List<bItemType> itemTypes;
|
|
|
|
private List<bItem> items = new();
|
|
private List<int> usedIndexes = new();
|
|
|
|
private List<bItem> gemableWeapons = new();
|
|
private bItem selectedPowerableItem;
|
|
private int selectedGemableItemIndex;
|
|
|
|
public bool isLockedToEquip
|
|
{
|
|
get { return _isLockedToEquip; }
|
|
set
|
|
{
|
|
if (_isLockedToEquip != value) onSetLockToEquip.Invoke(value);
|
|
_isLockedToEquip = value;
|
|
}
|
|
}
|
|
|
|
public bool ignoreEquipEvents;
|
|
internal bool isInit;
|
|
|
|
public void Init()
|
|
{
|
|
if (!isInit) Start();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (!isInit)
|
|
{
|
|
isInit = true;
|
|
|
|
// Dependencies
|
|
if (inventory == null) inventory = GetComponentInParent<bInventory>();
|
|
if (inventory == null && Player.Instance != null && Player.Instance.ItemManager != null)
|
|
inventory = Player.Instance.ItemManager.inventory;
|
|
|
|
itemManager = Player.Instance != null ? Player.Instance.GetComponent<bItemManager>() : null;
|
|
|
|
if (equipSlots.Count == 0)
|
|
{
|
|
var equipSlotsArray = GetComponentsInChildren<bEquipSlot>(true);
|
|
equipSlots = equipSlotsArray.vToList();
|
|
}
|
|
|
|
foreach (bEquipSlot slot in equipSlots)
|
|
{
|
|
slot.SetInventory(this.inventory); // Explicitly set inventory
|
|
|
|
slot.onSubmitSlotCallBack = OnSubmitSlot;
|
|
slot.onSelectSlotCallBack = OnSelectSlot;
|
|
slot.onDeselectSlotCallBack = OnDeselect;
|
|
onSetLockToEquip.AddListener(slot.SetLockToEquip);
|
|
if (slot.displayAmountText) slot.displayAmountText.text = "";
|
|
slot.onChangeAmount.Invoke("");
|
|
}
|
|
}
|
|
if (itemPicker)
|
|
{
|
|
itemPicker.onSelectSlot.AddListener(SetPowerableWeaponImage);
|
|
}
|
|
}
|
|
|
|
public void SetEquipmentWindow()
|
|
{
|
|
itemPicker.CreateEquipmentWindow(inventory.items, itemTypes, equipSlots[0].item, OnPickItem);
|
|
}
|
|
|
|
public void SetNewItemWindow(bItemWindow newItemPicker)
|
|
{
|
|
if (itemPicker) DisableItemWindow();
|
|
itemPicker = newItemPicker;
|
|
EnableItemWindow();
|
|
}
|
|
|
|
public void DisableItemWindow() => itemPicker.gameObject.SetActive(false);
|
|
public void EnableItemWindow() => itemPicker.gameObject.SetActive(true);
|
|
|
|
public void SetEquipmentwindowWithFilter(List<bItemType> types)
|
|
{
|
|
itemPicker.CreateEquipmentWindow(inventory.items, types, null, OnPickItem);
|
|
}
|
|
|
|
public void SetEquipmentWindowsUsableWithFilter(List<bItemType> types)
|
|
{
|
|
itemPicker.CreateEquipmentWindowUsable(inventory.items, types, equipSlots[0].item, OnPickItem);
|
|
}
|
|
|
|
private void SetOccupiedIndexes()
|
|
{
|
|
usedIndexes.Clear();
|
|
for (int i = 0; i < equipSlots.Count; i++)
|
|
{
|
|
if (equipSlots[i].isOcupad())
|
|
usedIndexes.Add(i);
|
|
}
|
|
}
|
|
|
|
public bool IsAnyItemEquipped()
|
|
{
|
|
for (int i = 0; i < equipSlots.Count; i++) if (equipSlots[i].isOcupad()) return true;
|
|
return false;
|
|
}
|
|
|
|
public bEquipSlot currentEquippedSlot => equipSlots[indexOfEquippedItem];
|
|
|
|
public bItem currentEquippedItem
|
|
{
|
|
get
|
|
{
|
|
var validEquipSlots = ValidSlots;
|
|
if (validEquipSlots.Count > 0 && indexOfEquippedItem >= 0 && indexOfEquippedItem < validEquipSlots.Count) return validEquipSlots[indexOfEquippedItem].item;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public List<bEquipSlot> ValidSlots => equipSlots.FindAll(slot => slot.isValid && (!skipEmptySlots || slot.item != null));
|
|
|
|
public bool ContainsItem(vItem item) => ValidSlots.Find(slot => slot.item == item) != null;
|
|
|
|
private void UseItemInternal(bItem item)
|
|
{
|
|
if (item.type == bItemType.Gemstones)
|
|
{
|
|
if (gemableWeapons.Count < 1 || gemableWeapons[selectedGemableItemIndex] == null)
|
|
{
|
|
if (bItemCollectionDisplay.Instance && item.type == bItemType.Gemstones && item)
|
|
{
|
|
Player.Instance.PlayFullyChargedSound();
|
|
if (PopupMenuController.Instance != null)
|
|
PopupMenuController.Instance.TryToShowPopupMesssage("No proper weapons available");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bItemAttribute powerAttribute = gemableWeapons[selectedGemableItemIndex].attributes.First(attribute => attribute.name == bItemAttributes.Power);
|
|
if (powerAttribute.value >= powerChargesMaxValue)
|
|
{
|
|
Player.Instance.PlayFullyChargedSound();
|
|
if (PopupMenuController.Instance != null)
|
|
PopupMenuController.Instance.TryToShowPopupMesssage(gemableWeapons[selectedGemableItemIndex].name + " is already fully charged");
|
|
return;
|
|
}
|
|
powerAttribute.value += item.GetItemAttribute(bItemAttributes.Power).value;
|
|
if (powerAttribute.value > powerChargesMaxValue) powerAttribute.value = powerChargesMaxValue;
|
|
}
|
|
}
|
|
inventory.OnUseItem(item);
|
|
}
|
|
|
|
public void UseItem()
|
|
{
|
|
if (itemManager.UsingItem) return;
|
|
UseItemInternal(itemPicker.currentSelectedSlot.item);
|
|
}
|
|
|
|
public void UseItem(bEquipSlot equipSlot)
|
|
{
|
|
bItem itemBeingUsed = equipSlot.item;
|
|
FindGemableWeapons();
|
|
UseItemInternal(itemBeingUsed);
|
|
}
|
|
|
|
public void UseEquippedItem()
|
|
{
|
|
inventory.OnUseItem(currentEquippedItem);
|
|
}
|
|
|
|
public void OnSubmitSlot(bItemSlot slot)
|
|
{
|
|
lastSelectedSlot = currentSelectedSlot;
|
|
if (itemPicker != null)
|
|
{
|
|
currentSelectedSlot = slot as bEquipSlot;
|
|
if (setEquipSlotWhenSubmit)
|
|
{
|
|
SetEquipSlot(equipSlots.IndexOf(currentSelectedSlot));
|
|
}
|
|
itemPicker.gameObject.SetActive(true);
|
|
itemPicker.onCancelSlot.RemoveAllListeners();
|
|
itemPicker.onCancelSlot.AddListener(CancelCurrentSlot);
|
|
itemPicker.CreateEquipmentWindow(inventory.items, currentSelectedSlot.itemType, slot.item, OnPickItem);
|
|
onInitPickUpItem.Invoke();
|
|
}
|
|
}
|
|
|
|
public void CancelCurrentSlot()
|
|
{
|
|
if (currentSelectedSlot == null) currentSelectedSlot = lastSelectedSlot;
|
|
if (currentSelectedSlot != null) currentSelectedSlot.OnCancel();
|
|
onFinishPickUpItem.Invoke();
|
|
}
|
|
|
|
public void UnequipItem(bEquipSlot slot)
|
|
{
|
|
if (slot)
|
|
{
|
|
bItem item = slot.item;
|
|
if (ValidSlots[indexOfEquippedItem].item == item) lastEquipedItem = item;
|
|
slot.RemoveItem();
|
|
onUnequipItem.Invoke(this, item);
|
|
}
|
|
}
|
|
|
|
public void UnequipItem(bItem item)
|
|
{
|
|
var slot = ValidSlots.Find(_slot => _slot.item == item);
|
|
if (slot)
|
|
{
|
|
if (ValidSlots[indexOfEquippedItem].item == item) lastEquipedItem = item;
|
|
slot.RemoveItem();
|
|
onUnequipItem.Invoke(this, item);
|
|
}
|
|
}
|
|
|
|
public void UnequipCurrentItem()
|
|
{
|
|
if (currentSelectedSlot && currentSelectedSlot.item)
|
|
{
|
|
var _item = currentSelectedSlot.item;
|
|
if (ValidSlots[indexOfEquippedItem].item == _item) lastEquipedItem = _item;
|
|
currentSelectedSlot.RemoveItem();
|
|
onUnequipItem.Invoke(this, _item);
|
|
}
|
|
}
|
|
|
|
public void OnSelectSlot(bItemSlot slot)
|
|
{
|
|
if (equipSlots.Contains(slot as bEquipSlot))
|
|
currentSelectedSlot = slot as bEquipSlot;
|
|
else currentSelectedSlot = null;
|
|
|
|
onSelectEquipArea.Invoke(this);
|
|
CreateFullItemDescription(slot);
|
|
}
|
|
|
|
public void OnDeselect(bItemSlot slot)
|
|
{
|
|
if (equipSlots.Contains(slot as bEquipSlot))
|
|
currentSelectedSlot = null;
|
|
}
|
|
|
|
protected virtual void CreateFullItemDescription(bItemSlot slot)
|
|
{
|
|
var _name = slot.item ? slot.item.name : "";
|
|
var _type = slot.item ? slot.item.ItemTypeText() : "";
|
|
var _amount = slot.item ? slot.item.amount.ToString() : "";
|
|
var _description = slot.item ? slot.item.description : "";
|
|
var _attributes = slot.item ? slot.item.GetItemAttributesText(ignoreAttributes) : "";
|
|
|
|
if (displayNameText) displayNameText.text = _name;
|
|
onChangeName.Invoke(_name);
|
|
|
|
if (displayTypeText) displayTypeText.text = _type;
|
|
onChangeType.Invoke(_type);
|
|
|
|
if (displayAmountText) displayAmountText.text = _amount;
|
|
onChangeAmount.Invoke(_amount);
|
|
|
|
if (displayDescriptionText) displayDescriptionText.text = _description;
|
|
onChangeDescription.Invoke(_description);
|
|
|
|
if (displayAttributesText) displayAttributesText.text = _attributes;
|
|
onChangeAttributes.Invoke(_attributes);
|
|
}
|
|
|
|
// --- FIX 2: Safer OnPickItem implementation ---
|
|
public void OnPickItem(bItemSlot slot)
|
|
{
|
|
if (usedIndexes.Count == 0) SetOccupiedIndexes();
|
|
|
|
// 1. Unequip if checked
|
|
if (slot.isChecked)
|
|
{
|
|
bEquipSlot occupiedSlot = equipSlots.Find(eSlot => eSlot.item == slot.item);
|
|
if (occupiedSlot != null) // Check if slot was actually found
|
|
{
|
|
usedIndexes.Remove(equipSlots.IndexOf(occupiedSlot));
|
|
occupiedSlot.RemoveItem();
|
|
onUnequipItem?.Invoke(this, slot.item); // Safe Invoke
|
|
}
|
|
onFinishPickUpItem?.Invoke(); // Safe Invoke
|
|
return;
|
|
}
|
|
|
|
// 2. Fill first free slot
|
|
bEquipSlot freeSlot = equipSlots.Find(eslot => !eslot.isOcupad() && eslot.isValid && eslot.itemType.Contains(slot.item.type));
|
|
if (freeSlot)
|
|
{
|
|
onPickUpItemCallBack?.Invoke(this, slot);
|
|
freeSlot.AddItem(slot.item);
|
|
if (!ignoreEquipEvents)
|
|
{
|
|
onEquipItem?.Invoke(this, slot.item);
|
|
}
|
|
onFinishPickUpItem?.Invoke();
|
|
usedIndexes.Add(equipSlots.IndexOf(freeSlot));
|
|
return;
|
|
}
|
|
|
|
// 3. Swap with oldest (FIFO) if full
|
|
if (usedIndexes.Count > 0)
|
|
{
|
|
int slotIndexToTake = -1;
|
|
|
|
// Iterate through used indexes to find a compatible slot
|
|
for (int i = 0; i < usedIndexes.Count; i++)
|
|
{
|
|
int idx = usedIndexes[i];
|
|
if (equipSlots[idx].itemType.Contains(slot.item.type))
|
|
{
|
|
slotIndexToTake = idx;
|
|
usedIndexes.RemoveAt(i);
|
|
usedIndexes.Add(idx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we found a compatible slot to swap
|
|
if (slotIndexToTake != -1)
|
|
{
|
|
bEquipSlot slotToBeTaken = equipSlots[slotIndexToTake];
|
|
|
|
if (slotToBeTaken.isOcupad())
|
|
{
|
|
onUnequipItem?.Invoke(this, slotToBeTaken.item);
|
|
slotToBeTaken.RemoveItem();
|
|
}
|
|
|
|
onPickUpItemCallBack?.Invoke(this, slot);
|
|
slotToBeTaken.AddItem(slot.item);
|
|
|
|
if (!ignoreEquipEvents)
|
|
{
|
|
onEquipItem?.Invoke(this, slot.item);
|
|
}
|
|
}
|
|
}
|
|
|
|
onFinishPickUpItem?.Invoke();
|
|
}
|
|
// ----------------------------------------------
|
|
|
|
void FindGemableWeapons()
|
|
{
|
|
List<bItemType> weaponTypes = new List<bItemType>() { bItemType.Swords, bItemType.Axes };
|
|
gemableWeapons = inventory.items.FindAll(item => weaponTypes.Contains(item.type) && item.GetItemAttribute(bItemAttributes.Power) != null);
|
|
selectedGemableItemIndex = 0;
|
|
}
|
|
|
|
private void SetPowerableWeaponImage(bItemSlot itemSelected)
|
|
{
|
|
if (itemSelected.item.type == bItemType.Gemstones)
|
|
{
|
|
FindGemableWeapons();
|
|
if (gemableWeapons.Count > 0)
|
|
{
|
|
selectedPowerableItem = gemableWeapons[selectedGemableItemIndex];
|
|
itemPicker.SetPowerableSwitchSwordImage(selectedPowerableItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SwitchPowerableWeapon()
|
|
{
|
|
if (gemableWeapons.Count < 1)
|
|
{
|
|
itemPicker.SetPowerableSwitchSwordImage(null);
|
|
return;
|
|
}
|
|
selectedGemableItemIndex++;
|
|
if (selectedGemableItemIndex >= gemableWeapons.Count)
|
|
{
|
|
selectedGemableItemIndex = 0;
|
|
}
|
|
selectedPowerableItem = gemableWeapons[selectedGemableItemIndex];
|
|
itemPicker.SetPowerableSwitchSwordImage(selectedPowerableItem);
|
|
}
|
|
|
|
public void NextEquipSlot()
|
|
{
|
|
if (equipSlots == null || equipSlots.Count == 0) return;
|
|
|
|
lastEquipedItem = currentEquippedItem;
|
|
var validEquipSlots = ValidSlots;
|
|
if (indexOfEquippedItem + 1 < validEquipSlots.Count)
|
|
indexOfEquippedItem++;
|
|
else
|
|
indexOfEquippedItem = 0;
|
|
|
|
if (currentEquippedItem != null && !ignoreEquipEvents)
|
|
onEquipItem?.Invoke(this, currentEquippedItem);
|
|
onUnequipItem?.Invoke(this, lastEquipedItem);
|
|
}
|
|
|
|
public void PreviousEquipSlot()
|
|
{
|
|
if (equipSlots == null || equipSlots.Count == 0) return;
|
|
|
|
lastEquipedItem = currentEquippedItem;
|
|
var validEquipSlots = ValidSlots;
|
|
|
|
if (indexOfEquippedItem - 1 >= 0)
|
|
indexOfEquippedItem--;
|
|
else
|
|
indexOfEquippedItem = validEquipSlots.Count - 1;
|
|
|
|
if (currentEquippedItem != null && !ignoreEquipEvents)
|
|
onEquipItem?.Invoke(this, currentEquippedItem);
|
|
|
|
onUnequipItem?.Invoke(this, lastEquipedItem);
|
|
}
|
|
|
|
public void SetEquipSlot(int indexOfSlot)
|
|
{
|
|
if (equipSlots == null || equipSlots.Count == 0) return;
|
|
if (indexOfSlot < equipSlots.Count && indexOfSlot >= 0)
|
|
{
|
|
lastEquipedItem = currentEquippedItem;
|
|
indexOfEquippedItem = indexOfSlot;
|
|
if (currentEquippedItem != null && !ignoreEquipEvents)
|
|
{
|
|
onEquipItem?.Invoke(this, currentEquippedItem);
|
|
}
|
|
if (currentEquippedItem != lastEquipedItem)
|
|
onUnequipItem?.Invoke(this, lastEquipedItem);
|
|
}
|
|
}
|
|
|
|
public void EquipCurrentSlot()
|
|
{
|
|
if (!currentEquippedSlot || (currentEquippedSlot.item != null && currentEquippedSlot.item.isEquiped)) return;
|
|
if (currentEquippedItem) onEquipItem?.Invoke(this, currentEquippedItem);
|
|
else if (lastEquipedItem) onUnequipItem?.Invoke(this, lastEquipedItem);
|
|
}
|
|
|
|
public void AddItemToEquipSlot(bItemSlot slot, bItem item, bool autoEquip = false)
|
|
{
|
|
if (slot is bEquipSlot && equipSlots.Contains(slot as bEquipSlot))
|
|
{
|
|
AddItemToEquipSlot(equipSlots.IndexOf(slot as bEquipSlot), item, autoEquip);
|
|
}
|
|
}
|
|
|
|
public void AddItemToEquipSlot(int indexOfSlot, bItem item, bool autoEquip = false)
|
|
{
|
|
if (indexOfSlot < equipSlots.Count && item != null && item.canBeUsed)
|
|
{
|
|
var slot = equipSlots[indexOfSlot];
|
|
|
|
if (slot != null && slot.isValid && slot.itemType.Contains(item.type))
|
|
{
|
|
var sameSlot = equipSlots.Find(s => s.item == item && s != slot);
|
|
|
|
if (sameSlot != null)
|
|
RemoveItemOfEquipSlot(equipSlots.IndexOf(sameSlot));
|
|
|
|
if (slot.item != null && slot.item != item)
|
|
{
|
|
if (currentEquippedItem == slot.item) lastEquipedItem = slot.item;
|
|
slot.item.isInEquipArea = false;
|
|
onUnequipItem?.Invoke(this, slot.item);
|
|
}
|
|
item.checkColor = slot.checkColor;
|
|
item.isInEquipArea = true;
|
|
slot.AddItem(item);
|
|
if (autoEquip)
|
|
SetEquipSlot(indexOfSlot);
|
|
else if (!ignoreEquipEvents)
|
|
onEquipItem?.Invoke(this, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RemoveItemOfEquipSlot(bItemSlot slot)
|
|
{
|
|
if (slot is bEquipSlot && equipSlots.Contains(slot as bEquipSlot))
|
|
{
|
|
RemoveItemOfEquipSlot(equipSlots.IndexOf(slot as bEquipSlot));
|
|
}
|
|
}
|
|
|
|
public void RemoveItemOfEquipSlot(int indexOfSlot)
|
|
{
|
|
if (indexOfSlot < equipSlots.Count)
|
|
{
|
|
var slot = equipSlots[indexOfSlot];
|
|
if (slot != null && slot.item != null)
|
|
{
|
|
var item = slot.item;
|
|
item.isInEquipArea = false;
|
|
if (currentEquippedItem == item) lastEquipedItem = currentEquippedItem;
|
|
slot.RemoveItem();
|
|
onUnequipItem?.Invoke(this, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddCurrentItem(bItem item)
|
|
{
|
|
if (indexOfEquippedItem < equipSlots.Count)
|
|
{
|
|
var slot = equipSlots[indexOfEquippedItem];
|
|
if (slot.item != null && item != slot.item)
|
|
{
|
|
if (currentEquippedItem == slot.item) lastEquipedItem = slot.item;
|
|
slot.item.isInEquipArea = false;
|
|
onUnequipItem?.Invoke(this, currentSelectedSlot.item);
|
|
}
|
|
slot.AddItem(item);
|
|
if (!ignoreEquipEvents) onEquipItem?.Invoke(this, item);
|
|
}
|
|
}
|
|
|
|
public void RemoveCurrentItem()
|
|
{
|
|
if (!currentEquippedItem) return;
|
|
lastEquipedItem = currentEquippedItem;
|
|
ValidSlots[indexOfEquippedItem].RemoveItem();
|
|
onUnequipItem?.Invoke(this, lastEquipedItem);
|
|
}
|
|
}
|
|
} |