Hotbar Hotkey not working as expected

Zaddo

Active member
Environment: UISv1.2.5, UCCv2.4.5, URP, Unity2020.3.24f1

When I press the hotbar hotkey it does not equip the item. I am not sure if this is a problem with my configuration or a bug. The code that stops the item from equipping is in CharacterEquipUnequipHotbarAction (See CanInvokeInternal method copied below). The item is in a EquippableItemCollection, and so m_Equip is set to false. For the item to be equipped m_Equip needs to be true. My workaround is to remove the not (!) in the m_Equip assignment.

Before and after making this change I get the following warning message. How do I get rid of this?
Rich (BB code):
1 AssaultRifle (3729713782) || ItemCollection Hotbar (None) || ItemStack(-1171573632)[ 1 AssaultRifle (3729713782) in ItemCollection Hotbar (None)] cannot be moved to equippable because it was not in default first.

@Sangemdoko One last thing. Just in case you remember. I posted on the discord a question about the hotkeys not mapping correctly. This was because in UltimateCharacterLocomotion, I had the start type on the EquipUnequip ability set to Button down. I changed this to manual (I think this is correct)

C#:
        /// <summary>
        /// Returns true if the item action be invoked.
        /// </summary>
        /// <param name="itemInfo">The item.</param>
        /// <param name="itemUser">The inventory user.</param>
        /// <returns>True if it can be invoked.</returns>
        protected override bool CanInvokeInternal(ItemInfo itemInfo, ItemUser itemUser)
        {
            var characterLocomotion = itemUser.gameObject.GetCachedComponent<UltimateCharacterLocomotion>();
            if (characterLocomotion == null || !characterLocomotion.Alive) {
                return false;
            }

            var inventorySystemBridge = itemUser.gameObject.GetCachedComponent<CharacterInventoryBridge>();
            if (inventorySystemBridge == null) {
                return false;
            }
           
            var item = itemInfo.Item;
            // ====== Original Code ======
            //m_Equip = !inventorySystemBridge.EquippableItemCollections.Contains(item.ItemCollection);
            // ====== My Workaround ======
            m_Equip = inventorySystemBridge.EquippableItemCollections.Contains(item.ItemCollection);

            if (m_Equip) {
                m_Name = m_EquipActionName;
            } else {
                m_Name = m_UnequipActionName;
            }

            return true;
        }
 
Do you have the latest UIS/UCC integration package?
The Warning says that the item wasn't in the default collection before it you try to Equip it.
Could you try making the Hotbar ItemCollection into an Equippable Item Collection. This is done simply by adding the Hotbar ItemCollection name within the list of Equipppable Item Collection in the CharacterInventoryBridge component

That should allow you to Equip items that are inside the Hotbar Collection without moving them to another ItemCollection.
 
I do already have the Hotbar in the CharacterInventoryBridge equippable Item collection names. See screen capture below.

Debugging a little further, I can see that the warning comes from CharacterEquipUnequipHotbarAction, in method MoveItemToEquippable. So as you point out it is looking for it only in the Default item collection.

I am not sure why it is not looking at the Hotbar collection to check if it is equippable.

EDIT: The warning comes from CharacterEquipUnequipHotbarAction. In method InvokeActionInternal, it is hardcoded to call MoveEquip with an equippableItemCollectionSet = 0. This is passed to MoveEquip and then MoveItemToEquippable, where this value is used to retrieve the equippableCollection, the default collection (see code extract and comment below). That means it will always just check the default collection and never check the Hotbar collection and so throw that warning. In the comment below this, it says it will remove the item from the default collection? I think I got this right and it might be a bug? Or, should I be using a different action on the hotbar instead of CharacterEquipItemAction?

PS: Also, I downloaded the integration package again and checked. I do have the latest UCC/UIS integration package installed.

