Slot based inventory item duplication

Godot Version

Godot4.3

Question

I’ve recently added draggable items into my slot-based inventory, including equipable item slots. The problem I’m encountering is that dragging works fine when moving items within the inventory grid (GridContainer), or from the equippable slots (VBoxContainer) to the inventory. However, when dragging an item from the inventory to an equipable slot, it works fine until I drag the item to and from the same slot multiple times. At that point, if the item it’s attempting to swap with is null, the item gets duplicated, placed in the slot that the dragged item just vacated. It works fine as long as the item being swapped with isn’t null.

here is my current item swapping logic, i can provide more of this script if needed.

func swap_inventory_items(slot1: Control, slot2: Control) -> bool:
	var parent1 = slot1.get_parent()
	var parent2 = slot2.get_parent()

	var index1 := get_slot_index(slot1)
	var index2 := get_slot_index(slot2)

	# Inventory ↔ Inventory (works)
	if parent1 == grid_container and parent2 == grid_container:
		if index1 == -1 or index2 == -1:
			return false
		var temp = GlobalData.inventory[index1]
		GlobalData.inventory[index1] = GlobalData.inventory[index2]
		GlobalData.inventory[index2] = temp
		GlobalData.update_inventory()
		return true

	# Inventory → Equipment (broken)
	elif parent1 == grid_container and parent2 == v_container:
		if index1 == -1:
			return false

		var inv_item = slot1.item
		var equipped_item = slot2.item
		if inv_item != null:
			if equipped_item == null:
				slot2.set_empty()
			else:
				slot2.set_item(inv_item.duplicate(true))
		else:
			slot2.set_empty()
			
		GlobalData.inventory[index1] = equipped_item if equipped_item != null else null
		GlobalData.update_inventory()
		return true

	# Equipment → Inventory (works)
	elif parent1 == v_container and parent2 == grid_container:
		if index2 == -1:
			return false
		var equipped_item = slot1.item
		var inv_item = slot2.item
		if equipped_item != null:
			GlobalData.inventory[index2] = equipped_item.duplicate(true)
		else:
			GlobalData.inventory[index2] = null
		if inv_item != null:
			slot1.set_item(inv_item.duplicate(true))
		else:
			slot1.set_empty()

		GlobalData.update_inventory()
		return true

	return false

I’m curious as to the need to duplicate the items.
Why not just set the slot items to the item itself?
slot1.set_item(inv_item)

Also, does set_empty() free those duplicate items?

i tried to do that but the issue persisted, i agree it would be a more elegant solution and in the process of trying to fix it, the function morphed into what it is now. i don’t believe it does, but the issue existed before i tried that.

My step 1 would be to see what exactly is happening the first time you drag a node that involves the duplicate() function.
When you drag your item from inventory to equipment, you create a duplicate to place into equipment.
I don’t see anywhere that you remove the original. If that is the case then there will be two full instances of the item in memory (whether or not you see them visually).
You can click on the Remote tab of godot inspector while you are running and look at all the existing nodes (I am guessing that the item has some kind of node structure).

it is duplicating the item and not deleting it, how do i fix this? do i just remove the .duplicate?

Rewrite it to not duplicate and then we can start to fix the issues that arise from that point. (post the updated code)
Depending on your scene structure you may need to reparent the item but I can’t tell from the info available.
There are also some excellent inventory tutorials I recommend.
Have a look at this one. It doesn’t give you Inventory to Equipment but it is a similar situation to Ground to Inventory.

i just rewrote the code so it shouldn’t duplicate the item anymore, but the issue is still occuring. would it be helpful if i provided a photo of my scene structure and/or the rest of this script?

one thing i just realized while testing, is that the item’s icon is simply hiding/being set to null so it appears to have disappeared, but when you hovor over it, the data still appears which means the slot isn’t actually being cleared.

here are my set_item and set_empty functions if that would be helpful:

func set_empty():
	icon.texture = texture if texture else null
	quantity.text = ''
	
func set_item(new_item):
	if new_item == null:
		set_empty()
		GlobalData.player_node.update_equipped()
		return

	item = new_item
	icon.texture = item.get("texture", null)
	quantity.text = str(item.get("quantity", ""))
	item_name.text = str(item.get("name", ""))
	item_type.text = "type: " + str(item.get("type", ""))

	if str(item.get("worth", "")) != "":
		item_effect.text = "worth: " + str(item["worth"])
	else:
		item_effect.text = ""

	GlobalData.player_node.update_equipped()

by adding item = null into my set_empty function, i fixed the issue. this is because in my swap items func, i swap them by accessing the item data of the target slot which appeared to be null even when it wasn’t

1 Like