Additional Bank Functionalities

OK so following up from this topic, I wanted to ask for a few more functionalities for our bank. Here is what I would’ve loved to see included:

  1. Possibly an in-game right mouse button, named ‘Withdraw’. ‘Withdraw’ is an in-game Property drawer, so that when we right click an item in the player bank, we get a mini-drawer show up that allows us to withdraw either one item, all of that specific item, or ‘x’ amount of items from the player bank to the inventory.
  2. a button on the player inventory that allows us to deposit EVERYTHING in our Inventory to our bank (which ONLY shows up when the bank UI is open, otherwise it disappears)
  3. making stackable and non-stackable items both stackable within the bank (and differentiating them again when in the inventory), so that non-stackable items do not consume more than one inventory slot for a specific item in the bank container

A bit complex, low on the priority score

You’d regret that once you started with procedurally generated bonuses. Equipment needs to be non-stackable. Trust me, you’ll thank me later.

That one is doable
First, you need a method in Inventory to do the transferizing…

        public void TransferAllInventory(Inventory otherInventory)
        {
            if (otherInventory == null || otherInventory == this) return;
            for (int i = 0; i < inventorySize; i++)
            {
                if (slots[i].item == null) continue;
                for (int j = slots[i].number; j >0; j--)
                {
                    if (otherInventory.HasSpaceFor(slots[i].item))
                    {
                        otherInventory.AddToFirstEmptySlot(slots[i].item, 1);
                        slots[i].number--;
                        if (slots[i].number <= 0) slots[i].item = null;
                    }
                    else break;
                }
            }
            inventoryUpdated?.Invoke();
            otherInventory.inventoryUpdated?.Invoke();
        }

You’ll need to put buttons on the OtherInventory UI and the InventoryUI, we’ll link those up shortly
in InventoryUI, you’ll l need two UnityEvents

        [SerializeField] private UnityEvent EnabledEvent;
        [SerializeField] private UnityEvent DisabledEvent;

Leave these two fields alone in the main InventoryUI, but in the OtherInventoryUI, drag the button for the InventoryUI’s TransferAll into entries on both UnityEvents. All you want to do is call GameObject/SetActive true and false based on OnEnable and OnDisable.
You don’t have to worry about this with the PlayerInventory UI because the OtherInventory cannot be open with the Player Inventory.

Finally, you need a few methods in InventoryUI.cs

       private void OnEnable()
        {
            EnabledEvent?.Invoke();
        }

        private void OnDisable()
        {
            DisabledEvent?.Invoke();
        }

        public void TransferAllInventory()
        {
            if (selectedInventory != null)
            {
                selectedInventory.TransferAllInventory(GetOtherInventory());
            }
        }
        
        Inventory GetOtherInventory()
        {
            if (IsOtherInventory) return Inventory.GetPlayerInventory();
            var otherInventoryUI =
                FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory); //will return the other Inventory or null
            //Check to see if it's not null (should never happen), if it's Active, and if the otherInventory's inventory is valid.
            if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory!=null)
            {
                Inventory otherInventory = otherInventoryUI.SelectedInventory;
                return otherInventory;
            }
            return null;
        }

For both of the Transfer Buttons, you’ll want to link to that window’s InventoryUI.TransferAllInventory();

It took me a bit to fully grasp the idea and create the User Interface, but I got it to work. As usual, thank you so much Brian for this. I have 2 questions and an additional functionality though

  • The added functionality is that I only want the button to show up for my inventory when the bank is open. I don’t want it visible when the player is in the game world (I’ll investigate this and edit accordingly. If there are no edits, I probably didn’t figure it out).

  • My first question is regarding this:

A bit complex, low on the priority score

Regarding the withdrawals, if that’s the case, then how do we stop the player from withdrawing all stackable items at once? If we click on a stacked item in the bank, I’d prefer he withdraws one item at a time, not the entire stack

  • My second question is: what does the procedurally generated bonuses (I’m guessing that was the incomplete tutorial we had a talk about earlier today, the one where the weapon damage/defense bonus increases as we level up or invest resources into making it better?) have to do with stacking and non-stacking bank items? I trust your judgement that I might not need it, considering the upcoming functionalities we will add to this game, but I am also curious as to what these two components have to do with one another?

