| @ -0,0 +1,41 @@ | |||
| ## 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 | |||
| func _unhandled_input(event: InputEvent) -> void: | |||
| if event.is_action_pressed("quick_save"): | |||
| _save_game() | |||
| if event.is_action_pressed("quick_load"): | |||
| _load_game() | |||
| func _save_game() -> void: | |||
| var save_level_data_component: SaveLevelDataComponent = 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 | |||
| save_level_data_component.save_game() | |||
| game_saved.emit() | |||
| func _load_game() -> void: | |||
| EntityManager.reset_world.emit() | |||
| var save_level_data_component: SaveLevelDataComponent = 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 | |||
| save_level_data_component.load_game() | |||
| game_loaded.emit() | |||
| @ -0,0 +1 @@ | |||
| uid://dau7tp2xpp0w | |||
| @ -0,0 +1,26 @@ | |||
| ## Add to any node that has data we need to save | |||
| ## Each node type, such as TileMapLayer, will need a resource created for it | |||
| ## See `res://save_load/resources/node_types/` | |||
| class_name SaveDataComponent | |||
| extends Node | |||
| @export var save_data_resource: Node3DDataResource ## The resource describing the type of object being saved | |||
| @onready var parent_node: Node3D = get_parent() | |||
| func _ready() -> void: | |||
| add_to_group("save_data_component") | |||
| func _save_data() -> Resource: | |||
| if parent_node == null: | |||
| return | |||
| if save_data_resource == null: | |||
| push_error("save_data_resource not defined on node ", parent_node.name) | |||
| return | |||
| save_data_resource._save_data(parent_node) | |||
| return save_data_resource | |||
| @ -0,0 +1 @@ | |||
| uid://b0400fjcqflgh | |||
| @ -0,0 +1,6 @@ | |||
| [gd_scene load_steps=2 format=3 uid="uid://baki8rbf1ti0r"] | |||
| [ext_resource type="Script" uid="uid://b0400fjcqflgh" path="res://save_load/components/save_data_component.gd" id="1_nm1vf"] | |||
| [node name="SaveDataComponent" type="Node"] | |||
| script = ExtResource("1_nm1vf") | |||
| @ -0,0 +1,64 @@ | |||
| ## Performs the actual saving and loading of data related to this level/scene | |||
| ## Utilized by the SaveGameManager | |||
| class_name SaveLevelDataComponent | |||
| extends Node | |||
| ## See documentation to where this path is: https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#accessing-persistent-user-data-user[br][br] | |||
| ## Default Paths:[br] | |||
| ## * Windows: %APPDATA%\Godot\app_userdata\[project_name][br] | |||
| ## * macOS: ~/Library/Application Support/Godot/app_userdata/[project_name][br] | |||
| ## * Linux: ~/.local/share/godot/app_userdata/[project_name][br] | |||
| @export var save_game_data_path: String = "user://game_data/" | |||
| @export var save_file_name: String = "save_%s_game_data.tres" | |||
| var level_scene_name: String | |||
| var game_data_resource: SaveGameDataResource | |||
| var level_save_file_name: String | |||
| var save_game_file_path: String | |||
| func _ready() -> void: | |||
| add_to_group("save_level_data_component") | |||
| level_scene_name = get_parent().name | |||
| level_save_file_name = save_file_name % level_scene_name | |||
| save_game_file_path = save_game_data_path + level_save_file_name | |||
| 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) | |||
| func save_game() -> void: | |||
| if !DirAccess.dir_exists_absolute(save_game_data_path): | |||
| DirAccess.make_dir_absolute(save_game_data_path) | |||
| save_node_data() | |||
| var result: int = ResourceSaver.save(game_data_resource, save_game_file_path) | |||
| if result != OK: | |||
| printerr("Failed to save game: ", result) | |||
| func load_game() -> void: | |||
| 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) | |||
| @ -0,0 +1 @@ | |||
| uid://c7x2qvyu62230 | |||
| @ -0,0 +1,6 @@ | |||
| [gd_scene load_steps=2 format=3 uid="uid://c3pqilb6yh5kc"] | |||
| [ext_resource type="Script" uid="uid://c7x2qvyu62230" path="res://save_load/components/save_level_data_component.gd" id="1_exguq"] | |||
| [node name="SaveLevelDataComponent" type="Node"] | |||
| script = ExtResource("1_exguq") | |||
| @ -0,0 +1,16 @@ | |||
| ## The resource utilized for saving a Block | |||
| class_name BlockDataResource | |||
| extends Node3DDataResource | |||
| @export var block_id: String = "001" | |||
| func _save_data(node: Node3D) -> void: | |||
| super._save_data(node) | |||
| block_id = node.id | |||
| func _load_data(_window: Window) -> void: | |||
| EntityManager.create_block.emit(block_id, transform.origin) | |||
| @ -0,0 +1 @@ | |||
| uid://syaia0l6vjt1 | |||
| @ -0,0 +1,12 @@ | |||
| [gd_resource type="Resource" script_class="BlockDataResource" load_steps=2 format=3 uid="uid://dfos8np8agysk"] | |||
| [ext_resource type="Script" uid="uid://syaia0l6vjt1" path="res://save_load/resources/node_types/block_data_resource.gd" id="1_a06et"] | |||
| [resource] | |||
| script = ExtResource("1_a06et") | |||
| block_id = "001" | |||
| block_position = Vector3(0, 0, 0) | |||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) | |||
| node_path = NodePath("") | |||
| parent_node_path = NodePath("") | |||
| metadata/_custom_type_script = "uid://syaia0l6vjt1" | |||
| @ -0,0 +1,20 @@ | |||
| ## The base resource for saving a specific Node | |||
| class_name Node3DDataResource | |||
| extends Resource | |||
| @export var transform: Transform3D | |||
| @export var node_path: NodePath | |||
| @export var parent_node_path: NodePath | |||
| func _save_data(node: Node3D) -> void: | |||
| transform = node.transform | |||
| node_path = node.get_path() | |||
| var parent_node: Node3D = node.get_parent() | |||
| if parent_node != null: | |||
| parent_node_path = parent_node.get_path() | |||
| func _load_data(_window: Window) -> void: | |||
| pass | |||
| @ -0,0 +1 @@ | |||
| uid://drj0sfem1gmsk | |||
| @ -0,0 +1,9 @@ | |||
| [gd_resource type="Resource" script_class="NodeDataResource" load_steps=2 format=3 uid="uid://dald1lud7ktsj"] | |||
| [ext_resource type="Script" uid="uid://drj0sfem1gmsk" path="res://save_load/resources/node_types/node3d_data_resource.gd" id="1_b70a7"] | |||
| [resource] | |||
| script = ExtResource("1_b70a7") | |||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) | |||
| node_path = NodePath("") | |||
| parent_node_path = NodePath("") | |||
| @ -0,0 +1,16 @@ | |||
| ## The resource utilized for saving a Player | |||
| class_name PlayerDataResource | |||
| extends Node3DDataResource | |||
| func _save_data(node: Node3D) -> void: | |||
| super._save_data(node) | |||
| # TODO: Save inventory or any other data | |||
| func _load_data(window: Window) -> void: | |||
| var scene_node: Node = window.get_node_or_null(node_path) | |||
| if scene_node == null: return | |||
| EntityManager.spawn_player.emit(transform) | |||
| @ -0,0 +1 @@ | |||
| uid://dodqpooodtguo | |||
| @ -0,0 +1,10 @@ | |||
| [gd_resource type="Resource" script_class="PlayerDataResource" load_steps=2 format=3 uid="uid://bvsurbn5xgchr"] | |||
| [ext_resource type="Script" uid="uid://dodqpooodtguo" path="res://save_load/resources/node_types/player_data_resource.gd" id="1_scty6"] | |||
| [resource] | |||
| script = ExtResource("1_scty6") | |||
| transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) | |||
| node_path = NodePath("") | |||
| parent_node_path = NodePath("") | |||
| metadata/_custom_type_script = "uid://dodqpooodtguo" | |||
| @ -0,0 +1,8 @@ | |||
| ## The entire save structure that contains all Node3DDataResources | |||
| ## Node3DDataResources will be located by the SaveLevelDataComponent | |||
| class_name SaveGameDataResource | |||
| extends Resource | |||
| @export var game_version: String = ProjectSettings.get_setting("application/config/version") | |||
| @export var save_data_nodes: Array[Node3DDataResource] | |||
| @ -0,0 +1 @@ | |||
| uid://di6ov7tpewhft | |||
| @ -0,0 +1,9 @@ | |||
| [gd_resource type="Resource" script_class="SaveGameDataResource" load_steps=3 format=3 uid="uid://dkniygoky2jcx"] | |||
| [ext_resource type="Script" uid="uid://drj0sfem1gmsk" path="res://save_load/resources/node_types/node3d_data_resource.gd" id="1_7yx7n"] | |||
| [ext_resource type="Script" uid="uid://di6ov7tpewhft" path="res://save_load/resources/save_game_data_resource.gd" id="1_sbw5t"] | |||
| [resource] | |||
| script = ExtResource("1_sbw5t") | |||
| save_data_nodes = Array[ExtResource("1_7yx7n")]([]) | |||
| metadata/_custom_type_script = "uid://di6ov7tpewhft" | |||
| @ -0,0 +1,28 @@ | |||
| ## Used with the SaveDataComponent for defining node and scene_file_path | |||
| class_name SceneDataResource | |||
| extends Node3DDataResource | |||
| @export var node_name: String | |||
| @export var scene_file_path: String | |||
| func _save_data(node: Node3D) -> void: | |||
| super._save_data(node) | |||
| node_name = node.name | |||
| scene_file_path = node.scene_file_path | |||
| func _load_data(window: Window) -> void: | |||
| var parent_node: Node3D | |||
| if parent_node_path != null: | |||
| parent_node = window.get_node_or_null(parent_node_path) | |||
| var scene_node: Node3D | |||
| if node_path != null: | |||
| var scene_file_resource: Resource = load(scene_file_path) | |||
| scene_node = scene_file_resource.instantiate() | |||
| if parent_node != null and scene_node != null: | |||
| parent_node.add_child(scene_node) | |||
| @ -0,0 +1 @@ | |||
| uid://bxr74kmjt6heh | |||
| @ -0,0 +1,10 @@ | |||
| [gd_resource type="Resource" script_class="SceneDataResource" load_steps=2 format=3 uid="uid://duqhhi2nlic6n"] | |||
| [ext_resource type="Script" uid="uid://bxr74kmjt6heh" path="res://save_load/resources/scene_data_resource.gd" id="1_ihw72"] | |||
| [resource] | |||
| script = ExtResource("1_ihw72") | |||
| global_position = Vector2(0, 0) | |||
| node_path = NodePath("") | |||
| parent_node_path = NodePath("") | |||
| metadata/_custom_type_script = "uid://bxr74kmjt6heh" | |||