## Performs the actual Saving[br][br] ## ## Below is a basic definition of various components and resources for reference:[br] ## * SaveLevelDataComponent - The root save component that performs saving and loading of the actual data from disk ## * SaveDataComponent - Attached to a node that has properties that need to be saved ## * Node Type Resources - Each type of node that requires saving, such as a TileMapLayer, needs a resource created extends Node signal game_saved signal game_loaded signal refresh_saves_list signal create_auto_save_file signal create_save_file(filename: String) signal delete_save_file(filename: String) signal load_save_file(filename: String) signal quick_save signal quick_load signal open_save_list_ui signal close_save_list_ui var _game_data_resource: SaveGameDataResource var _save_level_data_component: SaveLevelDataComponent ## Contains the save paths and filenames func _ready() -> void: quick_load.connect(quick_load_game) quick_save.connect(quick_load_game) create_save_file.connect(_save_game_as_resource) load_save_file.connect(_load_game_resource) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("quick_save"): quick_save_game() if event.is_action_pressed("quick_load"): quick_load_game() func list_saves() -> Array[SaveFileDetailsResource]: var save_files: Array[SaveFileDetailsResource] = [] if not _load_save_level_data_component(): return save_files var save_game_data_path: String = _save_level_data_component.settings.save_game_data_path if !DirAccess.dir_exists_absolute(save_game_data_path): return save_files for filename: String in ResourceLoader.list_directory(save_game_data_path): if !filename.begins_with("save_"): continue if !filename.ends_with(".tres"): continue # Screenshots, etc var _save_path: String = save_game_data_path + filename var _save_icon: String = filename.replace(".tres", ".png") var _save_resource: SaveFileDetailsResource = SaveFileDetailsResource.new() # TODO: Reconsider loading the save to get the name # This could be a large save making opening each one slow # Should work out a better method for getting the save name (filename != save name) var _save_file: SaveGameDataResource = ResourceLoader.load(_save_path) _save_resource.save_name = _save_file.save_name _save_resource.filename = filename _save_resource.date_created = Time.get_datetime_string_from_unix_time(FileAccess.get_modified_time(_save_path)) ## In 4.5, this can probably be replaced with FileAccess.get_size(_save_path) ## This should reduce the need for opening the file here var _loaded_file: FileAccess = FileAccess.open(_save_path, FileAccess.READ) _save_resource.filesize = _loaded_file.get_length() if FileAccess.file_exists(save_game_data_path + _save_icon): _save_resource.save_icon = _save_icon save_files.append(_save_resource) return save_files func quick_save_game() -> void: if not _load_save_level_data_component(): return _save_game_as_resource(_save_level_data_component.settings.quicksave_file_name) game_saved.emit() func quick_load_game() -> void: if not _load_save_level_data_component(): return # TODO: Don't reset world if quicksave not found EntityManager.reset_world.emit() _load_game_resource(_save_level_data_component.settings.quicksave_file_name) game_loaded.emit() ## Find the SaveLevelDataComponent within the level which stores the save paths and filenames func _load_save_level_data_component() -> bool: _save_level_data_component = get_tree().get_first_node_in_group("save_level_data_component") if _save_level_data_component == null: push_error("Could not find SaveLevelDataComponent node in level") return false return true func _load_game_resource(resource_filename: String) -> void: if not _load_save_level_data_component(): return var save_game_file_path: String = _save_level_data_component.settings.save_game_data_path + resource_filename if !FileAccess.file_exists(save_game_file_path): printerr("Failed to load save. File does not exist: ", save_game_file_path) return _game_data_resource = ResourceLoader.load(save_game_file_path) if _game_data_resource == null: printerr("Failed to load save. Unknown format? ", save_game_file_path) return var root_node: Window = get_tree().root for resource: Resource in _game_data_resource.save_data_nodes: if resource is Node3DDataResource: (resource as Node3DDataResource)._load_data(root_node) func _save_game_as_resource(resource_filename: String) -> void: if not _load_save_level_data_component(): return if !DirAccess.dir_exists_absolute(_save_level_data_component.settings.save_game_data_path): DirAccess.make_dir_absolute(_save_level_data_component.settings.save_game_data_path) var save_game_file_path: String = _save_level_data_component.settings.save_game_data_path + resource_filename _save_node_data() var result: int = ResourceSaver.save(_game_data_resource, save_game_file_path) if result != OK: printerr("Failed to save game: ", result) ## Save the properties defined on the SaveDataComponents attached to various nodes (such as Block) func _save_node_data() -> void: var nodes: Array = get_tree().get_nodes_in_group("save_data_component") if nodes == null: return _game_data_resource = SaveGameDataResource.new() for node: Node in nodes: if node is SaveDataComponent: @warning_ignore("unsafe_method_access") var save_data_resource: Node3DDataResource = node._save_data() var save_final_resource: Node3DDataResource = save_data_resource.duplicate() _game_data_resource.save_data_nodes.append(save_final_resource)