Stackeable and non-stackeable items on inventory system

So I’m working in an inventory system for a game, everything was good but then I wanted to make a non-stackeable and stackeable items, the problem here, is that idk how to do it? Since Items are structured with dictionaries and for example, with the non-stackeable items there is already a value with the same key so it will just overwrite it instead of making another one.
I have the same problem as this person here.

1 Like

I dont know much about this but if the inventory is a GUI for each image label or text label i would put in int value in it with the amount of items that player has and a boolvalue named Stackable and if it equals false then it cant be stacked and if it is true then it can be stacked

I’m not using instance values, I’m using modules scripts and tables to manage the inventory with some functions.

but you could still use the int value if you are going to show the amount the player has so it can be edited by any script and so the player knows how much of the item they have
but with the modules i wont be of much help

If each Item is in a Specific Slot you could make a

if Module[Item].Stackable then
     local CurrentAmount = tonumber(Slot.Amount.Text);
     Slot.Amount.Text = tostring(CurrentAmount + Module[Item].Amount)
else
    -- You create a New Slot in the Inventory
end
1 Like

If you plan on doing a Minecraft-like approach, instead of using a dictionary, use an array of “slots”.

For example (just so we’re on the same page):

player.inventory = {
    {
        Item = "minecraft:diamond_sword",
        Count = 1,
        NBT = {...}
    }
}

When defining a non-stackable item, simply assign its max stack size to 1. Because when you think about it, non-stackable items don’t need to be that different from regular ones, just that they cannot stack past 1; any sorting/merging logic is effectively bypassed.

As for your particular implementation, that’s up for you to decide. I can throw more ideas at the table, but you’ll have to be a little more specific

1 Like

I think this is the solution! So for searching a item in the inventory I would make an extra function right? Also if you can quickly explain the logic for doing automatic stack like for example, how to make stacks depending on the amount that you are adding to the item, if so I would be very happy!

Im thinking it by just dividing the amount that is adding to the max amount for example :
120 / 64 = 1.875
then i will math.ceil the result = 2, so I will insert two items and then just going to add the amount and then resting the amount of the current stack to the adding amount so for the next stack is different.

This is what I did to make multiple stacks if the amount that you are adding is greater than the max amount :

local times = math.ceil(amount/itemD.MaxAmount)
for x = 1,times,1 do
	local currentAmount = amount
	local i = #inventory+1
	print(amount)
	table.insert(inventory,i,itemD)
	if amount >= itemD.MaxAmount then
	        print("lol "..itemD.MaxAmount)
	        inventory[i].Amount = itemD.MaxAmount
	elseif amount < itemD.MaxAmount then
		print("what")
		inventory[i].Amount = amount
	end
	amount-=inventory[i].Amount
end

But it sets all the items to the same last value, idk why, output :

 350
  lol 124
  226
  lol 124
  102
  what
  --------
  Id : wood_log
  Amount : 102
  Slot : 1
  -------- (x2)
  Id : wood_log
  Amount : 102
  Slot : 2
  -------- (x2)
  Id : wood_log
  Amount : 102
  Slot : 3
  --------

(sorry for late reply)
(extra late reply, DevForum went read-only and deleted most of my progress)

Good question! The logic may seem a little daunting at first, but try your best to gain an intuition for it. It’s really easy once you know what’s going on.

Disclaimer: This isn’t a script you can copy+paste in your game and expect it to work. This is a basic expression; a framework, of a true inventory system. The design of your game is inherently yours, and it should stay that way.

In this example, the inventory is an array of 27 items. We can use a STACKS dictionary to define stack sizes for different items.

local STACKS = {
    stone = 64,
    ender_pearl = 16,
    diamond_sword = 1
}


local Inventory = {}

Inventory.Items = {}

for i = 1, 27 do
    table.insert(Inventory.Items, { Count = 0 })
end

Now, I’ll define a helper function to merge items:

-- merges source onto dest, swap arguments for the opposite effect
function Inventory.Merge(source, dest)
    if not dest.Item or (source.Item == dest.Item) then
        -- either no item in dest, or both types are the same

        local result = source.Count + dest.Count
        local max_stack = STACKS[dest.Item]

        dest.Count = math.min(result, max_stack)
        source.Count = result - max_stack

        if source.Count < 1 then
            -- source depleted; destroy the item
            -- handle this your own way, i'll just unassign its type
            source.Item = nil
        end
        
    end
end

If we test this function, you can see items get properly stacked just like they do in untitled block game.

-- for example, source could be the item held at the cursor
local source = {
    Item = "stone",
    Count = 48
}

local dest = {
    Item = "stone",
    Count = 32
}

Inventory.Merge(source, dest)

print(source.Count) -- 16
print(dest.Count) -- 64

image image

Now, items don’t get swapped yet; they always go to the inventory. But, you can implement that with a few extra checks. I’ll make a separate function for that, as swapping logic may be unintentional at times, such as when picking items off the ground.

function Inventory:Swap(slot, item)
    local current = self.Items[slot] -- get the item for later
    self.Items[slot] = item -- replace the item

    return current -- return the old item. this goes to the cursor
end

Finally, we can step back a bit, and simulate a cursor-to-inventory interaction, such as left-clicking on items.

local function Inventory:LMB(slot, cursor)
    -- cursor being the item held at the cursor, if any

    local inv = self.Items[slot]

    if not (inv.Item and cursor.Item) or inv.Item ~= cursor.Item then
        -- if there's one or more items missing, just swap
        -- if items aren't the same, swap
        return self:Swap(slot, cursor)
    else
        -- merge from cursor
        self.Merge(cursor, inv)
    end
end

And that’s it! Whether your stack size is 100, 64 or 1, the same logic will still apply.

The actual networking required to make this work is for you to do. This is a formulaic expression, and will hopefully give you the intuition to create a real, and awesome, inventory system. If it’s wrong by chance, I apologize. I wrote this on a touchscreen.

I also apologize if this isn’t exactly what you needed right from the gate. I don’t feel like writing an entire functioning system, networking and all, on my phone, while mentally debugging as best as I can. Inventory systems require at least some programming knowledge as a prerequisite, so I’m assuming you have that.

6 Likes