That should be the case if you assign the EnabledEvent and DisabledEvent in the other inventory UI and link the button in the InventoryUI with GameObject SetActive(true) or false, depending on if it’s enabled or disabled.

Imagine you have a WeaponConfig for a Short Sword…
Now in game, two different enemies have dropped Short Swords as their random item, and when those swords were dropped, they were each given random attributes. One has a +5 to damage, the other has a +4 to damage.
This is managed by creating an instance of the Short Sword WeaponConfig. The one has stored attributes of +5 damage, the other instance has +4 damage attribute. The ItemID, however remains the same in both cases. This allows us to save each sword as an ItemID/State combo.
If you stack the items, then you will neccessarily lose this additional data, meaning when you retrieve one from the bank, you will no longer have either the +4 or +5 bonus attribute.

If it’s just a click handler, that’s a bit easier, but it won’t stop a drag from dragging the whole stack. It’s the right click pop up a menu handler that’s tricker. It’s certainly not impossible, but my GameDev time must be split between

  • Answering students stuck on course lectures (this isn’t)
  • Assisting students with new features not in the course with quick resolutions
  • Working on my own projects
  • Keeping up with engine changes that may affect courses
  • Working on new features requiring larger chunks of time/explanations or multiple iterations/research.

Actually restricting to a single item per doubleclick is astoundly easy… In InventorySlotUI, just restrict the number transferred from other inventory to inventory to 1

        private void HandleDoubleClick()
        {
            TimesUp();//Prevents triple click from starting another HandleDoubleClick
            InventoryItem item = inventory.GetItemInSlot(index);
            int number = inventory.GetNumberInSlot(index);
            if (item == null || number<1 ) return; //Nothing to do
            if (inventory.gameObject.CompareTag("Player"))
            {
                var otherInventoryUI =
                    FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory); //will return the other Inventory or null
                //Check to see if it's not null (should never happen), if it's Active, and if the otherInventory's inventory is valid.
                if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory!=null)
                {
                    Inventory otherInventory = otherInventoryUI.SelectedInventory;
                    TransferToOtherInventory(otherInventory, item, number);
                }
                else if(item is EquipableItem equipableItem && inventory.TryGetComponent(out Equipment equipment))
                {
                    EquipItem(equipableItem, equipment, number);
                }
            }
            else //if(!inventory.gameObject.CompareTag("Player") means we're the other inventory.
            {
                TransferToOtherInventory(Inventory.GetPlayerInventory(), item, 1); //<<---
            }
        }

Regarding the changes in ‘HandleDoubleClick’, changing the last parameter for ‘TransferToOtherInventory’ to just ‘1’ didn’t work as expected (I also tried changing it for both, still didn’t work). I’m still testing the other parts out for the moment :slight_smile:

Didn’t work as expected doesn’t tell me as much as you might thing.
What DID happen?

Apologies. It still grabs the entire stack (for stackable items) in and out of the bank :sweat_smile: , not one per click

Oooh, not only does it do that, but it still only removes one from the stack, meaning it’s like that ATM I always dreamed I would encounter that just keeps spitting out money from my empty bank account!

What a silly little bug… The method was adding the amount in inventory, but subtracting the number (1)

        private void TransferToOtherInventory(Inventory otherInventory, InventoryItem item, int number)
        {
            if (otherInventory.HasSpaceFor(item))
            {
                otherInventory.AddToFirstEmptySlot(inventory.GetItemInSlot(index),
                    number); //<<==
                inventory.RemoveItem(item, number);
                Setup(inventory, index);
            }
        }

Would I be publicly executed if I say that it still didn’t work? :stuck_out_tongue_winking_eye:

At least you have a bank account. Mine expired when I left Malaysia earlier this year :stuck_out_tongue:

