2025-04-22 12:08:09 -06:00
|
|
|
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:
|
2025-04-22 22:10:08 -06:00
|
|
|
if level_path != world.current_level_scene.resource_path:
|
2025-04-22 12:08:09 -06:00
|
|
|
push_warning(
|
|
|
|
"This save is for ",
|
|
|
|
level_path,
|
|
|
|
" but the loaded level is for ",
|
2025-04-22 22:10:08 -06:00
|
|
|
world.current_level_scene.resource_path
|
2025-04-22 12:08:09 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
2025-04-22 22:10:08 -06:00
|
|
|
save.level_path = world.current_level_scene.resource_path
|
2025-04-22 12:08:09 -06:00
|
|
|
|
|
|
|
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
|