grunk/src/world/save_state.gd

102 lines
3.1 KiB
GDScript

class_name SaveState extends Resource
## Serializable container for gameplay state.
const CURRENT_VERSION := 1
const PERSISTENT_GROUP := "Persistent"
const SERIALIZE_METHOD := "serialize"
const DESERIALIZE_METHOD := "deserialize"
@export var persistent_state: Dictionary[String, Dictionary] = {}
@export_group("Compatibility Data")
@export var save_version := CURRENT_VERSION
@export var level_path: String
@export var level_version := -1
@export_group("WorldManager State")
@export var grunk_tank_limit: int
@export var mp3_player_unlocked: bool
@export var toothbrush_unlocked: bool
@export var stickers_unlocked: bool
@export var grunk_tank: float
@export var grunk_vault: float
@export var alert_level: int
static func node_key(node: Node, world: World) -> String:
return str(world.level_root.get_path_to(node))
func load_to_world(world: World) -> void:
# Check save compatibility
if save_version != SaveState.CURRENT_VERSION:
push_warning(
"Save state version ",
save_version,
" is incompatible with the current game version ",
SaveState.CURRENT_VERSION
)
return
if level_path != world.current_level_scene.resource_path:
push_warning(
"Save state for level ",
level_path,
" is incompatible with the current level ",
world.current_level_scene.resource_path
)
return
if level_version != world.current_level.version:
push_warning(
"Save state for level version ",
level_version,
" is incompatible with the current level version ",
world.current_level.version
)
# Deserialize world state
world.manager.grunk_tank_limit = grunk_tank_limit
world.manager.mp3_player_unlocked = mp3_player_unlocked
world.manager.toothbrush_unlocked = toothbrush_unlocked
world.manager.stickers_unlocked = stickers_unlocked
world.manager.grunk_tank = grunk_tank
world.manager.grunk_vault = grunk_vault
world.manager.alert_level = alert_level
# Deserialize persistent level nodes
for node: Node in world.get_tree().get_nodes_in_group(PERSISTENT_GROUP):
var key := SaveState.node_key(node, world)
if key in persistent_state:
# Node is in our persistent state, so load it with data.
Callable(node, DESERIALIZE_METHOD).call(persistent_state[key])
else:
# Node isn't in our persistent state, so it must have been destroyed.
node.queue_free()
static func serialize(world: World) -> SaveState:
var save := SaveState.new()
# Serialize compatibility data
save.level_path = world.current_level_scene.resource_path
save.level_version = world.current_level.version
# Serialize world state
save.grunk_tank_limit = world.manager.grunk_tank_limit
save.mp3_player_unlocked = world.manager.mp3_player_unlocked
save.toothbrush_unlocked = world.manager.toothbrush_unlocked
save.stickers_unlocked = world.manager.stickers_unlocked
save.grunk_tank = world.manager.grunk_tank
save.grunk_vault = world.manager.grunk_vault
save.alert_level = world.manager.alert_level
# Serialize persistent level nodes
for node: Node in world.get_tree().get_nodes_in_group(PERSISTENT_GROUP):
var key := SaveState.node_key(node, world)
var data: Dictionary = Callable(node, SERIALIZE_METHOD).call()
save.persistent_state[key] = data
return save