-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from theludovyc/fix-#17
fix #17 Handle saves
- Loading branch information
Showing
12 changed files
with
493 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
extends Object | ||
class_name SaveHelper | ||
## Helps to save data in [JSON] format | ||
## | ||
## Here is a sample on how to save data: | ||
## [codeblock] | ||
## var data_to_save := {"something":"Something"} | ||
## if SaveHelper.save(data_to_save) != OK: | ||
## push_error("Cannot save data") | ||
## [/codeblock] | ||
## And how to load and use them: | ||
## [codeblock] | ||
## SaveHelper.update_save_file_names() | ||
## | ||
## if SaveHelper.load_last_save() != OK: | ||
## push_error("Cannot load data") | ||
## var something := SaveHelper.last_saved_data["something"] | ||
## [/codeblock] | ||
|
||
|
||
## where the data will be saved | ||
const save_dir_path = "user://saves" | ||
|
||
## to avoid errors | ||
const json_extension = "json" | ||
|
||
## updated with SaveHelper.update_save_files_names [br] | ||
## saved here to avoid multiples disk access [br] | ||
## after update it, can be used to check if save(s) exist or not [br] | ||
## should be clear with SaveHelper.clear_save_files_names to avoid | ||
## memory consumption | ||
static var save_file_names:PackedStringArray | ||
|
||
## the last saved file name without extension .json | ||
static var last_saved_file_name := "" | ||
|
||
## use it to save the file_name to load between scenes | ||
static var save_file_name_to_load := "" | ||
|
||
## last loaded data [br] | ||
## empty [Dictionary] by default | ||
static var last_loaded_data:Dictionary = {} | ||
|
||
## save data in a file [br] | ||
## the file will be created in save_dir_path [br] | ||
## the file_name will be generated from the systemTime in local time [br] | ||
## the file_name name will look like YYYY-MM-DDTHH:MM:SS.json [br] | ||
## yes, the user can modify his system time, so you can use this feature and/or create easter-eggs [br] | ||
## if cannot create saves directory return ERR_CANT_CREATE [br] | ||
## if cannot create and write in the save file return ERR_FILE_CANT_WRITE [br] | ||
## return OK in other cases | ||
static func save(data:Dictionary) -> Error: | ||
if not DirAccess.dir_exists_absolute(save_dir_path): | ||
if DirAccess.make_dir_absolute(save_dir_path) != OK: | ||
push_error("Cannot create saves directory in user://") | ||
return ERR_CANT_CREATE | ||
|
||
var file_name := Time.get_datetime_string_from_system() | ||
|
||
var file := FileAccess.open(save_dir_path + "/" + file_name + "." + json_extension, FileAccess.WRITE) | ||
if file == null: | ||
push_error("Cannot create the save file in " + save_dir_path) | ||
return ERR_FILE_CANT_WRITE | ||
|
||
var json_data = JSON.stringify(data) | ||
|
||
file.store_string(json_data) | ||
|
||
last_saved_file_name = file_name | ||
|
||
return OK | ||
|
||
## return a file_path into the save directory from the file_name [br] | ||
## if file_name is empty return empty String [br] | ||
## if the extension .json is missing it will be added | ||
static func get_save_file_path(file_name:String) -> String: | ||
if file_name.is_empty(): | ||
return "" | ||
|
||
if not file_name.get_extension() == json_extension: | ||
file_name += "." + json_extension | ||
|
||
return save_dir_path + "/" + file_name | ||
|
||
## return a file_path into the save directory from the file_name [br] | ||
## if the extension .json is present it will be removed | ||
## if file_name is empty return empty String | ||
static func get_save_file_path_without_extension(file_name:String) -> String: | ||
if file_name.is_empty(): | ||
return "" | ||
|
||
return save_dir_path + "/" + file_name.trim_suffix("." + json_extension) | ||
|
||
## load data from file [br] | ||
## file_name should looks like YYYY-MM-DDTHH:MM:SS.json [br] | ||
## if file_name is empty return ERR_INVALID_PARAMETER [br] | ||
## if the extension .json is missing it will be added [br] | ||
## if the file cannot be opened and read return ERR_FILE_CANT_READ [br] | ||
## if the file cannot be parsed to [JSON] return ERR_INVALID_DATA [br] | ||
## return OK in other cases and save the parsed result in last_loaded_data | ||
static func load(file_name:String) -> Error: | ||
if file_name.is_empty(): | ||
push_warning("file_name is empty !") | ||
return ERR_INVALID_PARAMETER | ||
|
||
var file_path := get_save_file_path(file_name) | ||
|
||
var file := FileAccess.open(file_path, FileAccess.READ) | ||
if file == null: | ||
push_error("Cannot open the save file: " + file_path) | ||
return ERR_FILE_CANT_READ | ||
|
||
var json_data := file.get_as_text(true) | ||
|
||
var parsed_json = JSON.parse_string(json_data) | ||
|
||
if parsed_json == null: | ||
last_loaded_data = {} | ||
|
||
push_error("Cannot parse to json the save file") | ||
return ERR_INVALID_DATA | ||
|
||
last_loaded_data = parsed_json | ||
|
||
return OK | ||
|
||
## Call SaveHelper.load(save_file_name_to_load) [br] | ||
## if save_file_name_to_load is empty return ERR_INVALID_PARAMETER | ||
static func load_saved_file_name() -> Error: | ||
return SaveHelper.load(save_file_name_to_load) | ||
|
||
## warning, make disk access, avoid multiples use [br] | ||
## set SaveHelper.save_file_names | ||
static func update_save_file_names() -> Error: | ||
var dirAccess := DirAccess.open(save_dir_path) | ||
if dirAccess == null: | ||
push_warning("Cannot open the save directory") | ||
return FAILED | ||
|
||
save_file_names.clear() | ||
|
||
dirAccess.list_dir_begin() | ||
|
||
var file_name = dirAccess.get_next() | ||
|
||
while not file_name.is_empty(): | ||
if not dirAccess.current_is_dir() \ | ||
and file_name.get_extension() == json_extension: | ||
save_file_names.push_back(file_name.left(file_name.rfind("."))) | ||
|
||
file_name = dirAccess.get_next() | ||
|
||
if not save_file_names.is_empty(): | ||
save_file_names.sort() | ||
|
||
return OK | ||
|
||
## set SaveHelper.save_file_name_to_load with last saved name [br] | ||
## if save_files_names is empty return ERR_DOES_NOT_EXIST [br] | ||
## return OK in other cases | ||
static func update_with_last_saved_name() -> Error: | ||
if save_file_names.is_empty(): | ||
push_warning("No save to load") | ||
return ERR_DOES_NOT_EXIST | ||
|
||
save_file_name_to_load = save_file_names[-1] | ||
|
||
return OK | ||
|
||
## move to trash the save file [br] | ||
## file_name should looks like YYYY-MM-DDTHH:MM:SS.json [br] | ||
## if file_name is empty return ERR_INVALID_PARAMETER [br] | ||
## if the extension .json is missing it will be added [br] | ||
## if the file cannot be found return ERR_FILE_NOT_FOUND [br] | ||
## return OS.move_to_trash(...) in other cases | ||
static func delete(file_name:String) -> Error: | ||
if file_name.is_empty(): | ||
push_warning("file_name is empty !") | ||
return ERR_INVALID_PARAMETER | ||
|
||
var file_path := get_save_file_path(file_name) | ||
|
||
if not FileAccess.file_exists(file_path): | ||
push_error("Cannot found the save file: " + file_path) | ||
return ERR_FILE_NOT_FOUND | ||
|
||
return OS.move_to_trash(ProjectSettings.globalize_path(file_path)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[gd_scene load_steps=2 format=3 uid="uid://choevp6ilq78t"] | ||
|
||
[ext_resource type="Script" path="res://scenes/LoadSaveMenu/load_save_menu.gd" id="1_nyk5l"] | ||
|
||
[node name="LoadSaveMenu" type="ScrollContainer"] | ||
anchors_preset = 15 | ||
anchor_right = 1.0 | ||
anchor_bottom = 1.0 | ||
grow_horizontal = 2 | ||
grow_vertical = 2 | ||
horizontal_scroll_mode = 0 | ||
script = ExtResource("1_nyk5l") | ||
|
||
[node name="VBoxContainer" type="VBoxContainer" parent="."] | ||
layout_mode = 2 | ||
size_flags_horizontal = 3 | ||
|
||
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] | ||
unique_name_in_owner = true | ||
|
||
[connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_confirmation_dialog_confirmed"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
extends ScrollContainer | ||
|
||
signal no_save_to_load | ||
|
||
const confirm_load = "Are you sure you want to load this save?\n" | ||
const confirm_delete = "Are you sure you want to delete this save ?\n" | ||
|
||
var SavePanel = preload("res://scenes/LoadSaveMenu/savePanelContainer.tscn") | ||
|
||
@onready var vbox_container = $VBoxContainer | ||
|
||
@onready var confirm_dialog = %ConfirmationDialog | ||
|
||
enum Modes{ | ||
Loading, | ||
Deleting | ||
} | ||
|
||
var popup_mode = Modes.Loading | ||
var current_save_file_name:String = "" | ||
var current_save_panel:Node = null | ||
|
||
# Called when the node enters the scene tree for the first time. | ||
func _ready() -> void: | ||
if SaveHelper.save_file_names.is_empty(): | ||
SaveHelper.update_save_file_names() | ||
|
||
if SaveHelper.save_file_names.is_empty(): | ||
push_warning("No save to load") | ||
pass | ||
|
||
for save_file_name in SaveHelper.save_file_names: | ||
var save_panel = SavePanel.instantiate() | ||
|
||
vbox_container.add_child(save_panel) | ||
|
||
save_panel.init(save_file_name) | ||
|
||
save_panel.load_button.pressed.connect(_on_load_button_pressed.bind(save_file_name)) | ||
save_panel.delete_button.pressed.connect( | ||
_on_delete_button_pressed.bind(save_panel, save_file_name)) | ||
|
||
func _on_load_button_pressed(save_file_name:String): | ||
popup_mode = Modes.Loading | ||
|
||
current_save_file_name = save_file_name | ||
|
||
confirm_dialog.dialog_text = confirm_load + save_file_name | ||
|
||
confirm_dialog.popup_centered() | ||
|
||
func _on_delete_button_pressed(save_panel:Node, save_file_name:String): | ||
popup_mode = Modes.Deleting | ||
|
||
current_save_file_name = save_file_name | ||
|
||
current_save_panel = save_panel | ||
|
||
confirm_dialog.dialog_text = confirm_delete + save_file_name | ||
|
||
confirm_dialog.popup_centered() | ||
|
||
func _on_confirmation_dialog_confirmed() -> void: | ||
match(popup_mode): | ||
Modes.Loading: | ||
SaveHelper.save_file_name_to_load = current_save_file_name | ||
|
||
SceneLoader.change_scene(RGT_Globals.first_game_scene_setting) | ||
|
||
Modes.Deleting: | ||
SaveHelper.delete(current_save_file_name) | ||
|
||
# move to trash the screenshot | ||
OS.move_to_trash( | ||
ProjectSettings.globalize_path( | ||
SaveHelper.get_save_file_path_without_extension(current_save_file_name) + ".png" | ||
)) | ||
|
||
SaveHelper.update_save_file_names() | ||
|
||
if SaveHelper.save_file_names.is_empty(): | ||
no_save_to_load.emit() | ||
|
||
current_save_panel.queue_free() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
[gd_scene load_steps=2 format=3 uid="uid://wal8k0gos06y"] | ||
|
||
[ext_resource type="Script" path="res://scenes/LoadSaveMenu/save_panel_container.gd" id="1_np60c"] | ||
|
||
[node name="SavePanelContainer" type="PanelContainer"] | ||
script = ExtResource("1_np60c") | ||
|
||
[node name="MarginContainer" type="MarginContainer" parent="."] | ||
layout_mode = 2 | ||
theme_override_constants/margin_left = 12 | ||
theme_override_constants/margin_top = 12 | ||
theme_override_constants/margin_right = 12 | ||
theme_override_constants/margin_bottom = 12 | ||
|
||
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"] | ||
layout_mode = 2 | ||
theme_override_constants/separation = 8 | ||
|
||
[node name="SaveTextureRect" type="TextureRect" parent="MarginContainer/HBoxContainer"] | ||
unique_name_in_owner = true | ||
layout_mode = 2 | ||
size_flags_horizontal = 3 | ||
size_flags_vertical = 4 | ||
expand_mode = 5 | ||
stretch_mode = 5 | ||
|
||
[node name="NameLabel" type="Label" parent="MarginContainer/HBoxContainer"] | ||
unique_name_in_owner = true | ||
custom_minimum_size = Vector2(8, 8) | ||
layout_mode = 2 | ||
size_flags_horizontal = 3 | ||
horizontal_alignment = 1 | ||
vertical_alignment = 1 | ||
autowrap_mode = 1 | ||
|
||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"] | ||
layout_mode = 2 | ||
size_flags_vertical = 4 | ||
alignment = 1 | ||
|
||
[node name="LoadButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer"] | ||
unique_name_in_owner = true | ||
layout_mode = 2 | ||
text = "Load" | ||
|
||
[node name="DeleteButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer"] | ||
unique_name_in_owner = true | ||
layout_mode = 2 | ||
text = "Delete" |
Oops, something went wrong.