Item Shape Grid + MultiStackItemCollection crash

MajinSky

New member
Hi
Ultimate Inventory System version 1.1.6 and 1.1.8 too

When over the limit for one stack I have error:image.png

the item tries to add itself to the same place instead of next to it.
Everything works fine in the standard Inventory Grid

I am asking for an update to fix this problem.


Thank you and I am waiting for an answer.
 

Sangemdoko

Moderator
Staff member
Hi,

Could you explain what you are doing in more detail such that I can try to reproduce this issue?
How did you set the limit? Just the Shape Grid or do you have other Item Restrictions?

Perhaps some of the restrictions are incompatible toghether, if you show me your setup I might be able to reproduce the issue and get it fixed before the next update.
 

MajinSky

New member
1. add inventory shape grid
2. add to inventory(scripts) -> Item Collection ->MultiStackItemColletion
3.Limit Stack size to 10
4.Add some items to pickup (5x apple, 20x apple)
5.pickup item and get error

Video Link: (Edit Oryginal Demo Scene)

Thank you and I am waiting for an answer.
 

Sangemdoko

Moderator
Staff member
Thank you for the video it helped me replicate the issue.

So the probleme was that the item thought it could stack in the shape grid because the item was a common item. But since you have two ItemCollections they can't stack. So the grid shape was confused.

So far I changed this in the ItemShapeGridData which seems to solve the issue.

Code:
/// <summary>
/// Is the exact position available for the item to be placed.
/// </summary>
/// <param name="info">The item to check the position for.</param>
/// <param name="position">The position to check.</param>
/// <param name="canIgnore">Can certain positions be ignored.</param>
/// <returns>True if the position is available for that particular item.</returns>
protected virtual bool IsSingularPositionAvailable(ItemInfo info, Vector2Int position, Func<Vector2Int, bool> canIgnore = null)
{
    if (canIgnore?.Invoke(position) ?? false) { return true; }
    var gridElementData = m_ItemStackAnchorGrid[position.x, position.y];
    if (!gridElementData.IsOccupied) { return true; }
    if (gridElementData.ItemStack == info.ItemStack) { return true; }
    
    return false;
}


After fixing that there was another issue with only one stack in the multi stacks appearing in the shape grid. That was because the shape grid was listening to an Add event, but the multi stack was only sending one for the last added item stack.

With the change in MultiStackItemCollection below I now send events for each item stack added.

Code:
/// <summary>
/// Add an ItemAmount in an organized way.
/// </summary>
/// <param name="itemInfo">The item info being added to the item collection.</param>
/// <param name="targetStack">The item stack where the item should be added to (Can be null).</param>
protected override ItemInfo AddInternal(ItemInfo itemInfo, ItemStack targetStack, bool notifyAdd = true)
{
    var amountToAdd = itemInfo.Amount;
    var maxStackSize = GetMaxStackSize(itemInfo.Item);
    ItemStack addedItemStack = null;
    if (m_ItemStacks.Contains(targetStack)) {
        addedItemStack = targetStack;
        IncreaseStackAmount(targetStack, maxStackSize, ref amountToAdd);
        if (notifyAdd) {
            NotifyAdd(itemInfo, addedItemStack);
        }
    }
    for (int i = 0; i < m_ItemStacks.Count; i++) {
        var itemStack = m_ItemStacks[i];
        if (CanItemStack(itemInfo, itemStack) == false) { continue; }
        addedItemStack = itemStack;
        IncreaseStackAmount(itemStack, maxStackSize, ref amountToAdd);
        if (notifyAdd) {
            NotifyAdd(itemInfo, addedItemStack);
        }
    }
    
    itemInfo.Item.AddItemCollection(this);
    var stacksToAdd = amountToAdd / maxStackSize;
    var remainderStack = amountToAdd % maxStackSize;
    for (int i = 0; i < stacksToAdd; i++) {
        var newItemStack = GenericObjectPool.Get<ItemStack>();
        newItemStack.Initialize((itemInfo.Item, maxStackSize), this);
        addedItemStack = newItemStack;
        m_ItemStacks.Add(newItemStack);
        if (notifyAdd) {
            NotifyAdd(itemInfo, addedItemStack);
        }
    }
    if (remainderStack != 0) {
        var newItemStack = GenericObjectPool.Get<ItemStack>();
        newItemStack.Initialize((itemInfo.Item, remainderStack), this);
        addedItemStack = newItemStack;
        m_ItemStacks.Add(newItemStack);
        if (notifyAdd) {
            NotifyAdd(itemInfo, addedItemStack);
        }
    }
    return (itemInfo.Item, itemInfo.Amount, this, addedItemStack);
}

but I'll need to test it a bit more to be sure there aren't any other issues that come up. If you find any more errors/issues do let me know
 

MajinSky

New member
I found one more error after fix before problem

InventoryMonitor shows the added item too many times.

If I have 6 items stack (Crystal) on inventory and I pickup 1x Crystal InventoryMonitor shows Me 7x times (Crystal x 1)

And If i pickup Stack Object (20x) and Stack max limit is 10 InventoryMonitor shows Me 2 times 20x (should be 2 times x10)

If I did not explain it correctly, I can record a video.
 

Sangemdoko

Moderator
Staff member
Good catch! I did not notice it.
I was sending the event with the original amount to add each time instead of the amount actually added in each stack. I also added a condition to not send the event if the amount added to that stack was 0.

Here is the fixed code

