228 lines
6.6 KiB
GDScript
228 lines
6.6 KiB
GDScript
class_name PopochiuSaveLoad
|
|
extends Resource
|
|
## Class that allows to save and load game data.
|
|
##
|
|
## Thanks GDQuest for this! ↴↴↴
|
|
## https://github.com/GDQuest/godot-demos-2022/tree/main/save-game
|
|
|
|
# TODO: This could be in PopochiuSettings for devs to change the path
|
|
const SAVE_GAME_PATH := "user://save_%d.json"
|
|
|
|
|
|
#region Public #####################################################################################
|
|
func count_saves() -> int:
|
|
var saves := 0
|
|
|
|
for i in range(1, 5):
|
|
if FileAccess.file_exists(SAVE_GAME_PATH % i):
|
|
saves += 1
|
|
|
|
return saves
|
|
|
|
|
|
func get_saves_descriptions() -> Dictionary:
|
|
var saves := {}
|
|
|
|
for i in range(1, 5):
|
|
if FileAccess.file_exists(SAVE_GAME_PATH % i):
|
|
var opened := FileAccess.open(SAVE_GAME_PATH % i, FileAccess.READ)
|
|
if not opened:
|
|
PopochiuUtils.print_error(
|
|
"Could not open the file %s. Error code: %s" % [
|
|
SAVE_GAME_PATH % i, opened.get_open_error()
|
|
]
|
|
)
|
|
return {}
|
|
|
|
var content := opened.get_as_text()
|
|
opened.close()
|
|
|
|
var test_json_conv = JSON.new()
|
|
test_json_conv.parse(content)
|
|
|
|
if test_json_conv.data == null: continue
|
|
|
|
var loaded_data: Dictionary = test_json_conv.data
|
|
|
|
saves[i] = loaded_data.description
|
|
|
|
return saves
|
|
|
|
|
|
func save_game(slot := 1, description := "") -> bool:
|
|
var opened := FileAccess.open(SAVE_GAME_PATH % slot, FileAccess.WRITE)
|
|
if not opened:
|
|
PopochiuUtils.print_error(
|
|
"Could not open the file %s. Error code: %s" % [
|
|
SAVE_GAME_PATH % slot, opened.get_open_error()
|
|
]
|
|
)
|
|
return false
|
|
|
|
var data := {
|
|
description = description,
|
|
player = {
|
|
room = PopochiuUtils.r.current.script_name,
|
|
inventory = PopochiuUtils.i.items,
|
|
},
|
|
rooms = {}, # Stores the state of each PopochiuRoomData
|
|
characters = {}, # Stores the state of each PopochiuCharacterData
|
|
inventory_items = {}, # Stores the state of each PopochiuInventoryItemData
|
|
dialogs = {}, # Stores the state of each PopochiuDialog
|
|
globals = {}, # Stores the state of Globals
|
|
}
|
|
|
|
if PopochiuUtils.c.player:
|
|
data.player.id = PopochiuUtils.c.player.script_name
|
|
data.player.position = {
|
|
x = PopochiuUtils.c.player.global_position.x,
|
|
y = PopochiuUtils.c.player.global_position.y
|
|
}
|
|
|
|
# Go over each Popochiu type to save its current state -----------------------------------------
|
|
for type in ["rooms", "characters", "inventory_items", "dialogs"]:
|
|
_store_data(type, data)
|
|
|
|
# Save PopochiuGlobals.gd (Globals) ------------------------------------------------------------
|
|
# prop = {class_name, hint, hint_string, name, type, usage}
|
|
for prop in PopochiuUtils.globals.get_script().get_script_property_list():
|
|
if not prop.type in PopochiuResources.VALID_TYPES: continue
|
|
|
|
# Check if the property is a script variable (8192)
|
|
# or a export variable (8199)
|
|
if prop.usage == PROPERTY_USAGE_SCRIPT_VARIABLE or prop.usage == (
|
|
PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
|
):
|
|
data.globals[prop.name] = PopochiuUtils.globals[prop.name]
|
|
|
|
if PopochiuUtils.globals.has_method("on_save"):
|
|
data.globals.custom_data = PopochiuUtils.globals.on_save()
|
|
|
|
if data.globals.is_empty(): data.erase("globals")
|
|
|
|
# Write the JSON -------------------------------------------------------------------------------
|
|
var json_string := JSON.stringify(data)
|
|
opened.store_string(json_string)
|
|
opened.close()
|
|
|
|
return true
|
|
|
|
|
|
func load_game(slot := 1) -> Dictionary:
|
|
var opened := FileAccess.open(SAVE_GAME_PATH % slot, FileAccess.READ)
|
|
if not opened:
|
|
PopochiuUtils.print_error(
|
|
"Could not open the file %s. Error code: %s" % [
|
|
SAVE_GAME_PATH % slot, opened.get_open_error()
|
|
]
|
|
)
|
|
return {}
|
|
|
|
var content := opened.get_as_text()
|
|
opened.close()
|
|
|
|
var test_json_conv = JSON.new()
|
|
test_json_conv.parse(content)
|
|
var loaded_data: Dictionary = test_json_conv.data
|
|
|
|
# Load inventory items
|
|
for item in loaded_data.player.inventory:
|
|
PopochiuUtils.i.get_item_instance(item).add(false)
|
|
|
|
# Load main object states
|
|
for type in ["rooms", "characters", "inventory_items", "dialogs"]:
|
|
if loaded_data.has(type):
|
|
_load_state(type, loaded_data)
|
|
|
|
# Load globals
|
|
if loaded_data.has("globals"):
|
|
for prop in loaded_data.globals:
|
|
if typeof(PopochiuUtils.globals.get(prop)) == TYPE_NIL: continue
|
|
|
|
PopochiuUtils.globals[prop] = loaded_data.globals[prop]
|
|
|
|
if loaded_data.globals.has("custom_data")\
|
|
and PopochiuUtils.globals.has_method("on_load"):
|
|
PopochiuUtils.globals.on_load(loaded_data.globals.custom_data)
|
|
|
|
return loaded_data
|
|
|
|
|
|
#endregion
|
|
|
|
#region Private ####################################################################################
|
|
func _store_data(type: String, save: Dictionary) -> void:
|
|
for path in PopochiuResources.get_section(type):
|
|
# load the ___State.tres file
|
|
var data := load(path)
|
|
|
|
save[type][data.script_name] = {}
|
|
|
|
PopochiuResources.store_properties(save[type][data.script_name], data)
|
|
|
|
match type:
|
|
"rooms":
|
|
data.save_children_states()
|
|
|
|
for category in PopochiuResources.ROOM_CHILDREN:
|
|
save[type][data.script_name][category] = data[category]
|
|
|
|
save[type][data.script_name]["characters"] = data.characters
|
|
"dialogs":
|
|
save[type][data.script_name].options = {}
|
|
|
|
for opt in (data as PopochiuDialog).options:
|
|
save[type][data.script_name].options[opt.id] = {}
|
|
PopochiuResources.store_properties(
|
|
save[type][data.script_name].options[opt.id],
|
|
opt,
|
|
["id", "always_on"]
|
|
)
|
|
|
|
if save[type][data.script_name].is_empty():
|
|
save[type].erase(data.script_name)
|
|
|
|
if save[type].is_empty():
|
|
save.erase(type)
|
|
|
|
|
|
func _load_state(type: String, loaded_game: Dictionary) -> void:
|
|
for id in loaded_game[type]:
|
|
var state := load(PopochiuResources.get_data_value(type, id, ""))
|
|
|
|
for p in loaded_game[type][id]:
|
|
if p == "custom_data": continue
|
|
if type == "dialogs" and p == "options": continue
|
|
|
|
state[p] = loaded_game[type][id][p]
|
|
|
|
match type:
|
|
"rooms":
|
|
PopochiuUtils.r.rooms_states[id] = state
|
|
"characters":
|
|
PopochiuUtils.c.characters_states[id] = state
|
|
"inventory_items":
|
|
PopochiuUtils.i.items_states[id] = state
|
|
"dialogs":
|
|
PopochiuUtils.d.trees[id] = state
|
|
_load_dialog_options(state, loaded_game[type][id].options)
|
|
|
|
if loaded_game[type][id].has("custom_data")\
|
|
and state.has_method("on_load"):
|
|
state.on_load(loaded_game[type][id].custom_data)
|
|
|
|
|
|
func _load_dialog_options(
|
|
dialog: PopochiuDialog, loaded_options: Dictionary
|
|
) -> void:
|
|
for opt in dialog.options:
|
|
if not loaded_options.has(opt.id): continue
|
|
|
|
for prop in opt.get_script().get_script_property_list():
|
|
if prop.name == "always_on": continue
|
|
|
|
if loaded_options[opt.id].has(prop.name):
|
|
opt[prop.name] = loaded_options[opt.id][prop.name]
|
|
|
|
|
|
#endregion
|