Set field in item action at runtime

Robot_Nachos

New member
So I have some equipment view slots that when you click on them they open an item grid that contains all of the appropriate items for those slots. this grid has an item action panel equips the item to a slot by its string. I'm trying to use a unity event to set that string whenever the player clicks on an item slot, so that I don't need a specific action for each slot. I'm having a bit of trouble figuring out how to pass that string to the item action in the first place.

Is using unity events the right use case here or is there a better method?
 
For a use case like that I would recommend you inherit the "Item View Slots Container Item Action" class with your custom item action to open the grid. (or you can use the relevant interface if you want to inherit another class)

It is the last one explained on this page:

This will allow you to get a reference to the ItemViewSlotContainer that called the item action. Which should allow you to get the slot that the item action was called from.



Another option, since you are using an equipment view. You can have access to the ItemSlotCollection in your inventory and then you can try assigning the slotIndex (or slotName) via code. Either directly to the other item action, or through a "middleman" component:
Code:
var itemSlotCollection = itemInfo.Inventory.GetItemCollection("Equipment") as ItemSlotCollection;
var slotIndex = itemSlotCollection.GetItemSlotIndex(itemInfo.ItemStack);

//Set that slot Index in your custom Item Action for equipping.
//This can be done by serializing the second item action scriptable object inside the first.
m_ItemActionSet.ItemActionCollection.Initialize(false);

//Replace MoveToCollectionItemAction to your custom ItemAction which has a property of
var myEquipmentItemAction = m_ItemActionSet.ItemActionCollection[0] as MoveToCollectionItemAction;

//myEquipmentItemAction.SlotIndex = slotIndex.


//Or you can have a custom component either in the first or second DisplayPanel or ItemViewSlotContainer gameobject
//Which can have a public property for the slotIndex.
//Similar to how we made the MoveItemAction, check the source code if you are curious.


I hope that points you in the correct direction.
 
So I've tried implementing this with a middleman component and I'm getting a null reference exception, any ideas as to what I missed?

Rich (BB code):
NullReferenceException: Object reference not set to an instance of an object
Opsive.UltimateInventorySystem.ItemActions.OpenTechniquePanelItemAction.CanInvokeInternal (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, Opsive.UltimateInventorySystem.ItemActions.ItemUser itemUser) (at Assets/OpenTechniquePanelItemAction.cs:45)
Opsive.UltimateInventorySystem.ItemActions.ItemAction.CanInvoke (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, Opsive.UltimateInventorySystem.ItemActions.ItemUser itemUser) (at Assets/Opsive/UltimateInventorySystem/Scripts/ItemActions/ItemAction.cs:59)
Opsive.UltimateInventorySystem.ItemActions.ItemAction.InvokeAction (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, Opsive.UltimateInventorySystem.ItemActions.ItemUser itemUser) (at Assets/Opsive/UltimateInventorySystem/Scripts/ItemActions/ItemAction.cs:95)
Opsive.UltimateInventorySystem.UI.Panels.ItemViewSlotContainers.ItemViewSlotsContainerItemActionBindingBase.InvokeActionInternal (System.Int32 itemSlotIndex, System.Int32 itemActionIndex) (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Panels/ItemViewSlotContainers/ItemViewSlotsContainerItemActionBindingBase.cs:400)
Opsive.UltimateInventorySystem.UI.Panels.ItemViewSlotContainers.ItemViewSlotsContainerItemActionBindingBase.UseAllItemActions (System.Int32 itemSlotIndex) (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Panels/ItemViewSlotContainers/ItemViewSlotsContainerItemActionBindingBase.cs:301)
Opsive.UltimateInventorySystem.UI.Panels.ItemViewSlotContainers.ItemViewSlotsContainerItemActionBindingBase.TriggerItemAction (Opsive.UltimateInventorySystem.UI.Item.ItemViewSlot itemViewSlot) (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Panels/ItemViewSlotContainers/ItemViewSlotsContainerItemActionBindingBase.cs:264)
Opsive.UltimateInventorySystem.UI.Panels.ItemViewSlotContainers.ItemViewSlotsContainerItemActionBindingBase.TriggerItemAction () (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Panels/ItemViewSlotContainers/ItemViewSlotsContainerItemActionBindingBase.cs:229)
Opsive.UltimateInventorySystem.UI.Panels.ItemViewSlotContainers.ItemViewSlotsContainerItemActionBindingBase.HandleItemClicked (Opsive.UltimateInventorySystem.UI.Item.ItemViewSlotEventData eventdata) (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Panels/ItemViewSlotContainers/ItemViewSlotsContainerItemActionBindingBase.cs:152)
Opsive.UltimateInventorySystem.UI.Item.ItemViewSlotsContainerBase+<>c__DisplayClass78_0.<Initialize>b__0 () (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Item/ItemViewSlotsContainerBase.cs:216)
Opsive.UltimateInventorySystem.UI.CompoundElements.ActionButton.Press () (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/CompoundElements/ActionButton.cs:88)
Opsive.UltimateInventorySystem.UI.CompoundElements.ActionButton.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/CompoundElements/ActionButton.cs:152)
Opsive.UltimateInventorySystem.UI.Item.ItemViewSlot.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Assets/Opsive/UltimateInventorySystem/Scripts/UI/Item/ItemViewSlot.cs:191)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:50)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:262)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:385)

