Inventory is being destroyed

Hi there, and apologies for askign yet another question. I am trying to access my inventory and, through the PixelCrushers Dialogue System, see if my inventory contains an item by checking it’s UUID. This was working previously, but now it appears that every time the function is ran it says that the Inventory has been destroyed, therefore i’m getting a null reference exception. I can neither think nor find anywhere where this would be happening, as the Inventory is contained on the Player Character and that is still very much in the scene. Any help in this matter would be greatly appreciated.

namespace GameDevTV.Inventories
{
    /// <summary>
    /// Provides storage for the player inventory. A configurable number of
    /// slots are available.
    ///
    /// This component should be placed on the GameObject tagged "Player".
    /// </summary>
    public class Inventory : MonoBehaviour, ISaveable
    {
        // CONFIG DATA
        [Tooltip("Allowed size")]
        [SerializeField] int inventorySize = 16;

        // STATE
        InventorySlot[] slots;

        public struct InventorySlot
        {
            public InventoryItem item;
            public int number;
        }

        // PUBLIC
        [Tooltip("Typically leave unticked so temporary Dialogue Managers don't unregister your functions.")]
        public bool unregisterOnDisable = false;
        /// <summary>
        /// Broadcasts when the items in the slots are added/removed.
        /// </summary>
        public event Action inventoryUpdated;

        /// <summary>
        /// Convenience for getting the player's inventory.
        /// </summary>
        public static Inventory GetPlayerInventory()
        {
            var player = GameObject.FindWithTag("Player");
            return player.GetComponent<Inventory>();
        }

        /// <summary>
        /// Could this item fit anywhere in the inventory?
        /// </summary>
        public bool HasSpaceFor(InventoryItem item)
        {
            return FindSlot(item) >= 0;
        }

        /// <summary>
        /// How many slots are in the inventory?
        /// </summary>
        public int GetSize()
        {
            return slots.Length;
        }

        /// <summary>
        /// Attempt to add the items to the first available slot.
        /// </summary>
        /// <param name="item">The item to add.</param>
        /// <param name="number">The number to add.</param>
        /// <returns>Whether or not the item could be added.</returns>
        public bool AddToFirstEmptySlot(InventoryItem item, int number)
        {
            int i = FindSlot(item);

            if (i < 0)
            {
                return false;
            }

            slots[i].item = item;
            slots[i].number += number;
            print(slots[i].number);
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
            return true;
        }

        /// <summary>
        /// Is there an instance of the item in the inventory?
        /// </summary>
        public bool HasItem(InventoryItem item)
        {
            for (int i = 0; i < slots.Length; i++)
            {
                if (object.ReferenceEquals(slots[i].item, item))
                {
                    return true;
                }
            }
            return false;
        }

        public bool HasItemID(string itemID)
        {
            //if (slots[0].item == null) return false;
            if(this.gameObject == null)
            {
                return false;
            }
            for (int i = 0; i < slots.Length; i++)
            {
                if (slots[i].item != null)
                {
                    if (slots[i].item.GetItemID() == itemID)
                    {
                        return true;
                    }
                }  
            }
            return false;
        }

        /// <summary>
        /// Return the item type in the given slot.
        /// </summary>
        public InventoryItem GetItemInSlot(int slot)
        {
            return slots[slot].item;
        }

        /// <summary>
        /// Get the number of items in the given slot.
        /// </summary>
        public int GetNumberInSlot(int slot)
        {
            return slots[slot].number;
        }

        /// <summary>
        /// Remove a number of items from the given slot. Will never remove more
        /// that there are.
        /// </summary>
        public void RemoveFromSlot(int slot, int number)
        {
            slots[slot].number -= number;
            if (slots[slot].number <= 0)
            {
                slots[slot].number = 0;
                slots[slot].item = null;
            }
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
        }

        public void RemoveWithID(string itemID, double number)
        {

            for (int i = 0; i < slots.Length; i++)
            {
                if (slots[i].item != null)
                {
                    if (slots[i].item.GetItemID() == itemID)
                    {
                        RemoveFromSlot(i, Convert.ToInt32(number));
                        return;
                    }
                }
            }
            //slots[slot].number -= number;
            //if (slots[slot].number <= 0)
            //{
            //    slots[slot].number = 0;
            //    slots[slot].item = null;
            //}
            //if (inventoryUpdated != null)
            //{
            //    inventoryUpdated();
            //}
        }

