Files
dauphincraft/scripts/progression/SaveManager.gd

132 lines
3.5 KiB
GDScript

extends Node
## SaveManager — autoload singleton
## Sauvegarde automatique (toutes les 30s + à la mort).
## Persiste : XP, niveau, inventaire, achievements débloqués, position spawn.
const SAVE_PATH := "user://save.json"
const AUTOSAVE_INTERVAL: float = 30.0
var _autosave_timer: float = 0.0
var _player: Node = null
func _ready() -> void:
# Connect mort du joueur dès que le nœud player existe
# (on le cherche au premier process car le joueur est spawné après)
pass
func register_player(player: Node) -> void:
_player = player
if player.has_signal("player_died"):
if not player.player_died.is_connected(_on_player_died):
player.player_died.connect(_on_player_died)
func _process(delta: float) -> void:
_autosave_timer += delta
if _autosave_timer >= AUTOSAVE_INTERVAL:
_autosave_timer = 0.0
save_game()
func save_game() -> void:
var data: Dictionary = {}
# --- PlayerProgress ---
var pp: Node = get_node_or_null("/root/PlayerProgress")
if pp != null:
data["xp"] = pp.current_xp
data["level"] = pp.level
# --- Inventory ---
var main: Node = get_tree().get_first_node_in_group("main")
if main != null and main.get("inventory") != null:
var inv: Node = main.inventory
var slots: Array = []
if inv.has_method("get_all_slots"):
slots = inv.call("get_all_slots")
else:
# Fallback: read slots array directly if exposed
if "slots" in inv:
for s in inv.slots:
slots.append(s)
data["inventory"] = slots
# --- Achievements ---
var am: Node = get_node_or_null("/root/AchievementManager")
if am != null and am.get("_unlocked") != null:
data["achievements"] = am._unlocked
# --- Spawn position ---
if is_instance_valid(_player):
var pos: Vector3 = _player.global_position
data["spawn"] = {"x": pos.x, "y": pos.y, "z": pos.z}
# Write JSON
var file := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
if file == null:
push_warning("SaveManager: impossible d'ouvrir " + SAVE_PATH)
return
file.store_string(JSON.stringify(data, "\t"))
file.close()
func load_game() -> bool:
if not FileAccess.file_exists(SAVE_PATH):
return false
var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
if file == null:
return false
var raw: String = file.get_as_text()
file.close()
var parsed = JSON.parse_string(raw)
if parsed == null or not (parsed is Dictionary):
push_warning("SaveManager: save corrompu")
return false
var data: Dictionary = parsed
# --- PlayerProgress ---
var pp: Node = get_node_or_null("/root/PlayerProgress")
if pp != null:
if data.has("xp"):
pp.current_xp = int(data["xp"])
if data.has("level"):
pp.level = int(data["level"])
pp.emit_state()
# --- Achievements ---
var am: Node = get_node_or_null("/root/AchievementManager")
if am != null and data.has("achievements"):
am._unlocked = data["achievements"]
# --- Inventory ---
var main: Node = get_tree().get_first_node_in_group("main")
if main != null and main.get("inventory") != null and data.has("inventory"):
var inv: Node = main.inventory
var slots: Array = data["inventory"]
if inv.has_method("load_slots"):
inv.call("load_slots", slots)
elif "slots" in inv:
# Best-effort direct assignment
for i: int in range(min(slots.size(), inv.slots.size())):
inv.slots[i] = slots[i]
# --- Spawn position ---
if is_instance_valid(_player) and data.has("spawn"):
var sp: Dictionary = data["spawn"]
_player.global_position = Vector3(
float(sp.get("x", 0.0)),
float(sp.get("y", 55.0)),
float(sp.get("z", 0.0))
)
return true
func _on_player_died() -> void:
save_game()