using Invector; using Invector.vItemManager; using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI; namespace Beyond { [System.Serializable] public class OnHandleSlot : UnityEngine.Events.UnityEvent { } [System.Serializable] public class OnCompleteSlotList : UnityEngine.Events.UnityEvent> { } [vClassHeader("Item Window", openClose = false)] public class bItemWindow : vMonoBehaviour { public bInventory inventory; [vReadOnly] public bItem currentItem; private List items; [Header("Required References")] [Tooltip("The prefab used to instantiate items in the list.")] public bItemSlot slotPrefab; [Tooltip("The Content RectTransform inside your Scroll View.")] public RectTransform contentWindow; public List slots; public List supportedItems; public bool updateSlotCount = true; [Header("UI References")] public TMP_Text displayNameText; public Image rarityImage; public Text displayTypeText; public Text displayAmountText; public TMP_Text displayDescriptionText, changeableAttributesText; public Image powerSwordImage; public Text displayAttributesText; public Image displayItemImage, secondaryItemImage, teriaryItemImage; [SerializeField] private Sprite defaultSprite; [Header("Selection & Buttons")] public bItemSlot currentSelectedSlot; [SerializeField] private TMP_Text submitButtonText, consumeButtonText; [SerializeField] public RectTransform descriptionTransform; [SerializeField] private Scrollbar descriptionScroolbar; [SerializeField] private string useButtonString = ""; [SerializeField] private GameObject button, consumeButton; [SerializeField] private bool isTradeWindow = false; [vHelpBox("You can ignore display Attributes using this property")] public List ignoreAttributes; [vEditorToolbar("Text Events")] public InputField.OnChangeEvent onChangeName, onChangeType, onChangeAmount, onChangeDescription, onChangeAttributes; [vEditorToolbar("Events")] public OnCompleteSlotList onCompleteSlotListCallBack; public OnHandleSlot onSubmitSlot; public OnHandleSlot onSelectSlot; public UnityEvent onCancelSlot; public UnityEvent onAddSlots; public UnityEvent onClearSlots; private UnityAction onSubmitSlotCallback; private UnityAction onSelectCallback; private readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame(); private List chroniclesTypes = new List { bItemType.ChronicleResources, bItemType.Locations, bItemType.Science, bItemType.Characters }; private void OnEnable() { if (descriptionScroolbar && descriptionTransform) StartCoroutine(RebuildLayoutCoroutine(descriptionTransform)); } private void OnDisable() => UnloadCurrentImage(); private void UnloadCurrentImage() { if (displayItemImage && displayItemImage.sprite) Resources.UnloadAsset(displayItemImage.sprite); if (secondaryItemImage && secondaryItemImage.sprite) Resources.UnloadAsset(secondaryItemImage.sprite); if (teriaryItemImage && teriaryItemImage.sprite) Resources.UnloadAsset(teriaryItemImage.sprite); if (powerSwordImage && powerSwordImage.sprite) Resources.UnloadAsset(powerSwordImage.sprite); } public void ReloadItems() => ReloadItems(items); public void ClearSlots() { if (slots == null) return; for (int i = 0; i < slots.Count; i++) if (slots[i]) Destroy(slots[i].gameObject); slots.Clear(); currentSelectedSlot = null; } public void ReloadItems(List items) { int indexOfSlot = slots.Contains(currentSelectedSlot) ? slots.IndexOf(currentSelectedSlot) : 0; for (int i = 0; i < slots.Count; i++) { if (i >= 0 && i < slots.Count) { if (slots[i] != null && (slots[i].item == null || !items.Contains(slots[i].item))) { Destroy(slots[i].gameObject); slots.Remove(slots[i]); if (i == indexOfSlot) { currentSelectedSlot = i - 1 >= 0 ? slots[i - 1] : slots.Count - 1 > 0 ? slots[0] : null; if (currentSelectedSlot != null) CreateFullItemDescription(currentSelectedSlot); } i--; } else if (slots[i] == null) { slots.RemoveAt(i); i--; } } } if (currentSelectedSlot == null || currentSelectedSlot.item == null || slots.Count == 0) { CreateFullItemDescription(null); if (slots.Count == 0) onClearSlots.Invoke(); } else CreateFullItemDescription(currentSelectedSlot); } public virtual void CreateEquipmentWindow(List items, UnityAction onPickUpItemCallBack = null, UnityAction onSelectSlotCallBack = null, bool destroyAdictionSlots = true) { StartCoroutine(CreateEquipmentWindowRoutine(items, onPickUpItemCallBack, onSelectSlotCallBack, destroyAdictionSlots)); } public virtual void CreateEquipmentWindow(List items, List type, bItem currentItem = null, UnityAction onPickUpItemCallback = null, UnityAction onSelectSlotCallBack = null) { this.supportedItems = type; this.items = items.FindAll(item => type.Contains(item.type)); List list = new List(); foreach (var bItem in this.items.OrderBy(item => item.type).ThenBy(item => item.name)) list.Add(bItem); this.items = list; this.currentItem = currentItem; if (!this.currentItem && this.items.Count > 0) this.currentItem = this.items[0]; CreateEquipmentWindowNormal(this.items, onPickUpItemCallback, onSelectSlotCallBack, destroyAdictionSlots: true); } public virtual void CreateEquipmentWindowUsable(List items, List type, bItem currentItem = null, UnityAction onPickUpItemCallback = null, UnityAction onSelectSlotCallBack = null) { this.supportedItems = type; this.currentItem = currentItem; var _items = items.FindAll(item => type.Contains(item.type)); var _itemsUsable = _items.FindAll(item => item.canBeUsed); CreateEquipmentWindowNormal(_itemsUsable, onPickUpItemCallback, onSelectSlotCallBack, destroyAdictionSlots: true); } public void CreateEquipmentWindowNormal(List items, UnityAction onPickUpItemCallBack = null, UnityAction onSelectSlotCallBack = null, bool destroyAdictionSlots = true) { if (inventory == null && Player.Instance != null && Player.Instance.ItemManager != null) inventory = Player.Instance.ItemManager.inventory; var _items = supportedItems == null || supportedItems.Count == 0 ? items : items.FindAll(i => supportedItems.Contains(i.type)); if (_items.Count == 0) { CreateFullItemDescription(null); onClearSlots.Invoke(); if (slots != null && slots.Count > 0 && destroyAdictionSlots && updateSlotCount) ClearSlots(); currentSelectedSlot = null; } else { if (slots == null) slots = new List(); if (slots.Count > _items.Count && destroyAdictionSlots && updateSlotCount) { int difference = slots.Count - _items.Count; for (int i = 0; i < difference; i++) { if (slots[0]) Destroy(slots[0].gameObject); slots.RemoveAt(0); } } bool selecItem = false; onSubmitSlotCallback = onPickUpItemCallBack; onSelectCallback = onSelectSlotCallBack; if (updateSlotCount) { for (int i = 0; i < _items.Count; i++) { bItemSlot slot = null; if (i < slots.Count) slot = slots[i]; else { // --- SAFETY CHECK --- if (slotPrefab == null) { Debug.LogError("[bItemWindow] Error: 'Slot Prefab' is not assigned! Please drag a bItemSlot prefab into the 'Slot Prefab' field in the Inspector.", this); return; } if (contentWindow == null) { Debug.LogError("[bItemWindow] Error: 'Content Window' is not assigned! Please assign the Content RectTransform of your Scroll View.", this); return; } // -------------------- slot = Instantiate(slotPrefab) as bItemSlot; slots.Add(slot); var rectTranform = slot.GetComponent(); rectTranform.SetParent(contentWindow); rectTranform.localPosition = Vector3.zero; rectTranform.localScale = Vector3.one; rectTranform.localRotation = Quaternion.identity; // Ensure the new slot is visible and active slot.gameObject.SetActive(true); } slot.SetInventory(this.inventory); slot.AddItem(_items[i]); if (slot.item.type == bItemType.PowerScroll && slot.item.canBeDroped) slot.TryToMarkAsSpeciallEquipped(); else slot.CheckItem(_items[i].isInEquipArea); slot.onSubmitSlotCallBack = OnSubmit; slot.onSelectSlotCallBack = OnSelect; if (currentItem != null && currentItem == _items[i]) { if (currentSelectedSlot) currentSelectedSlot.MarkSlotAsDeselected(); selecItem = true; currentSelectedSlot = slot; OnSelect(slot); } slot.UpdateDisplays(); } } if (slots.Count > 0 && !selecItem) currentSelectedSlot = slots[0]; if (contentWindow) LayoutRebuilder.ForceRebuildLayoutImmediate(contentWindow); } SetSubmitButton(currentSelectedSlot); if (slots.Count > 0) { onAddSlots.Invoke(); EnableDescriptionObjects(); CreateFullItemDescription(currentSelectedSlot); } else DisableDescriptionObjects(); onCompleteSlotListCallBack.Invoke(slots); } public void EnableDescriptionObjects() { if (displayNameText) displayNameText.enabled = true; if (descriptionTransform) descriptionTransform.gameObject.SetActive(true); } public void DisableDescriptionObjects() { if (displayNameText) displayNameText.enabled = false; if (descriptionTransform) descriptionTransform.gameObject.SetActive(false); } public void SetPowerableSwitchSwordImage(bItem selectedPowerableItem) { if (powerSwordImage == null) return; if (powerSwordImage.sprite) Resources.UnloadAsset(powerSwordImage.sprite); if (!selectedPowerableItem) powerSwordImage.enabled = false; else { powerSwordImage.enabled = true; powerSwordImage.sprite = Resources.Load(selectedPowerableItem.iconPath); } } protected virtual IEnumerator CreateEquipmentWindowRoutine(List items, UnityAction onPickUpItemCallBack = null, UnityAction onSelectSlotCallBack = null, bool destroyAdictionSlots = true) { if (inventory == null && Player.Instance != null && Player.Instance.ItemManager != null) inventory = Player.Instance.ItemManager.inventory; var _items = supportedItems == null || supportedItems.Count == 0 ? items : items.FindAll(i => supportedItems.Contains(i.type)); if (_items.Count == 0) { CreateFullItemDescription(null); onClearSlots.Invoke(); ClearSlots(); } else { if (slots == null) slots = new List(); ClearSlots(); for (int i = 0; i < _items.Count; i++) { if (slotPrefab == null) { Debug.LogError("[bItemWindow] Error: 'Slot Prefab' is missing!", this); yield break; } bItemSlot slot = Instantiate(slotPrefab) as bItemSlot; slots.Add(slot); var rectTranform = slot.GetComponent(); rectTranform.SetParent(contentWindow); rectTranform.localPosition = Vector3.zero; rectTranform.localScale = Vector3.one; slot.gameObject.SetActive(true); slot.inventory = this.inventory; slot.AddItem(_items[i]); slot.CheckItem(_items[i].isInEquipArea); slot.onSubmitSlotCallBack = OnSubmit; slot.onSelectSlotCallBack = OnSelect; slot.UpdateDisplays(); yield return null; } if (contentWindow) LayoutRebuilder.ForceRebuildLayoutImmediate(contentWindow); if (slots.Count > 0) { currentSelectedSlot = slots[0]; CreateFullItemDescription(currentSelectedSlot); } } if (slots.Count > 0) onAddSlots.Invoke(); onCompleteSlotListCallBack.Invoke(slots); } public virtual IEnumerator SetSelectableHandle(GameObject target) { if (this.enabled) { yield return WaitForEndOfFrame; SetSelectable(target); } } public virtual void SetSelectable(GameObject target) { if (!EventSystem.current) return; var pointer = new PointerEventData(EventSystem.current); if (EventSystem.current.currentSelectedGameObject) ExecuteEvents.Execute(EventSystem.current.currentSelectedGameObject, pointer, ExecuteEvents.pointerExitHandler); EventSystem.current.SetSelectedGameObject(target, new BaseEventData(EventSystem.current)); ExecuteEvents.Execute(target, pointer, ExecuteEvents.selectHandler); } public virtual void OnSubmit(bItemSlot slot) { if (ShouldDissalowUseOfScrolls(slot)) { if (PopupMenuController.Instance != null && SkillsManager.instance != null) PopupMenuController.Instance.TryToShowPopupMesssage("Skill Required: " + SkillsManager.instance.GetStringNameOf(Skills.MasterOfScrolls)); } else { currentSelectedSlot = slot; onSubmitSlotCallback?.Invoke(slot); SetSubmitButton(slot); onSubmitSlot.Invoke(slot); } } private bool ShouldDissalowUseOfScrolls(bItemSlot slot) { if (slot == null || slot.item == null) return false; if (SkillsManager.instance == null) return false; return slot.item.type == bItemType.PowerScroll && SkillsManager.instance.GetSkillLevelOf(Skills.MasterOfScrolls) == 0 && PopupMenuController.Instance != null && !isTradeWindow; } private void SetSubmitButton(bItemSlot slot) { if (!slot || !button) { if (button) button.SetActive(false); if (consumeButton) consumeButton.SetActive(false); return; } button.SetActive(true); if (useButtonString != "") { if (submitButtonText) submitButtonText.text = useButtonString; return; } if (submitButtonText) { bItemType itemType = slot.item.type; switch (itemType) { case bItemType.MeleeWeapon: case bItemType.Swords: case bItemType.Axes: case bItemType.QuantaPower: case bItemType.Gemstones: case bItemType.Trinkets: submitButtonText.text = slot.isChecked ? "Unequip" : "Equip"; break; case bItemType.PowerScroll: if (!slot.item.canBeDroped) submitButtonText.text = slot.isChecked ? "Unequip" : "Equip"; else button.SetActive(false); break; case bItemType.Builder: submitButtonText.text = "Create"; break; case bItemType.Consumable: case bItemType.ConsumablesFaith: submitButtonText.text = slot.isChecked ? "Unequip" : "Equip"; if (consumeButton) { consumeButton.SetActive(true); if (consumeButtonText) consumeButtonText.text = "Consume"; } break; case bItemType.Guilts: submitButtonText.text = "Agree"; break; default: submitButtonText.text = slot.isChecked ? "Unequip" : "Equip"; if (consumeButton) consumeButton.SetActive(false); break; } } } public void OnSubmit() => OnSubmit(currentSelectedSlot); public virtual void OnSelect(bItemSlot slot) { if (slot == null || slot.item == null) return; if (currentSelectedSlot) currentSelectedSlot.MarkSlotAsDeselected(); currentSelectedSlot = slot; slot.MarkSlotAsSelected(); CreateFullItemDescription(slot); onSelectCallback?.Invoke(slot); SetSubmitButton(slot); onSelectSlot.Invoke(slot); } protected virtual void CreateFullItemDescription(bItemSlot slot) { if (!slot) { if (displayNameText) displayNameText.text = ""; if (displayDescriptionText) displayDescriptionText.text = ""; return; } var _name = slot.item ? slot.item.name : ""; var _description = slot.item ? slot.item.description : ""; var _type = slot.item ? slot.item.ItemTypeText() : ""; var _amount = slot.item ? slot.item.amount.ToString() : ""; var _attributes = slot.item ? slot.item.GetItemAttributesText(ignoreAttributes) : ""; Sprite _image = defaultSprite; Sprite _secondaryImage = null; Sprite _teriaryImage = null; if (slot.item) { if (!string.IsNullOrEmpty(slot.item.iconPath)) _image = Resources.Load(slot.item.iconPath); if (!string.IsNullOrEmpty(slot.item.secondaryIconPath)) _secondaryImage = Resources.Load(slot.item.secondaryIconPath); if (!string.IsNullOrEmpty(slot.item.teriaryIconPath)) _teriaryImage = Resources.Load(slot.item.teriaryIconPath); } int _rarity = -1; if (ItemIsRarityInterested(slot)) { bItemAttribute rarityAttribute = slot.item.GetItemAttribute(bItemAttributes.Rarity); _rarity = rarityAttribute != null ? rarityAttribute.value : 0; } 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); if (rarityImage) { if (_rarity >= 0 && slot.raritySprites != null && _rarity < slot.raritySprites.Count) { rarityImage.gameObject.SetActive(true); rarityImage.sprite = slot.raritySprites[_rarity]; } else rarityImage.gameObject.SetActive(false); } if (displayItemImage) { if (slot.item != null && chroniclesTypes.Contains(slot.item.type)) displayItemImage.sprite = _secondaryImage; else displayItemImage.sprite = _image; if (slot.item != null) { bItemAttribute xOffset = slot.item.GetItemAttribute(bItemAttributes.ImageXDescOffset); bItemAttribute yOffset = slot.item.GetItemAttribute(bItemAttributes.ImageYDescOffset); displayItemImage.transform.localPosition = new Vector3(xOffset != null ? xOffset.value : 0, yOffset != null ? yOffset.value : 0, 0); } } if (secondaryItemImage) secondaryItemImage.sprite = _secondaryImage; if (teriaryItemImage) teriaryItemImage.sprite = _teriaryImage; if (changeableAttributesText) changeableAttributesText.gameObject.SetActive(false); if (powerSwordImage) powerSwordImage.gameObject.SetActive(false); if (slot.item != null) { bItemAttribute _power = slot.item.GetItemAttribute(bItemAttributes.Power); bItemAttribute _Damage = slot.item.GetItemAttribute(bItemAttributes.Damage); if (slot.item.type == bItemType.Gemstones) { if (powerSwordImage && powerSwordImage.sprite) powerSwordImage.gameObject.SetActive(true); if (changeableAttributesText && _power != null) { changeableAttributesText.gameObject.SetActive(true); changeableAttributesText.text = "+" + _power.value.ToString() + " Quantum Charge"; } } else if (_power != null && changeableAttributesText) { changeableAttributesText.gameObject.SetActive(true); changeableAttributesText.text = "" + _power.value.ToString() + "/100 Quantum Charge "; if (_Damage != null) { if (_power.value > 0) { changeableAttributesText.text += "\n" + (_Damage.value * 2).ToString() + " Damage" + "\n" + (_Damage.value * 2).ToString() + " Spectral Damage"; } else { changeableAttributesText.text += "\n" + (_Damage.value).ToString() + " Damage" + "\n" + "0" + " Spectral Damage"; } } } } if (descriptionTransform) LayoutRebuilder.ForceRebuildLayoutImmediate(descriptionTransform); } private static bool ItemIsRarityInterested(bItemSlot slot) { return slot.item && slot.rarityInterestedType.Contains(slot.item.type); } private IEnumerator RebuildLayoutCoroutine(RectTransform transform) { yield return new WaitForSecondsRealtime(Time.fixedDeltaTime); if (transform) LayoutRebuilder.ForceRebuildLayoutImmediate(transform); } public virtual void OnCancel() => onCancelSlot.Invoke(); public void BlockAllButGaze(bool b) { BlockSlots(b, 43); } public void BlockSlots(bool block, int exceptionItem) { foreach (var s in slots) { if (block) { if (s.item.id != exceptionItem) s.Clickable = false; } else s.Clickable = true; } } } }