Save/ Load bug with inventory items

Godot Version

3.5

Question

Hi all. Sorry to trouble you with another bug I’ve encountered, but its got me scratching my head.

I’m trying to fix a bug that happens when I load a previously saved game. the bug (seems to be) is that when I have a ‘key’ in my inventory (key being an inventory item that can open a door) and I try to open a door with keys used from a previously saved game, my key list will always return the keys… plus a null object in teh beginning of the array.

I’ll post the relevant code first, but it could be a bug from somewhere else in it, too. this is the code I’ve been looking at, however.

EDIT: okay obvious error here where I have the if check i - 1 but still append i to the slot list. however, having fixed that part, with jsut regular i in both places, and tinkered around with trying i - 1 in boht places, I still think its not an off by one error

func get_items_in_inventory():
	var items = []
	for i in slot_list.size():
		if slot_list[i - 1].item != null: #bullshitting. if first item is ALWAYS null
			#maybe i + 1 will make it not null
			#okay... i + 1 just made TWO first items null???
			#i - 1 only does first item as null. but there still is that unremoveable first null item
			items.append(slot_list[i].item)
			#wham. first item AGAIN is null???
	return items #items IS returnning a null value. it is the FIRST value (in this specific run trhough)

I thought it was an off by one error at first (if you can see the uh. interesting comments I left behind) but I’m beginning to suspect it might be something else.

here’s the whole inventory script I’ve got:

extends Node2D

#get our grid container!
onready var inventory_slots = $GridContainer
var inventory_array = []
var slot_list
var slots_number = 0

# Called when the node enters the scene tree for the first time.
func _ready():
	InventoryManager.set_up_inventory_and_items()
	#print("inventory ready")
	get_slot_list()

func get_slot_list():
	slot_list = inventory_slots.get_children()
#	for i in slot_list.size():
#		print ("invenotry slot is named ")
#		print (slot_list[i].name)
	slots_number = slot_list.size()

func check_if_space_left(_item):
	for i in slot_list.size():
		#if something is in there!
		if slot_list[i].item == null:
			#print ("we have space left")
			return slot_list[i] #return what slot is free

func try_add_item_to_free_slot(item):
	var slot = check_if_space_left(item)
	#print ("we are adding an item to the slot from inventory script")
	if check_if_space_left(item) != null:
		slot.add_item(item)
		return true
	else:
		print ("could not add item")
		return false

func try_copy_item_to_free_slot(item):
	var slot = check_if_space_left(item)
	#print ("we are copying an item to the slot from teh inventory script")
	if check_if_space_left(item) != null:
		#so we must get our item AND see what its correpsonding gui item is!
		#....why do we have an if statement htat just leads to a pass?
		pass

func remove_item_from_slot(slot):
	slot.remove_item()

func get_items_in_inventory():
	var items = []
#	keys.remove(0)
	for i in slot_list.size():
		#ooooh... its no longer partnered to the slot list!!!!
		#we had to unpartner it to add the item above!!!!!
		if slot_list[i - 1].item != null: #bullshitting. if first item is ALWAYS null
			#maybe i + 1 will make it not null
			#okay... i + 1 just made TWO first items null???
			#i - 1 only does first item as null. but there still is that unremoveable first null item
			items.append(slot_list[i].item)
			#wham. first item AGAIN is null???
	return items #items IS returnning a null value. it is the FIRST value (in this specific run trhough)

#it looks like this assumes that EVERY inventory item is a key
#mabye ill have to add a seperate, non key inventory later!!!!
#okay. atm we are having a previously freed instance problem :X
func get_key_ids():
	var keys = get_items_in_inventory()
	var key_ids = []
	for i in keys.size():
		key_ids.append(keys[i])
	return key_ids #now it is returning ENCODED objects, NOT key ids >:(

func save():
	#var items = get_items_in_inventory()
	var save_dict = {
		"filename" : get_filename(),
		"parent" : get_parent().get_path(),
		"pos_x" : position.x, #Vector 2 is not supported by JSON
		"pos_y" : position.y,
		"path" : get_path(),
		"slots_number" : slots_number
	}
	return save_dict

I also have the save/ load script, as well as an inventory manager and slot script. I"m not sure which of those will be most helpful, however. but if you need to see one, I’d be glad ot post it.

Thanks for anyone who can help. Sorry about my comments and strange logic. I’m a bit new to this.

If you want to improve your saving process to be able to store everything in a simple dict I reccommend:

func save_info():
	var content = [info]
	var f = File.new()
	f.open(path, File.WRITE)
	f.store_var(content)
	f.close()
func load_info():
	var content
	var f = File.new()
	if f.file_exists(path):
		var error = f.open(path, File.READ)
		if error == OK:
			content = f.get_var()
			info = content[0]
			f.close()

info is an ordinary dictionary, the array content makes it possible to change the storage in future updates without breaking anything
If the file doesnt exist, it just leaves the unedited info from the script unchanged

The rest of your code also seems rather convoluted. Id suggest boiling it down to this:

info (from above) contains an array inventory of size inventory.
inventory stores the object ids and empty spaces as -1, because null as unassigned item can get confusing with error messages and other null info, so -1 immediately lets you know whats up

now

get_items_in_inventory() = inventory
check_if_space_left() = inventory.find(-1)
 > return the first free space index, otherwise -1, if you want the last free slot use .find_last(-1)
remove_item_from_slot(slot) = inventory[slot] = -1

func try_add_item_to_free_slot(item):
 var location = inventory.find(-1)
 if location == -1:
  print("could not add item")
  return false
 inventory[location] = item
return true

func try_copy_item_to_free_slot(item):
 > I dont know what this should do, but if it takes an item from 
   the inventory and moves it to another free space then:
 var index = inventory.find(item)
 try_add_item_to_free_slot(item)
 inventory[index] = -1

I think get_key_ids() is simply missing a way to identify what a key is and what not, because at the moment get_key_ids() is just returning get_items_in_inventory()

I hope this helps a bit :slight_smile:

1 Like

Thanks for the reply. I’ll look into making a lot of changes to it today or tomorrow :>

1 Like

Here you are for-looping using an index. This is unnecessary.
Just use GDScript for-each syntax:

func get_items_in_inventory():
	var items = []
	for slot in slot_list:
		items.append(slot.item)
	return items

As to the error, is slot_list the correct size after this:

func get_slot_list():
	slot_list = inventory_slots.get_children()

Since keys is getting an item list from get_items_in_inventory() it will end up with only the keys in inventory_slots.
Print out slot_list.item to see if it already contains the null entry.

func get_slot_list():
	slot_list = inventory_slots.get_children()
    for slot in slot_list: 
        print(slot.item)

If it does contain the null then the rest of the code is not at fault.

func get_key_ids():
	var keys = get_items_in_inventory()
	var key_ids = []
	for i in keys.size():
		key_ids.append(keys[i])
	return key_ids #now it is re

Here again we can do away with the index:

func get_key_ids():
	var keys = get_items_in_inventory()
	var key_ids = []
	for key in keys:
		key_ids.append(key)
	return key_ids

Hey, thank you for taking the time to reply. I’ll be looking into implementing these changes to get it working better soon!

Okay ty everyone who has helped so far. I will keep looking into cleaning the code.

I fixed (okay got help from my brother to fix XD) an issue where the slots of hte inventory were saved in a weird order, not sure if that will help things or not.

I haven’t been able to replicate the error on teh initial save but i will keep testing