Tough_Crowd/godot/savegame/save_game.gd

163 lines
5.2 KiB
GDScript3
Raw Permalink Normal View History

2024-01-26 18:01:32 +00:00
## Script that manages saving games.
class_name SaveGame extends Node
2024-01-27 19:07:56 +00:00
const ENABLED = false
2024-01-26 18:01:32 +00:00
const ENCRYPTION_KEY = "godotrules"
const SAVE_GAME_TEMPLATE = "savegame.save"
const SAVE_GROUP_NAME = "Persist"
const NODE_DATA = "node_data"
static func delete_save() -> void:
if not ENABLED:
return
DirAccess.remove_absolute("user://" + SAVE_GAME_TEMPLATE)
static func has_save() -> bool:
return FileAccess.file_exists("user://" + SAVE_GAME_TEMPLATE)
static func save_game(tree:SceneTree):
if not ENABLED:
return
print("Saving game to user://" + SAVE_GAME_TEMPLATE)
var save_file = null
if OS.is_debug_build():
save_file = FileAccess.open("user://" + SAVE_GAME_TEMPLATE, FileAccess.WRITE)
else:
save_file = FileAccess.open_encrypted_with_pass("user://" + SAVE_GAME_TEMPLATE, FileAccess.WRITE, ENCRYPTION_KEY)
var save_nodes = tree.get_nodes_in_group(SAVE_GROUP_NAME)
for node in save_nodes:
var save_data = {}
# Check the node is an instanced scene so it can be instanced again during load.
if not node.scene_file_path.is_empty():
save_data["scene_file_path"] = node.scene_file_path
if not node.get_path().is_empty():
save_data["path"] = node.get_path()
if not node.get_parent().get_path().is_empty():
save_data["parent"] = node.get_parent().get_path()
if "position" in node:
save_data["pos_x"] = node.position.x
save_data["pos_y"] = node.position.y
if node.position is Vector3:
save_data["pos_z"] = node.position.z
if node is Node2D:
save_data["rotation"] = node.rotation
elif node is Node3D:
save_data["rotation_x"] = node.rotation.x
save_data["rotation_y"] = node.rotation.y
save_data["rotation_z"] = node.rotation.z
if "scale" in node:
save_data["scale_x"] = node.scale.x
save_data["scale_y"] = node.scale.y
if node.scale is Vector3:
save_data["scale_z"] = node.scale.z
save_data["visible"] = node.visible
if node is CanvasItem:
save_data["modulate_r"] = node.modulate.r
save_data["modulate_g"] = node.modulate.g
save_data["modulate_b"] = node.modulate.b
save_data["modulate_a"] = node.modulate.a
# Call the node's save function.
if node.has_method("save_data"):
save_data["node_data"] = node.call("save_data")
# Store the save dictionary as a new line in the save file.
save_file.store_line(JSON.stringify(save_data))
static func load_game(tree:SceneTree) -> void:
if not ENABLED:
return
if not has_save():
print("No save game found. Skipped loading!")
return
print("Load game from user://" + SAVE_GAME_TEMPLATE)
var save_nodes = tree.get_nodes_in_group(SAVE_GROUP_NAME)
var nodes_by_path = {}
for node in save_nodes:
if not node.get_path().is_empty():
nodes_by_path[node.get_path()] = node
# Load the file line by line and process that dictionary to restore
# the object it represents.
var save_file = null
if OS.is_debug_build():
save_file = FileAccess.open("user://" + SAVE_GAME_TEMPLATE, FileAccess.READ)
else:
save_file = FileAccess.open_encrypted_with_pass("user://" + SAVE_GAME_TEMPLATE, FileAccess.READ, ENCRYPTION_KEY)
while save_file.get_position() < save_file.get_length():
# Get the saved dictionary from the next line in the save file
var test_json_conv = JSON.new()
test_json_conv.parse(save_file.get_line())
var save_data = test_json_conv.get_data()
# Firstly, we need to create the object and add it to the tree and set its position.
var node = null
if "path" in save_data and nodes_by_path.has(NodePath(save_data.path)):
node = nodes_by_path[NodePath(save_data.path)]
nodes_by_path.erase(NodePath(save_data.path))
elif "path" in save_data and "parent" in save_data and "scene_file_path" in save_data:
# node is not present in tree so it was dynamically added at runtime
var parent = tree.root.get_node(NodePath(save_data["parent"]))
node = load(save_data["scene_file_path"]).instantiate()
parent.add_child(node)
else:
push_warning("skipping loading node from save game: node got moved.")
continue
if "position" in node:
if node.scale is Vector2:
node.position = Vector2(save_data["pos_x"], save_data["pos_y"])
elif node.scale is Vector3:
node.position = Vector3(save_data["pos_x"], save_data["pos_y"], save_data["pos_z"])
if node is Node2D:
node.rotation = save_data["rotation"]
elif node is Node3D:
node.rotation = Vector3(save_data["rotation_x"], save_data["rotation_y"], save_data["rotation_z"])
if "scale" in node:
if node.scale is Vector2:
node.scale = Vector2(save_data["scale_x"], save_data["scale_y"])
elif node.scale is Vector3:
node.scale = Vector3(save_data["scale_x"], save_data["scale_y"], save_data["scale_z"])
if save_data.has("visible") and "visible" in node:
node.visible = save_data["visible"]
if node is CanvasItem:
node.modulate = Color(save_data["modulate_r"], save_data["modulate_g"], save_data["modulate_b"], save_data["modulate_a"])
if node.has_method("load_data") and save_data.has("node_data"):
node.call("load_data", save_data["node_data"])
# delete any node from scene that was not persisted into the save file
# but is currently tagged as "Persisted" -> this means node got removed in the meantime
for key in nodes_by_path:
var node = nodes_by_path[key]
node.queue_free()