Initializing a custom attribute globally

altug

New member
I'm using a custom attribute to be able to have items with procedural stats & modifications. My attribute is simply keeping that randomized mods.
But first I have to generate those mods for the item (ideally when the item is initialized / created in the inventory system)
I can use EventNames.c_Inventory_OnAdd_ItemInfo_ItemStack event to be aware when that item type is added, then I can access the item to randomize values and apply to the attribute, but as I see that event just responsible for a specific inventory, but I need to do this globally, like when an item from that category drops from a monster, or spawned in a shop, lootbox etc.

I tried to make that attribute a Monobehaviour so that I can use OnAwake() for this, but I can not access if the attribute is monobehaviour and gives nullreference error.

For now, I'm manually iterating my characters itemCollection and generate data for those kind of items to be able to test my gameplay;


Code:
opInventory = player.GetComponent<Inventory>();

        ItemCollection ItemCollection = opInventory.GetItemCollection("NewItemCollection");
        var allItemStacks = ItemCollection.GetAllItemStacks();

        for (int i = 0; i < allItemStacks.Count; i++)
        {
            var itemStack = allItemStacks[i];
            var item = itemStack.Item;

            if(item == null){ continue;}
            if (item.GetItemCategory().name == "Submodule")
            {
                var sData = item.GetAttribute<Attribute<SubmoduleData>>("Submodule Data");

                if (sData.GetValue() == null)
                    Debug.LogWarning("submoduleData is null");

                if (sData.GetValue().itemLevel == 0) //means its not generated yet, minimum level is 1
                {
                    GenerateSubmodule();
                    sData.SetOverrideValue(GenerateSubmoduleData());
                }
            }
 
Last edited:
Happy new year!
I'm sorry for the delay I was on Holiday.

That's a very good question. The way you would do this is by overriding when items get created. I don't have the project open in front of me so I can't confirm 100% but I believe there is some kind of event you can register for item initialization. If you can't find it another approach is to override the InventorySystemManager component. In there you can override the function to create items and then do whatever initialization you need. All items get created in the item factory so you'll know your items will be initalized the way you want. it's also a great way to have control over probabilities to ensure they are not really random, but more designed towards controlled "fun" or whatever you want.
 
Happy new year Sangemdoko!, thanks for your answer.
While waiting the answer, I tried a similar approach as you described after some digging. I inherited the InventorySystemFactory and override createItem method and added an action;


Code:
public Action<Item> OnItemCreated;
public MyInventorySystemFactory(IInventorySystemManager manager) : base(manager)  { }

public override Item CreateItem(Item item, IReadOnlyList<AttributeBase> overrideAttributes = null)
{
    Item newItem = base.CreateItem(item, overrideAttributes);
    if (item.Category.name == "Submodule")
    {
        //Debug.Log($"Submodule Created: {newItem.name}");
        OnItemCreated?.Invoke(newItem);
    }
    return newItem;
}

and I had to edit the InventorySystemManager itself;
Code:
protected virtual void CreateInventorySystemFactory()
{
    m_Factory = new MyInventorySystemFactory(this);
}

I tried to inherit InventorySystemManager to just override that method, but unity throws me errors if I try to use inherited InventorySystemManager instead the base class.

This setup worked perfectly, I just need to edit single method of base InventorySystemManager incase of an update.
 
What was the error message exactly out of curiosity?

But at least I'm glad you were able to get it working by changing the source code directly. It's a good workaround for now
 
This is the inherited manager code:
Code:
using Opsive.UltimateInventorySystem.Core;

public class MyInventorySystemManager : InventorySystemManager
{
    protected override void CreateInventorySystemFactory()
    {
        m_Factory = new MyInventorySystemFactory(this);
    }
}

If I use this, I got 9-10 reference errors like;
NullReferenceException: Object reference not set to an instance of an object
Opsive.UltimateInventorySystem.Core.InventorySystemManager.CreateItem (Opsive.UltimateInventorySystem.Core.Item item, System.Collections.Generic.IReadOnlyList`1[T] attributes) (at Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventorySystemManager.cs:132)

And other NullReferenceException's with the last error line only:
Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventorySystemManager.cs:55)
Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventorySystemManager.cs:64)
Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventorySystemManager.cs:68)
Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventorySystemManager.cs:65)
Assets/Opsive/UltimateInventorySystem/Scripts/UI/Panels/PanelOpener.cs:46)
 
Sorry for the delay.
I found the issue. The problem is the execution order. You custom InventorySystemManager needs the same script execution number as the base script (-160). You can set that up in the Project Settings -> Script Execution Order. Then drag and drop your script and set the value.

I hope that helps :)
 
Btw I'm adding an example of this in the next update as a new feature sample scene. Thank you for the suggestion :)
 
Back
Top