+1 vote

So i'm currently trying to learn how to make games using godot. Right now i'm trying to create my own inventory system for my game. I have a function that adds an item to the inventory.
I want to add an item to the inventory every time player collides with a certain object (for example, if the player collides with a carrot, i want this carrot to be added to his inventory).

Here is my script for the item:

extends Node2D
export var my_res: Resource
onready var slots_script = get_node("/root/Slots")

func _on_Area2D_area_entered(area):
   slots_script.AddItemToInventory(my_res)

And here is the function that this item is triggering:

func AddItemToInventory(item):
print("adding")
for i in range(0, slots.size()):
    if slots[i].isFull == false: 
        slots[i].isFull = true
        slots[i].addItem(item)
        break

For some reason the only part of this function that is triggering right now is the first line that is printing a message in the console.
I tested this function before so there should be no problem with it. I think there is something wrong with my item script but i can't figure out what. I would really like to get some help with it
P.S I'm just a beginner so please don't judge my coding skills too much

Godot version v3.2.3.stable.official
in Engine by (15 points)

Hi! Is the identation right? I mean that you should ident after AddItemToInventory, but it's probably good because godot would complain if it isn't. What's slots.size(), isn't it 0? That would justify the loop not working. Another thing would be if all the slots are full.

The slot size is okay, it is automatically set by the count of slots it the inventory. This function works completely how it should. I tested it by writing a code that would add a random item to the inventory after i press a certain button and everythink worked perfectly. There is certainly something wrong with the item, because i tried to write the same code inside the item script and it also didn't work (this code below)

func _process(delta):
if Input.is_action_just_pressed("add_item"):
    slots_script.AddItemToInventory(my_res)

Hmm then the next thing that comes into my mind is that maybe something is wrong with my_res ? And maybe I'd put a print under slots[i].addItem(item) to see that it tries to add the item just to make sure
is something different about the item (my_res) when it works compared to when it doesn't? how do you set it? (I see you export it)

Basically i have a resource based item system in my project. This way i can set different parameters for different items separately inside the editor (like the name of the object, the texture and etc). So i set my_res variable from the editor (i just drag and drop the right resource to a certain item).
What i also noticed about my problem is that the only way the AddItemToInventory() function works properly is when i call it from the script that has this function inside of it. I tried testing this input thing that i wrote about in my previous comment from other nodes on the scene and it didn't work.

func AddItemToInventory(item):
print("...")
for i in range(0, slots.size()):
    if slots[i].isFull == false: 
        slots[i].isFull = true
        slots[i].addItem(item)
        break

The weirdest thing about this is that the only thing that triggers properly is the print command (these three dots). The message always appears in the console, indicating that other scripts can actually access this function. I can't express the amount of confusion i have right now

hmm thats quite advanced! so the item is the exact same resource when it's working and when it doesn't? Also, is the "..." there if you move the print under the if statement? Or inside the addItem function?

or can you see if isFull becomes true?

Also, is the "..." there if you move the print under the if statement?

No, it doesn't

interesting, and between the for and the if statement?
a) if it doesn't print, try printing slots.size() before the loop
b) if it prints, try printing slots[i].isFull
and try to see if they are correct
that would be what I'd do probably

No, doesn't work

hmm and are you very sure that slots.size() is not 0 then (try printing slots.size() before the loop maybe)?
I have no idea what's happening if its not 0, that's my last guess :/

after that I'd be thinking about trying it with call_deferred (slots_script.call_deferred("AddItemToInventory", my_res) if I didn't mess it up) but I don't think that would help

am i referring to the slots script correctly? I am using get.node() here. What are the other ways of doing that?

I was thinking about that a bit because this onready var slots_script = get_node("/root/Slots") looks very strange, but if it triggers the print, there's no reason that it shouldn't trigger the rest.
Do you have the Slots as an autoload singleton? ( https://docs.godotengine.org/en/stable/getting_started/step_by_step/singletons_autoload.html )
or is it the root of the scene?
because the root of the scene or how it's called, e.g. a Node2D that you see in the editor, its path is /root/Node2D, where root is of type viewport, and Node2D's children are the nodes you put there
and if you autoload something (in project settings), it will have the path /root/GlobalScript (or how you called it) but you don't need to use that path, because its already available everywhere with the name you provided (e.g. GlobalScript)
but considering the print statement is already called, its very strange

Yes, it is an autoload singleton. I tried referring to the slots script both ways, but none of them worked. I only can run this function properly from the script where the function is located, that's why i thought that maybe im referring to it incorrectly

I tried different ways of referring to the slots script and got an error "can't call non-static function 'AddItemToInventory()' in script". Can this be the problem?

It's definitely something, though I don't really understand that part. One thing that you could try figuring out is if the script you're referring to is the same script where the slots Array is defined and has the slot objects, this static / non-static thing has something to do with the script/instances of the script.

This is the perfect use for the debugger. Set a breakpoint on the print and step through. My hunch is slots.size() is 0 and therefore no loop is run

i'm sure beaverusiv is correct, slots.size() is 0.
Can you show us the code that initializes your inventory, especially slots.


btw, if you want a forloop from 0 to x, you can just write

for i in x:

PS:
You can add print(slots.size()) at the start of your method, then you know for sure ;)
(or just set a breakpoint)


PPS:
I think you can also write:

func AddItemToInventory(item) -> bool:
    assert(slots.size(), "slots is empty :(")
    for slot in slots:
        if not slot.isFull: 
            slot.isFull = true
            slot.addItem(item)
            return true
    return false

looks a bit nicer and i better to understand. tells you also if slots is empty and if you have sucessfully stored the item.

The slot size is okay. I tried printing it from different parts of the code and it worked correctly. I think that my problem has something to do with instancing. I think i might set my scenes incorrectly and that's why this problem occures. Right now i'm working on this weird problem and i think im close to solving it

A side note, too, instead of referencing a node and calling a function on it, this is where you should be emitting an event that your inventory should be listening to

please be so kind and make some example-code. I'm interested, too

Emit a signal when it gets added to inventory:

extends Node2D
export var my_res: Resource
signal add_to_inventory(res)

func _on_Area2D_area_entered(area):
   emit_signal("add_to_inventory", res)

And in your Slots node programmatically or setup in the GUI:

func _ready():
   connect("add_to_inventory", self, "AddToInventory")

Mh, i'm not sure if i would do that with signals... i mean of course it highly depends on the inventory-system, but if the inventory can be full, i have to check first if the inventory has space so i can remove the item from the normal gameworld. So i can do something like this:

if Inventory.add_item(self): # true -> successfully added
    queue_free() # kill it (or maybe reparent the node to the inventory)
else: # false -> adding failed
    pass # stuff if the inventory is full (or whatever went wrong)

With signals that would be kind of a pingpong-game...

btw, i'm using signals way to rarely. maybe i'm getting something wrong here ;)
I'm always open to learn something new.

We're totally off-topic, but yeah I don't have many signals - I use a few autoloaded singletons and inventory should definitely be one. For the signals I do use I generally use an event bus for it: https://www.gdquest.com/docs/guidelines/best-practices/godot-gdscript/event-bus/

@misuki: I would also see how other people have implemented such systems, I know a quick google or github search will return a few results

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.