class_name SaveState extends Resource ## Serializable container for gameplay state. const CURRENT_VERSION := 0 const PERSISTENT_GROUP := "Persistent" const SERIALIZE_METHOD := "serialize" const DESERIALIZE_METHOD := "deserialize" @export var save_version := CURRENT_VERSION @export var level_path: String @export var persistent_state: Dictionary[String, Dictionary] = {} @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: if level_path != world.current_level_scene.resource_path: push_warning( "This save is for ", level_path, " but the loaded level is for ", world.current_level_scene.resource_path ) 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 var persistent := world.get_tree().get_nodes_in_group(PERSISTENT_GROUP) for node: Node in persistent: 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() save.level_path = world.current_level_scene.resource_path 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 # NOTE: I'm assuming that `persistent` will have the same order ever time the world is loaded. # This may not be the case. If so, we need to find a different way to uniquely identify nodes. var persistent := world.get_tree().get_nodes_in_group(PERSISTENT_GROUP) for node: Node in persistent: var key := SaveState.node_key(node, world) var data: Dictionary = Callable(node, SERIALIZE_METHOD).call() save.persistent_state[key] = data return save