Code:
/// <summary>
/// Add an ItemAmount in an organized way.
/// </summary>
/// <param name="itemInfo">The item info being added to the item collection.</param>
/// <param name="targetStack">The item stack where the item should be added to (Can be null).</param>
protected override ItemInfo AddInternal(ItemInfo itemInfo, ItemStack targetStack, bool notifyAdd = tru
{
    var amountToAdd = itemInfo.Amount;
    var maxStackSize = GetMaxStackSize(itemInfo.Item);
    ItemStack addedItemStack = null;
    if (m_ItemStacks.Contains(targetStack)) {
        addedItemStack = targetStack;
        var amountAdded = IncreaseStackAmount(targetStack, maxStackSize, ref amountToAdd);
        if (amountAdded > 0 && notifyAdd) {
            NotifyAdd((amountAdded, itemInfo), addedItemStack);
        }
    }
    for (int i = 0; i < m_ItemStacks.Count; i++) {
        var itemStack = m_ItemStacks[i];
        if (CanItemStack(itemInfo, itemStack) == false) { continue; }
        addedItemStack = itemStack;
        var amountAdded = IncreaseStackAmount(itemStack, maxStackSize, ref amountToAdd);
        if (amountAdded > 0 && notifyAdd) {
            NotifyAdd((amountAdded, itemInfo), addedItemStack);
        }
    }
    
    itemInfo.Item.AddItemCollection(this);
    var stacksToAdd = amountToAdd / maxStackSize;
    var remainderStack = amountToAdd % maxStackSize;
    for (int i = 0; i < stacksToAdd; i++) {
        var newItemStack = GenericObjectPool.Get<ItemStack>();
        newItemStack.Initialize((itemInfo.Item, maxStackSize), this);
        addedItemStack = newItemStack;
        m_ItemStacks.Add(newItemStack);
        if (notifyAdd) {
            NotifyAdd((addedItemStack.Amount, itemInfo), addedItemStack);
        }
    }
    if (remainderStack != 0) {
        var newItemStack = GenericObjectPool.Get<ItemStack>();
        newItemStack.Initialize((itemInfo.Item, remainderStack), this);
        addedItemStack = newItemStack;
        m_ItemStacks.Add(newItemStack);
        if (notifyAdd) {
            NotifyAdd((addedItemStack.Amount, itemInfo), addedItemStack);
        }
    }
    return (itemInfo.Item, itemInfo.Amount, this, addedItemStack);
}
/// <summary>
/// Increase the stack amount.
/// </summary>
/// <param name="targetItemStack">The item stack to increase the amount from.</param>
/// <param name="maxStackSize">The max stack size.</param>
/// <param name="amountToAdd">The amount to add.</param>
/// <returns>The amount added to the stack.</returns>
private int IncreaseStackAmount(ItemStack targetItemStack, int maxStackSize, ref int amountToAdd)
{
    if (targetItemStack.Amount == maxStackSize) { return 0; }
    var originalAmountToAdd = amountToAdd;
    var totalToSet = targetItemStack.Amount + amountToAdd;
    var sizeDifference = totalToSet - maxStackSize;
    if (sizeDifference <= 0) {
        targetItemStack.SetAmount(totalToSet);
        amountToAdd = 0;
    } else {
        targetItemStack.SetAmount(maxStackSize);
        amountToAdd = sizeDifference;
    }
    var amountAdded = originalAmountToAdd - amountToAdd;
    return amountAdded;
}

Thank you for testing this, and if you find anything else do let me know, I'll fix it asap
 

MajinSky

New member
Hello, I found one more problem or 2 in the ItemShapeGrid.

First:
If there is no inventory space for a 2x2 item, the item can be picked up, but it just disappears.

Second:
Automatic item sorting does not work.

waiting for a reply and thank you.
 

Sangemdoko

Moderator
Staff member
The first issue is something we are looking to fix for the next update. Currently when an item does not fit it simply does not get added.
In the next update we are adding an event for items that get rejected by the inventory.

In the case of pickups though we plan to add an option to prevent pickups if the item does not fit in the inventory.

When it comes to equipping/unequipping you might have the same issue with an item disappearing because it does not fit. If that's the case you should write a custom Item Action for equipping/unequipping to choose what happens if the equipped item does not fit back in the inventory. unfortunalty there's no generic solution for that (apart from listening to the new rejection event we are adding in the next update).

For the second issue, what do you mean by automatic item sorting? If you meant the ItemInfoFilterSorter then that's normal, The item shape grid cannot be sorted with the same sorter as a normal Inventory Grid, the filters work fine though. You will need to sort it using a custom sorting algorithm. Since it is 2D it is more complicated than just saying sort by name or by attribute value.
 

MajinSky

New member
okay, thank you for the information.
Here are screenshots with the description of the second problem.
1.jpg
2.jpg
3.jpg


when it comes to the first problem.

In which script do I have to add the Event?

Thank you and I am sorry if I am writing not very clearly :)
 

Sangemdoko

Moderator
Staff member
Ah I see... that's not a feature that exists. I can add it to my list of TODOs, but I'm afraid that it won't make the next update.
If you wish you can create a custom ItemShapeGridData to add that functionality yourself.

That being said the item should have been able to fit since if it was placed two slots on the right it would fit. I'll add that to things to investigate.

As for the rejection event, it does not exist yet, it will be available in the next update. Like all events in the system it will be in the "EventNames.cs" file and will have a little description on the data required, I will also update the documentation when the update comes out to show some API examples.
 
Top