We never execute students in public. It lowers sales.
What’s happening when you click on a stacked item in the bank?

Before clicking:
image
One Double Click:
image
Three more double clicks
image

So I’m still getting executed… Might as well make sure I leave my tough questions out there then :stuck_out_tongue_winking_eye:

Well I have a debugger that tells me which button in my inventory/bank I clicked, but that’s about it. When I bank/debank stacked items, the entire suicide squad of the same item comes along

Paste in your InventorySlotUI.cs…

using UnityEngine;
using System.Linq;
using GameDevTV.Inventories;
using GameDevTV.Core.UI.Dragging;
using UnityEngine.EventSystems;

namespace GameDevTV.UI.Inventories
{
    public class InventorySlotUI : MonoBehaviour, IItemHolder, IDragContainer<InventoryItem>, IPointerClickHandler
    {
        // CONFIG DATA
        [SerializeField] InventoryItemIcon icon = null;

        // STATE
        int index;
        InventoryItem item;
        Inventory inventory;

        // PUBLIC

        public void Setup(Inventory inventory, int index)
        {
            this.inventory = inventory;
            this.index = index;
            icon.SetItem(inventory.GetItemInSlot(index), inventory.GetNumberInSlot(index));
        }

        public int MaxAcceptable(InventoryItem item)
        {
            if (inventory.HasSpaceFor(item))
            {
                return int.MaxValue;
            }
            return 0;
        }

        public void AddItems(InventoryItem item, int number)
        {
            inventory.AddItemToSlot(index, item, number);
        }

        public InventoryItem GetItem()
        {
            return inventory.GetItemInSlot(index);
        }

        public int GetNumber()
        {
            return inventory.GetNumberInSlot(index);
        }

        public void RemoveItems(int number)
        {
            inventory.RemoveFromSlot(index, number);
        }

        // Equipping a Weapon by clicking on it, and banking it if the bank is open:
        public void TryHandleRightClick() {

            InventoryItem item = inventory.GetItemInSlot(index);
            int number = inventory.GetNumberInSlot(index);
            if (item == null || number < 1) return; // inventory/bank slot is empty, so do nothing

            if (!inventory.gameObject.CompareTag("Player")) {

                TransferToOtherInventory(Inventory.GetPlayerInventory(), item, number);
                return;

            }

            var otherInventoryUI = FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory);  // returns other inventory, or null

            if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory != null) {

                Inventory otherInventory = otherInventoryUI.SelectedInventory;
                TransferToOtherInventory(otherInventory, item, number);
                return;

            }

            if (item is EquipableItem equipableItem) {

                Equipment equipment = inventory.GetComponent<Equipment>();
                EquipableItem equippedItem = equipment.GetItemInSlot(equipableItem.GetAllowedEquipLocation());
                if (!equipableItem.CanEquip(equipableItem.GetAllowedEquipLocation(), equipment)) return;    // The line solely responsible for checking for Predicate conditions, prior to being able to wield a weapon (if you're not high enough of a level, you can't wield that)
                equipment.RemoveItem(equipableItem.GetAllowedEquipLocation());
                equipment.AddItem(equipableItem.GetAllowedEquipLocation(), equipableItem);
                RemoveItems(1);

                if (equippedItem != null)
                {
                    AddItems(equippedItem, 1);
                }

            }