        /// <summary>
        /// Will add an item to the given slot if possible. If there is already
        /// a stack of this type, it will add to the existing stack. Otherwise,
        /// it will be added to the first empty slot.
        /// </summary>
        /// <param name="slot">The slot to attempt to add to.</param>
        /// <param name="item">The item type to add.</param>
        /// <param name="number">The number of items to add.</param>
        /// <returns>True if the item was added anywhere in the inventory.</returns>
        public bool AddItemToSlot(int slot, InventoryItem item, int number)
        {
            if (slots[slot].item != null)
            {
                return AddToFirstEmptySlot(item, number); ;
            }

            var i = FindStack(item);
            if (i >= 0)
            {
                slot = i;
            }

            slots[slot].item = item;
            slots[slot].number += number;
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
            return true;
        }

        // PRIVATE

        private void Awake()
        {
            slots = new InventorySlot[inventorySize];
        }

        /// <summary>
        /// Find a slot that can accomodate the given item.
        /// </summary>
        /// <returns>-1 if no slot is found.</returns>
        private int FindSlot(InventoryItem item)
        {
            int i = FindStack(item);
            if (i < 0)
            {
                i = FindEmptySlot();
            }
            return i;
        }

        /// <summary>
        /// Find an empty slot.
        /// </summary>
        /// <returns>-1 if all slots are full.</returns>
        private int FindEmptySlot()
        {
            for (int i = 0; i < slots.Length; i++)
            {
                if (slots[i].item == null)
                {
                    return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// Find an existing stack of this item type.
        /// </summary>
        /// <returns>-1 if no stack exists or if the item is not stackable.</returns>
        private int FindStack(InventoryItem item)
        {
            if (!item.IsStackable())
            {
                return -1;
            }

            for (int i = 0; i < slots.Length; i++)
            {
                 if (object.ReferenceEquals(slots[i].item, item))
                {
                    return i;
                }
            }
            return -1;
        }

        [System.Serializable]
        private struct InventorySlotRecord
        {
            public string itemID;
            public int number;
        }

        object ISaveable.CaptureState()
        {
            var slotStrings = new InventorySlotRecord[inventorySize];
            for (int i = 0; i < inventorySize; i++)
            {
                if (slots[i].item != null)
                {
                    slotStrings[i].itemID = slots[i].item.GetItemID();
                    slotStrings[i].number = slots[i].number;
                }
            }
            return slotStrings;
        }

        void ISaveable.RestoreState(object state)
        {
            var slotStrings = (InventorySlotRecord[])state;
            for (int i = 0; i < inventorySize; i++)
            {
                slots[i].item = InventoryItem.GetFromID(slotStrings[i].itemID);
                slots[i].number = slotStrings[i].number;
            }
            if (inventoryUpdated != null)
            {
                inventoryUpdated();
            }
        }

        void OnEnable()
        {
            // Make the functions available to Lua: (Replace these lines with your own.)
            Lua.RegisterFunction(nameof(HasItemID), this, SymbolExtensions.GetMethodInfo(() => HasItemID(string.Empty)));
            Lua.RegisterFunction(nameof(RemoveWithID), this, SymbolExtensions.GetMethodInfo(() => RemoveWithID(string.Empty,(double)0)));
        }

        void OnDisable()
        {
            if (unregisterOnDisable)
            {
                // Remove the functions from Lua: (Replace these lines with your own.)
                Lua.UnregisterFunction(nameof(HasItemID));
                Lua.UnregisterFunction(nameof(RemoveFromSlot));
            }
        }
    }
}

It would be helpful to see the full text of the error message and the script that is throwing the error (with the line number pointed out as our code blocks in this forum don’t provide line numbers).

That might give a clue as to where to start looking…

I will note that there is a potential mismatch between your OnEnable and OnDisable in the Inventory.cs… In the event that unregisterOnDisable is false, the reference won’t get removed from your Lua registered functions…

Now normally, we don’t enable and disable the player, except that on a scene change these things happen automatically… So if unregisterOnDisable is false…

  • Start in Scene 1, OnEnable registers the HasItemID and RemoveWithID methods tied to the Inventory on the player in scene 1…
  • Transition to Scene 2, OnDisable is not called, leaving the Instance calls of HasItemID and RemoveWithID still registered with Lua. OnEnable() is then called to register the new HasItemID() and RemoveWithID() methods, but when Lua tries to access the event, it tries to call them on both inventories, and the first one becomes… a MissingReferenceException

Privacy & Terms