C#:
        public virtual ItemInfo MoveItemToEquippable(CharacterInventoryBridge inventorySystemBridge, ItemInfo itemInfo, int equippableCollectionIndex = 0, int slotIndex = -1)
        {
// *** equippableCollectionIndex will always be 0, the default collection.
            var equippableCollection = inventorySystemBridge.EquippableItemCollections.GetItemCollection(equippableCollectionIndex);

            if (equippableCollection == null)
            {
                Debug.LogWarning($"The equippable item collection at index {equippableCollectionIndex} could not be found", inventorySystemBridge.gameObject);
                return ItemInfo.None;
            }

            //Remove the item from Default collection.
            itemInfo = inventorySystemBridge.DefaultItemCollection.RemoveItem(itemInfo);

1643621180021.png
 
Last edited:
I took a stab at fixing this. Is this close? I put ** in the comments for the lines I changed.

I might have this totally backwards, because I don't understand the full context.

Code:
        /// <summary>
        /// Move an item from one collection to another.
        /// </summary>
        /// <param name="itemInfo">The item info.</param>
        protected override void InvokeActionInternal(ItemInfo itemInfo, ItemUser itemUser)
        {
            var inventorySystemBridge = itemUser.gameObject.GetCachedComponent<CharacterInventoryBridge>();

            // *** Added *** Get the collection index for the item (Probably a better way to do this)
            int equippableCollectionIndex = 0;
            for (int i = 0; i< inventorySystemBridge.EquippableItemCollections.GetItemCollectionCount(); i++)
            {
                if (itemInfo.ItemCollection == inventorySystemBridge.EquippableItemCollections.GetItemCollection(i))
                {
                    equippableCollectionIndex = i;
                    break;
                } 
            }

            //inventorySystemBridge.MoveEquip(itemInfo, m_Equip);
            // ** CHANGED ** MoveEquip(inventorySystemBridge, itemInfo, 0, -1, m_Equip);
            MoveEquip(inventorySystemBridge, itemInfo, equippableCollectionIndex, -1, m_Equip);
        }

        /// <summary>
        /// Move the Item from the Default Item Collection to one of the equippable item collections.
        /// </summary>
        /// <param name="itemInfo">The item info to move.</param>
        /// <param name="equippableCollectionIndex">The index of the equippable item collection.</param>
        /// <param name="slotIndex">The slot Index if the equippable is an item slot collection.</param>
        /// <returns>The item info moved.</returns>
        public virtual ItemInfo MoveItemToEquippable(CharacterInventoryBridge inventorySystemBridge, ItemInfo itemInfo, int equippableCollectionIndex = 0, int slotIndex = -1)
        {
            var equippableCollection = inventorySystemBridge.EquippableItemCollections.GetItemCollection(equippableCollectionIndex);

            if (equippableCollection == null)
            {
                Debug.LogWarning($"The equippable item collection at index {equippableCollectionIndex} could not be found", inventorySystemBridge.gameObject);
                return ItemInfo.None;
            }

            // ** CHANGED** if (inventorySystemBridge.DefaultItemCollection.HasItem(itemInfo) == false)
            if (equippableCollection.HasItem(itemInfo) == false)
            {
                Debug.LogWarning($"{itemInfo} cannot be moved to equippable because it was not in default first.", inventorySystemBridge.gameObject);
                return ItemInfo.None;
            }

            //Remove the item from Default collection.
            // ** CHANGED** itemInfo = inventorySystemBridge.DefaultItemCollection.RemoveItem(itemInfo);
            itemInfo = equippableCollection.RemoveItem(itemInfo);

            ItemInfo movedItemInfo;

            if (equippableCollection is Opsive.UltimateInventorySystem.Core.InventoryCollections.ItemSlotCollection equippableSlotCollection)
            {

                if (slotIndex == -1)
                {
                    slotIndex = equippableSlotCollection.GetTargetSlotIndex(itemInfo.Item);
                }

                var previousItemInSlot = equippableSlotCollection.GetItemInfoAtSlot(slotIndex);

                if (previousItemInSlot.Item != null)
                {
                    //If the previous item is stackable don't remove it.
                    if (previousItemInSlot.Item.StackableEquivalentTo(itemInfo.Item))
                    {
                        previousItemInSlot = ItemInfo.None;
                    }
                    else
                    {
                        previousItemInSlot = equippableSlotCollection.RemoveItem(slotIndex);
                    }
                }

                movedItemInfo = equippableSlotCollection.AddItem(itemInfo, slotIndex);

                if (previousItemInSlot.Item != null)
                {
                    Debug.Log("Moving old item back to inventory");
                    var previousItemInfo = inventorySystemBridge.DefaultItemCollection.AddItem(previousItemInSlot);
                 
                    // Previous item failed to move back to inventory. Drop it.
                    if (previousItemInfo.Amount == 0)
                    {
                        EventHandler.ExecuteEvent<ItemInfo>(CustomEventNames.c_Hotbar_Drop_Item, previousItemInSlot);
                    }
                }

            }
            else
            {
                movedItemInfo = equippableCollection.AddItem(itemInfo);
            }

            //Not all the item was added, return the items to the default collection.
            if (movedItemInfo.Amount != itemInfo.Amount)
            {
                var amountToReturn = itemInfo.Amount - movedItemInfo.Amount;
                // ** CHANGED** inventorySystemBridge.DefaultItemCollection.AddItem((ItemInfo)(amountToReturn, itemInfo));
                equippableCollection.AddItem((ItemInfo)(amountToReturn, itemInfo));
            }

            return movedItemInfo;

        }
 
I just double checked your CHaracterEquipUnequipItemAction look nothing like my current one.
1643625310757.png
You want this to be ticked on because you aren't moving the item
C#:
/// <summary>
/// Item action used to Move an item from one collection to another. It can be used to equip/unequip items too.
/// </summary>
[System.Serializable]
public class CharacterEquipUnequipItemAction : ItemAction
{
    [Tooltip("The name that should be displayed when the item can be equipped.")]
    [SerializeField] protected string m_EquipActionName = "Equip";
    [Tooltip("The name that should be displayed when the item can be unequipped.")]
    [SerializeField] protected string m_UnequipActionName = "Unequip";
    [Tooltip("Actively Equip the item if it is soft Equipped but not part of the active item set.")]
    [SerializeField] protected bool m_NotMoveEquip = false;
    private bool m_Equip;
    protected bool m_MoveEquip;
   
    /// <summary>
    /// Default constructor.
    /// </summary>
    public CharacterEquipUnequipItemAction()
    {
        m_Name = "Equip";
    }
    /// <summary>
    /// Returns true if the item action be invoked.
    /// </summary>
    /// <param name="itemInfo">The item.</param>
    /// <param name="itemUser">The inventory user.</param>
    /// <returns>True if it can be invoked.</returns>
    protected override bool CanInvokeInternal(ItemInfo itemInfo, ItemUser itemUser)
    {
        var characterLocomotion = itemUser.gameObject.GetCachedComponent<UltimateCharacterLocomotion>();
        if (characterLocomotion == null || !characterLocomotion.Alive) {
            return false;
        }
        var inventorySystemBridge = itemUser.gameObject.GetCachedComponent<CharacterInventoryBridge>();
        if (inventorySystemBridge == null) {
            return false;
        }
       
        var item = itemInfo.Item;
       
        if (m_NotMoveEquip) {
            m_MoveEquip = false;
            m_Equip = !inventorySystemBridge.IsItemActive(item);
        } else {
            m_MoveEquip = true;
            m_Equip = !inventorySystemBridge.EquippableItemCollections.Contains(item.ItemCollection);
        }
       
       
       
        if (m_Equip) {
            m_Name = m_EquipActionName;
        } else {
            m_Name = m_UnequipActionName;
        }
        return true;
    }
    /// <summary>
    /// Move an item from one collection to another.
    /// </summary>
    /// <param name="itemInfo">The item info.</param>
    protected override void InvokeActionInternal(ItemInfo itemInfo, ItemUser itemUser)
    {
        var inventorySystemBridge = itemUser.gameObject.GetCachedComponent<CharacterInventoryBridge>();
        if (m_MoveEquip) {
            inventorySystemBridge.MoveEquip(itemInfo,m_Equip);
        } else {
            inventorySystemBridge.Equip(itemInfo,m_Equip);
        }
       
    }
}
I double checked the integration download and it does have the correct ItemAction. I checked with the TPC download but they should all be the same.

Are you sure you are importing the correct package? What download are you using?
 
Sorry Sange. I messed up. I had a customized version I must have swapped over several months ago and didn't realize it was my code that I was looking at not yours.

My sincere apologies
 
Top