Not sure how to add items to inventory from tutorial

Godot Version

Godot 4.2.1 Stable

Question

Hi, I know this is a bit of a vague question, which is frowned upon generally, but I recently watched Alphredo M’s Diablos/Dredge like grid inventory system (not sure if I’m allowed to link it), and the tutorial unfortunately doesn’t go into how to add items to the inventory dynamically (such as picking an item off the ground). I’m wondering if anyone can help point me in the right direction of where I might be able to look in order to try and figure this out, as I’ve been trying for days to no avail. I could post what I’ve tried, but I feel like it doesn’t make sense without the context of the entirety of the programming presented in the tutorial. Any help would be greatly appreciated

Linking the tutorial is fine. If it’s a long video, do let us know what part of it is most relevant.

Posting relevant code excerpts along with an explanation of your attempts would also be helpful.

Ok, watched the video on 2x speed to get a sense of what you’re talking about.

So, at 34:49 he finishes defining on_buton_spawn_pressed() [sic] which currently creates a new item and sets up the interactions in the inventory.

So it looks like what you want is a method on Inventory that takes in the ID of the item you want to load rather than picking a random one, but is otherwise the same as what the spawn button does. Then when you’re in the world and you want to pickup an object, the player script would figure out the ID of the thing you just picked up, and then call that method on Inventory to get it loaded up in the picker.

Depending on your design, you may want the same Inventory method to also pop up the inventory if it’s hidden, or maybe the player script pops up the inventory first, and then loads the item into it. Both work, it’s up to your taste.

You might also want to close the inventory automatically some delay after you’ve placed your item, or you may not. Depends on your taste and game-design, again.

The last thing you’ll probably want is some kind of hook that goes back from the Inventory script to the player script (a signal likely) that fires when an item has been placed, and another when the inventory has been closed without placing an item. This way your player script can either remove the item from the overworld if it’s in your inventory now, or it can leave it alone if you decided not to make it fit.

Good luck!

Here’s the tutorial, it’s a pretty long one, but it’s also relevant to the issue, so I’m not sure where I’d timestamp: https://www.youtube.com/watch?v=WN40PrPRDXs

As for attempts, here’s the code for my closest attempt:

    pickup_item = item_scene.instantiate()
    held_ID = pickup_item.item_ID
    held_icon = pickup_item.item_icon
    held_grid = pickup_item.item_grids
    item_held = pickup_item
    _pickup_item()

func _pickup_item():
    for i in grid_array.size():
        if check_slot_availability_pickup(grid_container.get_child(i)) == true:
            place_item_pickup(grid_container.get_child(i))
            return
        else:
            continue

func check_slot_availability_pickup(a_Slot):
    for grid in pickup_item.item_grids:
        var grid_to_check = a_Slot.slot_ID + grid[0] + grid[1] * col_count
        var line_switch_check = a_Slot.slot_ID % col_count + grid[0]
        if line_switch_check < 0 or line_switch_check >= col_count:
            return false
        if grid_to_check < 0 or grid_to_check >= grid_array.size():
            return false
        if grid_array[grid_to_check].state == grid_array[grid_to_check].States.TAKEN:
            return false
        else:
            return true

func place_item_pickup(a_Slot):
    var calculated_grid_id = a_Slot.slot_ID + icon_anchor.x * col_count + icon_anchor.y
    grid_container.add_child(pickup_item)
    pickup_item._snap_to(grid_array[calculated_grid_id].global_position)
    #pickup_item.global_position = grid_array[calculated_grid_id].global_position
    print(calculated_grid_id)
    pickup_item.grid_anchor = a_Slot
    for grid in pickup_item.item_grids:
        var grid_to_check = a_Slot.slot_ID + grid[0] + grid[1] * col_count
        grid_array[grid_to_check].state = grid_array[grid_to_check].States.TAKEN 
        grid_array[grid_to_check].item_stored = item_held
    item_held = null
    clear_grid()

This is the outcome when I try that:

Also if I try to spawn an item using the original spawn button (which you then have to place into the grid manually after spawning), then try to “pickup” option, I get the error "Invalid get index ‘180004’ ('on base: ‘Array’). on the line “pickup_item._snap_to(grid_array[calculated_grid_id].global_position)”

Hopefully this helps. Thanks!

Thanks for your reply! I’ve made a reply to a post that goes into depth more on my attempts, so hopefully that clears up what I’m trying to do. The main issue I’m having is trying to add an item to the next available spot, I feel like I’m almost there, but there’s just a few blind spots in my code that I hope others can help me find. Thanks for your help!

Oh, okay, yeah. I think I see the problem! You’re checking for the next available slot. That’s not enough, though, because your items take multiple slots.

What you need is to check, for each slot, if the entire item can fit there. So similar to what he was doing with held_item on hover, you need to know if, from this available position, all of the other positions are also free. (All the other positions your particular item would need, that is)

Is that not being done in check_slot_availability_pickup(a_Slot), as the for loop at the start is checking item_grids, which is an array that contains all of the co-ordinates that the items takes up? So the for loop should be checking each spot before returning true?

Nope, because your else case is inside the for loop. So as soon as it finds one available slot, it returns true from the whole method.

I think you want to leave the else case off and return true at the end, after the loop

So is this how it should look?

That looks more right to me. Does it work?

Kind of, the first time I tried it, I got the error "Invalid get index ‘180004’ ('on base: ‘Array’). on the line “pickup_item._snap_to(grid_array[calculated_grid_id].global_position)”. Then the next time I tried it, it worked, but it was offset to the right by 1 slot:

Progress!

I have a process question: do you know how to use the interactive debugger? If I were debugging this issue, these are the steps I’d have taken:

  1. Start the program
  2. Spawn item manually, place it in the first slot.
  3. Go back to the editor and put a breakpoint on the first line of check_slot_availability_pickup
  4. Pickup an item

This would allow me to step through the check_slot_availability_pickup method line-by-line and see if it’s doing what I think it’s doing. In this case, it would be pretty clear that it checked the first position, and then returned.

If you’re not sure how to set a breakpoint, it’s the little red circle off to the left of the code:

If you click over there, then from that point on, in the running game, it will pause when you get there and allow you to look around. You can then use the “Stack Trace” panel at the bottom to see the values of variables:

And then the buttons at the top right allow you to “Step Into” (which continues running the code by going into whatever method is being called on the current line), or “Step Over” which just runs the next line in this function without going into what sub-routines were called to get there. And then finally on the far-right “Continue” which just runs the code until the next breakpoint.

It’s a really useful tool for when things are going screwy. You can probably use it right now to figure out why things are offset by 1, and also probably to figure out where 180004 is coming from.

I’ll try this soon, and get back to you, thanks for all the help!

Good luck, and let us know if you get stuck.

Happy bug hunting!

Here’s the outcomes from using the Stack Trace panel, honestly not really sure what it means:


debug2

I think the issue might have to do with the icon_anchor variable, but I’m honestly not sure how to fix that, the use of the icon_anchor variable wasn’t explained super well in the tutorial, so I’m not even entirely sure what it does.

I added the lines:

		if grid[1] < icon_anchor.x: icon_anchor.x = grid[1]
		if grid[0] < icon_anchor.y: icon_anchor.y = grid[0]

to the end of the place_item_pickup function, which made more progress, however the first item that gets added is still offset by one spot. Not sure where to go from here, but it’s progress. Also, “picking up” an item after placing one in the old way will still return the "Invalid get index ‘180004’ ('on base: ‘Array’). error

Right, yeah!

So, what we can see here are all the variables in their current values.
So, we can see that calculated_grid_id is 180003. And from the code we can see that:

var calculated_grid_id = a_Slot.slot_ID + icon_anchor.x * col_count + icon_anchor.y

And in the members section we can see that icon_anchor.x is 10000 and icon_anchor.y is 1000000. And col_count is 8.

So icon_anchor.x * col_count + icon_anchor.y => 10000 * 8 + 1000000 => 80000 + 1000000 => 1800000. So far so “good”. So I’m going to guess a_Slot.slot_ID is 3. To confirm, you could click on the Object ID: blahblah entry next to a_Slot, which should pop open the object in the inspector (right panel), and maybe allow you to see the inner value of slot_ID yourself.

So yeah! Good progress! Now I see you made another post, which I’ll look at more closely and reply separately!

Ah, okay. Just rewatched the video.

So! What the icon_anchor is for is finding the top-left slot of the item. That’s why it’s starting at a very big number, bigger than any grid you’ll make. And then while it’s iterating through item_grids it’s saying if this item is “topper” or “lefter” than my current “toppest” and “leftest” slot, then use this position instead. And it’s doing that in left and top separately, to account for things that are shaped like “╝”

And the reason it’s doing all of that, is so that when you actually pick where this item goes, it can snap its top-left point to the position of the slot in the top-left position, so it’ll line up with the grid.

It strikes me as a bit weird that it’s using [1] for x and [0] for y…? You have that right, compared to his code… but it’s possible that’s a bug. I wasn’t actually paying that close attention to his convention, but normally x is first. Unsure if that’s the problem or not.

But! With that knowledge in mind, and the debugger, maybe see if you can figure out why it’s picking the wrong one for that first item. Intuitively, we would expect that the code will pick 0, 0, which would equate to calculated_grid_id of 0. If it’s not, you may have to do some debugging to figure out why…

Also, since where the icon is placed is different code from where the icon is stored in the actual inventory logic, it’s probably also a good idea to use the debugger to figure out if the problem is that it’s being put in the wrong spot in the data, or if it’s just being drawn in the wrong spot. Both are possible, but they’re different bugs. Is the problem in _snap_to or the data that’s being passed into it, or is it actually just picking the wrong slot as available? You may have to find out…

Honestly, this guy has made some choices I don’t fully agree with. It’s not bad necessarily, but it seems a little error-prone. And this icon_anchor variable seems like one of them.

If we look, it’s an member variable, and it’s set to a big number at the top of _on_slot_mouse_entered function, then it’s set to the real number inside set_grids, and then used inside place_item. I’m not sure what I’d do, but this all feels a bit indirect to me, and you’re getting bitten by that. I see why he’s doing it, he doesn’t want to iterate over the items again, but it’s leading to this issue.

But now that I’ve explained the purpose behind the value, you need to come up with your own way to solve the problem. Because right now the logic of finding the top-left is in the code that colours the slots while hovering, but your new logic doesn’t do any hovering. So you’ll need to generate the same information (which slot is the top-left of where I want to put this item) while doing your own logic of automatically finding a place.

Yeah it does seem pretty complex, I’m not sure how I would go about making my own way of finding the top left of an object. I’ve only just recently moved over to Godot after using Game Maker for many years. I’m wondering if perhaps it might be worth following a different tutorial on this sort of inventory system, as there’s functionality in this tutorial that I don’t think I’d implement, and would needlessly complicate the system, such as having irregular shaped items, such as this “L” piece.