Save & Load Examples
This page provides practical examples of implementing save/load functionality in your Argode projects.
Basic Save/Load Implementation
Simple Save Points
Create save points at key moments in your story:
# In your .rgd script
label chapter_1_start:
"Welcome to Chapter 1!"
"This looks like a good place to save."
save 0 "Chapter 1 Start"
"The story continues..."
label important_choice:
"An important decision approaches..."
save 1 "Before Important Choice"
menu:
"Choose your path":
"Path A":
jump path_a
"Path B":
jump path_b
Auto-Save Integration
Implement automatic saving at key points:
# In your custom command or game logic
extends BaseCustomCommand
func _init():
command_name = "auto_checkpoint"
description = "Create an auto-save checkpoint"
func execute(params: Dictionary, adv_system: Node) -> bool:
# Auto-save the current state
var success = adv_system.SaveLoadManager.auto_save()
if success:
# Show brief save notification
adv_system.UIManager.show_notification("Game Saved")
return success
Save Menu Implementation
Custom Save/Load UI
Create a custom save/load interface:
# SaveLoadMenu.gd
extends Control
class_name SaveLoadMenu
@onready var save_slots = $VBoxContainer/SaveSlots
@onready var load_button = $VBoxContainer/LoadButton
@onready var save_button = $VBoxContainer/SaveButton
var selected_slot = -1
func _ready():
_refresh_save_slots()
func _refresh_save_slots():
# Clear existing slots
for child in save_slots.get_children():
child.queue_free()
# Create slots 0-8 (reserve 9 for auto-save)
for i in range(9):
var slot_button = _create_slot_button(i)
save_slots.add_child(slot_button)
func _create_slot_button(slot_index: int) -> Button:
var button = Button.new()
var save_info = ArgodeSystem.get_save_info(slot_index)
if save_info.is_empty():
button.text = "Slot %d - Empty" % (slot_index + 1)
else:
var date_str = save_info.get("save_date", "Unknown")
button.text = "Slot %d - %s\n%s" % [
slot_index + 1,
save_info.get("save_name", "Unnamed Save"),
date_str
]
button.pressed.connect(_on_slot_selected.bind(slot_index))
return button
func _on_slot_selected(slot_index: int):
selected_slot = slot_index
load_button.disabled = ArgodeSystem.get_save_info(slot_index).is_empty()
save_button.disabled = false
func _on_save_pressed():
if selected_slot >= 0:
var save_name = "Save %d" % (selected_slot + 1)
var success = ArgodeSystem.save_game(selected_slot, save_name)
if success:
_refresh_save_slots()
func _on_load_pressed():
if selected_slot >= 0:
ArgodeSystem.load_game(selected_slot)
Integration with RGD Scripts
Use the save menu from scripts:
# Show save menu command
save_menu
# Show load menu command
load_menu
# Quick save to specific slot
save 0 "Quick Save"
Advanced Save Features
Save Data Validation
Implement save data validation:
# SaveValidator.gd
extends RefCounted
class_name SaveValidator
static func validate_save_data(save_data: Dictionary) -> bool:
# Check required fields
var required_fields = ["version", "variables", "save_time"]
for field in required_fields:
if not save_data.has(field):
push_error("Save data missing required field: " + field)
return false
# Validate version compatibility
var save_version = save_data.get("version", "1.0")
if not _is_version_compatible(save_version):
push_error("Incompatible save version: " + save_version)
return false
return true
static func _is_version_compatible(version: String) -> bool:
# Define version compatibility rules
var compatible_versions = ["2.0", "2.1"]
return version in compatible_versions
Custom Save Data Extension
Add custom data to saves:
# CustomSaveExtension.gd
extends Node
func _ready():
# Connect to save system
ArgodeSystem.SaveLoadManager.connect("game_saved", _on_game_saved)
ArgodeSystem.SaveLoadManager.connect("game_loaded", _on_game_loaded)
func _on_game_saved(slot: int):
# This is called after the main save is complete
_save_additional_data(slot)
func _on_game_loaded(slot: int):
# This is called after the main load is complete
_load_additional_data(slot)
func _save_additional_data(slot: int):
var additional_data = {
"achievements": GameData.unlocked_achievements,
"statistics": GameData.play_statistics,
"settings": GameData.user_preferences
}
var file_path = "user://saves/slot_%d_extra.dat" % slot
var file = FileAccess.open(file_path, FileAccess.WRITE)
if file:
file.store_string(JSON.stringify(additional_data))
file.close()
func _load_additional_data(slot: int):
var file_path = "user://saves/slot_%d_extra.dat" % slot
if FileAccess.file_exists(file_path):
var file = FileAccess.open(file_path, FileAccess.READ)
if file:
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
if json.parse(json_string) == OK:
var data = json.data
GameData.unlocked_achievements = data.get("achievements", [])
GameData.play_statistics = data.get("statistics", {})
GameData.user_preferences = data.get("settings", {})
Save System Events
Progress Tracking
Track save/load progress for long operations:
# SaveProgressTracker.gd
extends Control
@onready var progress_bar = $ProgressBar
@onready var status_label = $StatusLabel
func _ready():
# Connect to save system signals
var save_manager = ArgodeSystem.SaveLoadManager
save_manager.save_started.connect(_on_save_started)
save_manager.save_progress.connect(_on_save_progress)
save_manager.save_completed.connect(_on_save_completed)
func _on_save_started():
show()
progress_bar.value = 0
status_label.text = "Preparing save data..."
func _on_save_progress(percentage: float, status: String):
progress_bar.value = percentage
status_label.text = status
func _on_save_completed(success: bool):
if success:
status_label.text = "Save completed!"
await get_tree().create_timer(1.0).timeout
else:
status_label.text = "Save failed!"
await get_tree().create_timer(2.0).timeout
hide()
Error Recovery
Implement error recovery for save operations:
# SaveErrorRecovery.gd
extends Node
const MAX_RETRY_ATTEMPTS = 3
var retry_count = 0
func _ready():
ArgodeSystem.SaveLoadManager.save_failed.connect(_on_save_failed)
ArgodeSystem.SaveLoadManager.load_failed.connect(_on_load_failed)
func _on_save_failed(slot: int, error: String):
if retry_count < MAX_RETRY_ATTEMPTS:
retry_count += 1
print("Save failed, retrying... Attempt %d/%d" % [retry_count, MAX_RETRY_ATTEMPTS])
# Wait a bit and retry
await get_tree().create_timer(1.0).timeout
ArgodeSystem.save_game(slot, "Retry Save")
else:
retry_count = 0
_show_save_error_dialog(error)
func _on_load_failed(slot: int, error: String):
_show_load_error_dialog(slot, error)
func _show_save_error_dialog(error: String):
var dialog = AcceptDialog.new()
dialog.dialog_text = "Failed to save game: " + error
get_tree().current_scene.add_child(dialog)
dialog.popup_centered()
dialog.confirmed.connect(dialog.queue_free)
func _show_load_error_dialog(slot: int, error: String):
var dialog = ConfirmationDialog.new()
dialog.dialog_text = "Failed to load save slot %d: %s\n\nTry a different slot?" % [slot, error]
get_tree().current_scene.add_child(dialog)
dialog.popup_centered()
dialog.confirmed.connect(_show_load_menu)
dialog.get_cancel_button().pressed.connect(dialog.queue_free)
func _show_load_menu():
# Show save/load menu for user to select different slot
pass
Performance Optimization
Async Save Operations
Implement non-blocking save operations:
# AsyncSaveManager.gd
extends Node
signal save_completed(success: bool)
func save_game_async(slot: int, save_name: String):
# Start async save operation
_perform_async_save.call_deferred(slot, save_name)
func _perform_async_save(slot: int, save_name: String):
var success = true
# Perform save in chunks to avoid blocking
for i in range(10): # Simulate chunked operation
await get_tree().process_frame
# Perform partial save operation
# Complete the save
var final_success = ArgodeSystem.save_game(slot, save_name)
save_completed.emit(final_success)
Save Data Compression
Reduce save file size with compression:
# CompressedSaveManager.gd
extends SaveLoadManager
func save_game_compressed(slot: int, save_name: String) -> bool:
var save_data = _collect_game_state()
save_data["save_name"] = save_name
save_data["save_date_string"] = Time.get_datetime_string_from_system()
save_data["save_time"] = Time.get_unix_time_from_system()
save_data["slot"] = slot
save_data["version"] = SAVE_VERSION
var json_string = JSON.stringify(save_data)
var compressed_data = json_string.to_utf8_buffer().compress(FileAccess.COMPRESSION_GZIP)
var file_path = SAVE_FOLDER + "slot_" + str(slot) + ".save"
var file = FileAccess.open(file_path, FileAccess.WRITE)
if file == null:
push_error("Failed to create save file: " + file_path)
return false
file.store_32(compressed_data.size()) # Store original size
file.store_buffer(compressed_data)
file.close()
return true
func load_game_compressed(slot: int) -> bool:
var file_path = SAVE_FOLDER + "slot_" + str(slot) + ".save"
if not FileAccess.file_exists(file_path):
return false
var file = FileAccess.open(file_path, FileAccess.READ)
if file == null:
return false
var original_size = file.get_32()
var compressed_data = file.get_buffer(file.get_length() - 4)
file.close()
var decompressed_data = compressed_data.decompress(original_size, FileAccess.COMPRESSION_GZIP)
var json_string = decompressed_data.get_string_from_utf8()
var json = JSON.new()
if json.parse(json_string) != OK:
return false
var save_data = json.data
return _restore_game_state(save_data)
These examples demonstrate various aspects of implementing a robust save/load system with the Argode framework, from basic functionality to advanced features like error recovery and performance optimization.