            else if (item is ActionItem actionItem) {

                ActionStore actionStore = inventory.GetComponent<ActionStore>();
                int slot = actionStore.GetFirstEmptySlot();

                if (slot > -1) {

                    actionStore.AddAction(actionItem, slot, GetNumber());
                    RemoveItems(GetNumber());

                }

            }

        }

            // BELOW LIES THE CODE THAT ENSURES WE CAN CLICK ON STUFF IN OUR INVENTORY TO BANK IT:

            // Static variable. Each time InventorySlotUI is clicked, this value is updated:
            private static InventorySlotUI LastUIClicked;

            // The 'OnPointerClick' method below checks to see if the Last UI Clicked Slot is 'this' inventory slot.
            // if it isn't, set 'Last UI Clicked' to this, and a 'TimesUp()' timer (a timer that checks to see if we're the LastUIClicked. If so, clear 'LastUIClicked') 
            // is invoked

            public void OnPointerClick(PointerEventData eventData) {

                if (LastUIClicked != this) {

                    LastUIClicked = this;
                    Invoke(nameof(TimesUp), .5f);
                    Debug.Log($"{index} was clicked once");

                }

                else {
                    HandleDoubleClick();
                }
            }

            // 'TimesUp' checks if we're the last UI clicked (if true, clear 'LastUIClicked')
            private void TimesUp() {

                if (LastUIClicked == this) LastUIClicked = null;

            }

            // HandleDoubleClicked() function deals with 3 states:

            // 1. I am the player, and 'OtherInventory' (Bank) is Open
            // 2. I am the bank, and 'OtherInventory' (Player) is Closed
            // 3. I am the 'OtherInventory' (bank)

            private void HandleDoubleClick() {

                TimesUp();  // avoids triple clicking from starting another 'HandleDoubleClick'
                InventoryItem item = inventory.GetItemInSlot(index);
                int number = inventory.GetNumberInSlot(index);

                if (item == null || number < 1) return;

                if (inventory.gameObject.CompareTag("Player")) {

                    var otherInventoryUI = FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory);

                    if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory != null) {

                        Inventory otherInventory = otherInventoryUI.SelectedInventory;
                        TransferToOtherInventory(otherInventory, item, number);

                    }

                    else if (item is EquipableItem equipableItem && inventory.TryGetComponent(out Equipment equipment)) {

                        EquipItem(equipableItem, equipment, number);

                    }

                }

                else {

                // The Code below ensures that when we double click on STACKED items in our bank to extract them, ONE ITEM AT A TIME COMES OUT (by swapping the last parameter from 'number' to '1'):
                TransferToOtherInventory(Inventory.GetPlayerInventory(), item, 1);

                }

            }

            // Transferring stuff from the Inventory to our Bank:

                private void TransferToOtherInventory(Inventory otherInventory, InventoryItem item, int number) {

                    if (otherInventory.HasSpaceFor(item)) {

                        otherInventory.AddToFirstEmptySlot(inventory.GetItemInSlot(index), number);
                        inventory.RemoveItem(item, number);
                        Setup(inventory, index); // RECHECK Second argument, if it's 'item' or 'index'

                    }

                }

                // Equip item, if it's Equipable:

                private void EquipItem(EquipableItem equipableItem, Equipment equipment, int number) {

                    if (equipableItem.CanEquip(equipableItem.GetAllowedEquipLocation(), equipment)) {

                        EquipableItem otherEquipableItem = equipment.GetItemInSlot(equipableItem.GetAllowedEquipLocation());
                        equipment.RemoveItem(equipableItem.GetAllowedEquipLocation());
                        inventory.RemoveFromSlot(index, number);
                        equipment.AddItem(equipableItem.GetAllowedEquipLocation(), equipableItem);

                        if (otherEquipableItem != null) inventory.AddItemToSlot(index, otherEquipableItem, 1);

                        Setup(inventory, index);

                    }

                }

            }

        }

Take a closer look at how many items you asked TransferToOtherInventory to transfer…

OK so setting the last parameter in the latest block you shared does indeed split the stackable items when extracting them, but not the same case when importing them to the bank.

I wonder if there’s another line of code in there that calls TransferToOtherInventory(otherInventory, item, number)…
So here’s your challenge:
Find the location in HandleDoubleClick() is calling a transfer from the player Inventory to the Other Inventory, and adjust the parameter so that only one is transferred…

I did find this block. It’s the only other block with the TransferToOtherInventory() function call, in HandleDoubleClick():

if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory != null) {

                        Inventory otherInventory = otherInventoryUI.SelectedInventory;
                        TransferToOtherInventory(otherInventory, item, 1);

                    }

I adjusted its value to 1 a short while ago, but the issue still persists

Privacy & Terms