Browse Source

Merge pull request 'Save/Load Updates' (#18) from save-load-updates into master

pull/20/head
Ryan Reed 1 month ago
parent
commit
b04f54660c
11 changed files with 120 additions and 90 deletions
  1. +82
    -69
      addons/save_load_system/autoloads/save_game_manager.gd
  2. +4
    -4
      project.godot
  3. +1
    -1
      scenes/player/player.tscn
  4. +3
    -1
      scenes/ui/menus/pause_menu.gd
  5. +2
    -2
      scenes/ui/menus/saves_manager/save_file.gd
  6. +3
    -2
      scenes/ui/menus/saves_manager/save_files_list.gd
  7. +5
    -1
      scenes/ui/menus/saves_manager/save_load_ui.gd
  8. +2
    -1
      scenes/ui/menus/saves_manager/save_load_ui.tscn
  9. +7
    -5
      scenes/ui/ui.gd
  10. +2
    -1
      scenes/ui/ui.tscn
  11. +9
    -3
      scenes/world/world.gd

+ 82
- 69
addons/save_load_system/autoloads/save_game_manager.gd View File

@ -7,38 +7,43 @@
extends Node
signal game_saved
signal game_loaded
signal create_auto_save_file
signal create_save_file(save_name: String)
signal delete_save_file(filename: String)
signal load_save_file(filename: String)
signal apply_complete
signal apply_save ## Apply the loaded data to the tree. Note: This should happen AFTER load_save()
signal create_auto_save
signal create_save(save_name: String)
signal delete_save(filename: String)
signal delete_error(error_message: String)
signal load_complete
signal load_error(error_message: String)
signal load_save(filename: String) ## Loads the save into memory. Does NOT apply the load as this allows for other actions (such as resetting the levle/world).[br]Don't forget to run `apply_save` after loading
signal save_complete
signal save_error(error_message: String)
signal quick_save
signal quick_load
signal open_save_list_ui
signal close_save_list_ui
signal toggle_save_icon_generation(toggled: bool) ## Enable/Disable the generation of a screenshot during save for the save icon.
var _enable_save_icon_generation: bool = true
var _game_data_resource: SaveGameDataResource = SaveGameDataResource.new()
var _loaded_save_resource: SaveGameDataResource = SaveGameDataResource.new()
var _save_icon_size: Vector2i = Vector2i(896, 504) ## If Vector2.ZERO, uses the user's resolution
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(_on_save_game_as_resource)
load_save_file.connect(load_game_save)
delete_save_file.connect(_on_delete_save_file)
apply_save.connect(_on_apply_save)
create_save.connect(_on_save_game_as_resource)
delete_save.connect(_on_delete_save)
load_save.connect(_on_load_game_save)
quick_load.connect(_on_quick_load)
quick_save.connect(_on_quick_save)
toggle_save_icon_generation.connect(_on_toggle_save_icon_generation)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("quick_save"):
quick_save_game()
_on_quick_save()
if event.is_action_pressed("quick_load"):
quick_load_game()
_on_quick_load()
func list_saves(include_quick_saves: bool = true, include_auto_saves: bool = true) -> Array[SaveFileDetailsResource]:
@ -81,27 +86,26 @@ func list_saves(include_quick_saves: bool = true, include_auto_saves: bool = tru
save_files.sort_custom(_custom_save_file_sort)
return save_files
func load_game_save(resource_filename: String) -> void:
_load_game_resource(resource_filename)
game_loaded.emit()
func quick_save_game() -> void:
if not _load_save_level_data_component(): return
_game_data_resource.save_name = "Quick Save"
_save_game_as_resource(_save_level_data_component.settings.quicksave_file_name)
game_saved.emit()
## Sort the save files list by date created, descending
func _custom_save_file_sort(a: SaveFileDetailsResource, b: SaveFileDetailsResource) -> bool:
return a.date_created > b.date_created
func quick_load_game() -> void:
if not _load_save_level_data_component(): return
## Save the properties defined on the SaveDataComponents attached to various nodes (such as Block)
func _generate_save_game_resource() -> SaveGameDataResource:
var nodes: Array = get_tree().get_nodes_in_group("save_data_component")
_load_game_resource(_save_level_data_component.settings.quicksave_file_name)
game_loaded.emit()
if nodes == null: return
var _resource: SaveGameDataResource = 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()
_resource.save_data_nodes.append(save_final_resource)
## Sort the save files list by date created, descending
func _custom_save_file_sort(a: SaveFileDetailsResource, b: SaveFileDetailsResource) -> bool:
return a.date_created > b.date_created
return _resource
## Generate the texture for use in the save file listing
func _generate_save_icon_texture(save_icon: String) -> Texture2D:
@ -133,48 +137,34 @@ func _load_game_resource(resource_filename: String) -> void:
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)
load_error.emit("Failed to load save. File does not exist: %s" % 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)
_loaded_save_resource = ResourceLoader.load(save_game_file_path)
if _loaded_save_resource == null:
load_error.emit("Failed to load save. Unknown format? %s" % save_game_file_path)
return
EntityManager.reset_world.emit()
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)
load_complete.emit()
func _save_game_as_resource(resource_filename: String) -> void:
func _save_game_as_resource(save_name, 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)
var _save_resource: SaveGameDataResource = _generate_save_game_resource()
_save_resource.save_name = save_name
var result: int = ResourceSaver.save(_save_resource, save_game_file_path)
if result != OK:
printerr("Failed to save game (" , result, "): ", save_game_file_path)
save_error.emit("Failed to save game (" , result, "): " + save_game_file_path)
return
_take_save_screenshot(save_game_file_path)
## 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
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)
save_complete.emit()
## Takes a screenshot and saves next to the save file[br]
## The icon utilizes the same filename as the save file, replacing `.tres` with `.png`
@ -190,26 +180,49 @@ func _take_save_screenshot(save_game_file_path: String) -> void:
_icon.save_png(_icon_filepath)
## Save the game, with a filename of `<save_prepend><current_date>.tres
func _on_save_game_as_resource(save_name: String) -> void:
if not _load_save_level_data_component(): return
## Apply the loaded save. Should be performed after load_save
func _on_apply_save() -> void:
if _loaded_save_resource == null: return
var current_date: String = Time.get_datetime_string_from_system().replace(":", "")
var _filename: String = "save_" + current_date + ".tres"
var root_node: Window = get_tree().root
for resource: Resource in _loaded_save_resource.save_data_nodes:
if resource is Node3DDataResource:
(resource as Node3DDataResource)._load_data(root_node)
_game_data_resource.save_name = save_name
_save_game_as_resource(_filename)
game_saved.emit()
apply_complete.emit()
## Delete both the save file and the related screenshot
func _on_delete_save_file(filename: String) -> void:
func _on_delete_save(filename: String) -> void:
if filename.length() < 1:
printerr("_on_delete_save_file(): Empty filename provided")
delete_error.emit("Empty filename provided")
return
var save_file_path: String = _save_level_data_component.settings.save_game_data_path + filename
DirAccess.remove_absolute(save_file_path)
DirAccess.remove_absolute(save_file_path.replace(".tres", ".png")) # Delete icon
func _on_load_game_save(resource_filename: String) -> void:
_load_game_resource(resource_filename)
func _on_quick_load() -> void:
if not _load_save_level_data_component(): return
_load_game_resource(_save_level_data_component.settings.quicksave_file_name)
func _on_quick_save() -> void:
if not _load_save_level_data_component(): return
_save_game_as_resource("Quick Save", _save_level_data_component.settings.quicksave_file_name)
## Save the game, with a filename of `<save_prepend><current_date>.tres
func _on_save_game_as_resource(save_name: String) -> void:
if not _load_save_level_data_component(): return
var current_date: String = Time.get_datetime_string_from_system().replace(":", "")
var _filename: String = "save_" + current_date + ".tres"
_loaded_save_resource.save_name = save_name
_save_game_as_resource(save_name, _filename)
save_complete.emit()
func _on_toggle_save_icon_generation(toggled: bool) -> void:
_enable_save_icon_generation = toggled