C#:
namespace Opsive.UltimateInventorySystem.ItemActions
{
    using Opsive.UltimateInventorySystem.Core;
    using Opsive.UltimateInventorySystem.Core.DataStructures;
    using Opsive.UltimateInventorySystem.Core.InventoryCollections;
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine.Events;
    using Opsive.UltimateInventorySystem.UI.CompoundElements;
    using Opsive.UltimateInventorySystem.ItemActions;
    using Opsive.Shared.Game;
    using Opsive.UltimateInventorySystem.Storage;
    using System;
    using Opsive.UltimateInventorySystem.Equipping;

    [System.Serializable]
    public class OpenTechniquePanelItemAction : ItemViewSlotsContainerItemAction
    {
        //[SerializeField] protected InventorySystemManager inventorySystemManager;
        //[SerializeField] public ItemViewSlotsContainerItemAction itemViewSlots;
        [SerializeField] public ActiveSlotHolder activeSlotHolder;
        public OpenTechniquePanelItemAction()
        {
            m_Name = "Open";
        }

        /// <summary>
        /// Can the item action be invoked.
        /// </summary>
        /// <param name="itemInfo">The item info.</param>
        /// <param name="itemUser">The item user (can be null).</param>
        /// <returns>True if it can be invoked.</returns>
        protected override bool CanInvokeInternal(ItemInfo itemInfo, ItemUser itemUser)
        {
            // Return true if the item can be invoked or false if it cannot.
            Debug.Log("CanInvoke");
            // Get the Display Panel Manager by ID from the Inventory System Manager singleton.
            var displayPanelManager = InventorySystemManager.GetDisplayPanelManager(1);
            //get the panel from that manager
            var TechniquePanel = displayPanelManager.GetPanel("NormalTechniques");
            TechniquePanel.SmartToggle();

            var itemSlotCollection = itemInfo.Inventory.GetItemCollection("NormalCombo") as ItemSlotCollection;
            var slotIndex = itemSlotCollection.GetItemSlotIndex(itemInfo.ItemStack);

            activeSlotHolder = GameObject.FindWithTag("ActiveSlotHolderObject").GetComponent<ActiveSlotHolder>();
            activeSlotHolder.activeSlot = slotIndex;

            return true;
        }

        /// <summary>
        /// Consume the item.
        /// </summary>
        /// <param name="itemInfo">The item info.</param>
        /// <param name="itemUser">The item user (can be null).</param>
        protected override void InvokeActionInternal(ItemInfo itemInfo, ItemUser itemUser)
        {


            Debug.Log("Invoked");


        }
        // Invoke the item action

    }
}
 
Last edited:
Well the error says it happens on line 45.
I assume that your "itemSlotCollection" is null. Either "NormalCombo" item collection does not exist, or it is is not an ItemSlotCollection.
 
In that case use Debug.Log to see exaclty what is null. Or use your IDE debug break point to step through the code and see what's going on.
Perhaps you are looking in the wrong Inventory
 
Ah, I think see what's going on now. I'm getting a null reference because those equipment slots start off empty so there's no inventory for the itemInfo parameter to get. Does that sound about right?
 
Yeah that makes sense.
Try getting the Inventory from the ItemUser instead.

itemUser.Inventory
or by ID
var inventory = InventorySystemManager.GetInventoryIdentifier(id).Inventory
 
Ok that works fine, but GetItemSlotIndex needs an ItemStack so it puts out null when I click on an empty slot. Is making an item to represent an empty slot a good workaround? Or can I do something else?
 
I'm sorry I forgot about that. Getting the index from the ItemSlotCollection won't work for empty stacks.

I added a new interface to help you with what you are trying to do: "IActionWithSlotContainer"

Add this file in Opsive/UltimateInventorySystem/Script/ItemActions
C#:
/// ---------------------------------------------
/// Ultimate Inventory System
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------

namespace Opsive.UltimateInventorySystem.ItemActions
{
    using Opsive.UltimateInventorySystem.UI.Item;

    /// <summary>
    /// An interface for an item action panel.
    /// </summary>
    public interface IActionWithSlotContainer
    {
        /// <summary>
        /// Set the Item View Slots Container.
        /// </summary>
        /// <param name="itemViewSlotsContainer">The item View Slots Container.</param>
        /// <param name="itemViewSlotIndex">The index of the selected Item View Slot.</param>
        void SetViewSlotsContainer(ItemViewSlotsContainerBase itemViewSlotsContainer, int itemViewSlotIndex);
    }
}

To avoid upgrade bugs this is the meta file contents:
Code:
fileFormatVersion: 2
guid: 93be75392ad6420ebf4a004755c685db
timeCreated: 1658129416

In the InvokeActionInternal function of the ItemViewSlotsContainerItemActionBindingBase script replace ItemViewSlotsContainerItemAction by the new interface IActionWithSlotContainer.

Finally make ItemViewSlotsContainerItemAction inherit from IActionWithSlotContainer

So it should look something like that:
1658129842272.png
As you can see when I invoke an ItemAction from the ItemViewSlotsContainerItemActionBindingBase class I set the container and item slot index to the itemActions inheriting that interface. So now you can use that interface to know which slot was selected to call the item action.

You can also inherit the IActionWithPanel to know the panel and item previous selectable (item view slot).

I hope that helps.
 
Top