Hi people !

I'm working on a game with a locked/unlocked levels grid. I already try something but it's doesn't run like I expected.

What I need to do : - Save in a JSON file dictionnaries for each levels with levels name, levels path,levels locked state. - Be able to use this JSON to update the levels grid with the good locked state for each level

What I already try :

## LEVEL MANAGER : level dictionnaries var lvl1 = { name = "lvl 1", path = "res://levels/level1.xml", locked = false } var lvl2 = { name = "lvl 2", path = "res://levels/level2.xml", locked = true } func grid() : var grid = [lvl1,lvl2] return grid `## LEVEL MANAGER : save function func save_game(): var savegame= File.new() savegame.open("res://data/savegame.bin", File.WRITE)

Save levels grid data

var grid = grid() for level in grid : var data = level savegame.store_line(data.to_json()) savegame.close()## LEVEL MANAGER : load function func load_game() : var savegame = File.new() var newGrid = [] if !savegame.file_exists("res://data/savegame.bin"): return #Error! We don't have a save to load var currentline = {} # dict.parse_json() requires a declared dict. savegame.open("res://data/savegame.bin", File.READ) while (!savegame.eof_reached()): currentline.parse_json(savegame.get_line()) newGrid.append(currentline) savegame.close() return newGrid`

PROBLEMS 1) load_game() return an array with three line filled with the level 2 dict, instead of a line lvl1 and a line lvl2, why ? 2) I don't know how to change a dictionnary values 3)How can I wrote a unlocked_next_level() function without write the next level path ? example : I'm on the first level. I click on the unlock level 2 button which call unlocked_next_level(). the function know that i'm on the first level and that the level 2 is the second thanks to JSON file.

What I'm not sure how to do 4) Is it a good idea to make a dictionnary for each level ?

Okay two first problem are solved with the help of people in Discord server.

answer : 1) it because parse_json() doesn't create a new currentline each time so it point to the same variable. I must move var curentline = {} at the beggining of the while and newGrid.pop_back() before return it because I don't know why I have an empty case....

2) this is my function to init the grid :

func load_grid() : var data = LevelManager.load_game() # return the grid for c in range(data.size() - 1) : var btn = load("res://scenes/levelSelectButton.xml").instance() grid.add_child(btn) #add a button to my grid pannel on the screen for child in grid.get_children() : child.set_levelNumber(i) if i <= data.size() : # set property on button on the grid child.set_levelName(data[i-1].name) child.set_levelPath(data[i-1].path) child.set_locked(data[i-1].locked) i += 1

Questions with no answers :

  • How can I wrote a unlocked_next_level() function without write the next level path ? example : I'm on the first level. I click on the unlock level 2 button which call unlocked_next_level(). the function know that i'm on the first level and that the level 2 is the second thanks to JSON file.

  • Is it a good idea to make a dictionnary for each level ?

I'd put all the data in one structure/dictionary which you write in one file. So you're not loading line by line but just loading the entire structure & contents at once.

So here's an example of such a structure:

var status_init= { 
       	        	"version": 1, 
       	        	"levels": [
				{
					name: "Level 1",
					path: "res://levels/level1.xml",
					locked: false,
					score: 1000
				},
				{
					name: "Level 2",
					path: "res://levels/level2.xml",
					locked: true,
					score: 0
				}
			],
			"options": {audioVolume: 0.5}
		}

Here's an example for loading and saving such a structure (best put in a global singleton). Also it has two functions to get the last unlocked level and to unlock a new level. This code is untested in parts and therefore may contain errors:

const SAVE_GAME = "user://save_game.dat"

var status
var currentLevelIx = 0

func _ready():
    	load_status()

func load_status():
	var file = File.new()
	var data = ""
	if file.file_exists(SAVE_GAME):
		file.open(SAVE_GAME, file.READ)
		data = file.get_as_text()
		file.close()
		print("Status loaded from "+SAVE_GAME)
			
	if (data == "") or (data == null):
		status = status_init
		print("Status new? -> Init")
	else:
		status = {}
		status.parse_json(data)
		if (status!=null) and (status.has("version")):
			print("Status parsed ok")
			save_status(SAVE_BAK)
		else:
			print("Error parsing Status! Resetting")
			status = status_init

	currentLevelIx = get_last_unlocked_level()

#call save_status always when something has changed (score, unlock, etc.)
func save_status(file_name = SAVE_GAME):
	var file = File.new()
	file.open(file_name, file.WRITE)
	file.store_string(status.to_json())
	file.close()
	print("Status saved to: "+file_name)

#returns index of currently unlocked level
#returns -1 if no level is unlocked
func get_last_unlocked_level():
	var lul=-1 #default value, -1 understood here as "invalid"
	var lcnt=0
	for l in status["levels"]:
		if l.has("locked") and (l["locked"] == false):
			lul=lcnt
		lcnt+=1
	return lul
			
#returns index of newly unlocked level
#returns -1 if no more level remains to unlock
func unlock_next_level():
	var lul=-1 #default value, -1 understood here as "invalid"
	var lcnt=0
	for l in status["levels"]:
		if !l.has("locked") or (l["locked"] == true):
			l["locked"] = true
			lul=lcnt
			break
		lcnt+=1
	return lul

4 months later

Thank a lot for your answer ! Sorry I have forgottent to answer you

5 years later