+ 4
- 4
project.godot View File

@ -53,6 +53,10 @@ folder_colors={
"res://scenes/ui/": "green"
}
[global_group]
Player=""
[input]
move_forward={
@ -205,7 +209,3 @@ quickslot10={
3d_physics/layer_1="World"
3d_physics/layer_2="Player"
3d_physics/layer_3="Pickups"
[physics]
3d/physics_engine="Jolt Physics"

+ 1
- 1
scenes/player/player.tscn View File

@ -14,7 +14,7 @@ height = 1.95
radius = 0.2
height = 0.89
[node name="Player" type="CharacterBody3D"]
[node name="Player" type="CharacterBody3D" groups=["Player"]]
collision_layer = 2
script = ExtResource("1_7sql3")
jump_count = 2


+ 3
- 1
scenes/ui/menus/pause_menu.gd View File

@ -1,6 +1,8 @@
class_name PauseMenu
extends Panel
@export var save_load_ui: SaveLoadUI
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and self.visible:
@ -27,4 +29,4 @@ func _on_settings_button_pressed() -> void:
SignalManager.open_settings_menu.emit()
func _on_saves_button_pressed() -> void:
SaveGameManager.open_save_list_ui.emit()
save_load_ui.open_save_list.emit()

+ 2
- 2
scenes/ui/menus/saves_manager/save_file.gd View File

@ -51,14 +51,14 @@ func _on_delete_button_pressed() -> void:
delete_confirm_ui.show()
func _on_delete_confirm_button_pressed() -> void:
SaveGameManager.delete_save_file.emit(save_file_details.filename)
SaveGameManager.delete_save.emit(save_file_details.filename)
queue_free()
func _on_delete_cancel_button_pressed() -> void:
delete_confirm_ui.hide()
func _on_load_button_pressed() -> void:
SaveGameManager.load_save_file.emit(save_file_details.filename)
SaveGameManager.load_save.emit(save_file_details.filename)
func _on_mouse_entered() -> void:
set("theme_override_styles/panel", save_panel_highlight)


+ 3
- 2
scenes/ui/menus/saves_manager/save_files_list.gd View File

@ -2,10 +2,11 @@ extends VBoxContainer
@export var save_file_scene: PackedScene
@export var save_load_ui: SaveLoadUI
func _ready() -> void:
SaveGameManager.open_save_list_ui.connect(_on_open_save_list_ui)
save_load_ui.open_save_list.connect(_on_open_save_list) # TODO: Reconsider this setup
## Clear the SaveFilesList node of all saves and load most recent saves
@ -24,5 +25,5 @@ func _clear_save_files_list() -> void:
for _panel: SaveFilePanel in get_children():
_panel.queue_free()
func _on_open_save_list_ui() -> void:
func _on_open_save_list() -> void:
refresh_saves_list()

+ 5
- 1
scenes/ui/menus/saves_manager/save_load_ui.gd View File

@ -2,6 +2,10 @@ class_name SaveLoadUI
extends Control
signal close_save_list
signal open_save_list
@export var show_save_ui_button: BaseButton
@export var new_save_ui: Control
@export var save_name_input: LineEdit
@ -26,7 +30,7 @@ func _on_create_save_button_pressed() -> void:
if _save_level_data_component.ui_node != null:
_save_level_data_component.ui_node.visible = false
await get_tree().create_timer(.150).timeout # A hack to allow time for UI to hide before taking screenshot
SaveGameManager.create_save_file.emit(save_name)
SaveGameManager.create_save.emit(save_name)
if _save_level_data_component.ui_node != null:
_save_level_data_component.ui_node.visible = true
new_save_ui.hide()


+ 2
- 1
scenes/ui/menus/saves_manager/save_load_ui.tscn View File

@ -58,9 +58,10 @@ custom_minimum_size = Vector2(0, 500)
layout_mode = 2
horizontal_scroll_mode = 0
[node name="SaveFilesList" parent="Panel/MarginContainer/VBoxContainer/ScrollContainer" instance=ExtResource("1_tqtxm")]
[node name="SaveFilesList" parent="Panel/MarginContainer/VBoxContainer/ScrollContainer" node_paths=PackedStringArray("save_load_ui") instance=ExtResource("1_tqtxm")]
layout_mode = 2
size_flags_horizontal = 6
save_load_ui = NodePath("../../../../..")
[node name="SaveFilePanel" parent="Panel/MarginContainer/VBoxContainer/ScrollContainer/SaveFilesList" instance=ExtResource("2_6uxbh")]
layout_mode = 2


+ 7
- 5
scenes/ui/ui.gd View File

@ -5,7 +5,7 @@ extends CanvasLayer
@onready var crosshair: CenterContainer = $Crosshair
@onready var quick_slots: MarginContainer = $QuickSlots
@onready var pause_menu: PauseMenu = $PauseMenu
@onready var save_load_ui: Control = $SaveLoadUI
@onready var save_load_ui: SaveLoadUI = $SaveLoadUI
@onready var settings_menu: SettingsMenu = $SettingsMenu
@onready var waila: Waila = $Waila
@ -16,9 +16,11 @@ func _ready() -> void:
SignalManager.close_settings_menu.connect(_on_close_settings_menu)
SignalManager.open_settings_menu.connect(_on_open_settings_menu)
SignalManager.resume_game.connect(_on_resume_game)
SaveGameManager.close_save_list_ui.connect(_on_close_save_list_ui)
SaveGameManager.open_save_list_ui.connect(_on_open_save_list_ui)
SaveGameManager.game_loaded.connect(_on_game_loaded)
save_load_ui.close_save_list.connect(_on_close_save_list_ui)
save_load_ui.open_save_list.connect(_on_open_save_list_ui)
SaveGameManager.load_complete.connect(_on_load_complete)
_on_resume_game()
@ -43,7 +45,7 @@ func _on_close_save_list_ui() -> void:
func _on_close_settings_menu() -> void:
SignalManager.resume_game.emit()
func _on_game_loaded() -> void:
func _on_load_complete() -> void:
_on_resume_game()
func _on_open_pause_menu() -> void:


+ 2
- 1
scenes/ui/ui.tscn View File

@ -31,8 +31,9 @@ mouse_filter = 2
[node name="QuickSlots" parent="." instance=ExtResource("4_g5kmx")]
[node name="PauseMenu" parent="." instance=ExtResource("6_7vp6q")]
[node name="PauseMenu" parent="." node_paths=PackedStringArray("save_load_ui") instance=ExtResource("6_7vp6q")]
visible = false
save_load_ui = NodePath("../SaveLoadUI")
[node name="SettingsMenu" parent="." instance=ExtResource("7_7vp6q")]
visible = false


+ 9
- 3
scenes/world/world.gd View File

@ -15,6 +15,7 @@ func _ready() -> void:
EntityManager.drop_block.connect(_create_dropped_block)
EntityManager.reset_world.connect(clear_world)
EntityManager.spawn_player.connect(spawn_player)
SaveGameManager.load_complete.connect(_on_load_save_complete)
clear_world()
create_world()
@ -48,9 +49,10 @@ func generate_world() -> void:
EntityManager.create_block.emit("002", ground_position)
func spawn_player(player_position: Transform3D) -> void:
if has_node("Player"):
$Player.queue_free()
await $Player.tree_exited
var players: Array = get_tree().get_nodes_in_group("Player")
for player: Player in players:
player.queue_free()
await player.tree_exited
var player: Player = player_scene.instantiate()
player.transform = player_position
@ -81,3 +83,7 @@ func _create_test_blocks() -> void:
func _on_reset_world() -> void:
clear_world()
func _on_load_save_complete() -> void:
clear_world()
SaveGameManager.apply_save.emit()

Loading…
Cancel
Save