This commit is contained in:
2026-01-31 16:31:36 +01:00
parent b62478bbea
commit 3f02f1d4ac
23 changed files with 711 additions and 258 deletions

View File

@@ -1,62 +1,123 @@
using System.Text.Json;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures;
using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Save system that serializes POCOs directly to JSON.
/// </summary>
public partial class SaveSystem : Node
{
[Export] public string SavePath { get; set; } = "user://savegame.save";
[Export] public int Version { get; set; } = 1;
[Export] public string SavePath { get; set; } = "user://savegame.json";
[Export] public int Version { get; set; } = 2; // Bumped version for new format
private GameManager _gameManager;
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public override void _Ready()
{
_gameManager = GetNode<GameManager>(Constants.GameManagerPath);
// No longer needs GameManager reference - works with GameStateStore directly
}
public void SaveGame()
{
var saveData = new Dictionary
var store = GameStateStore.Instance;
if (store == null)
{
{ "player_state", _gameManager.PlayerState},
{ "version", Version}
GD.PrintErr("SaveSystem: GameStateStore not available.");
return;
}
var saveData = new SaveData
{
Version = Version,
Player = store.Player,
CurrentLevel = store.Session.CurrentLevel
};
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreVar(saveData);
GD.Print("Game state saved to: ", SavePath);
try
{
var json = JsonSerializer.Serialize(saveData, JsonOptions);
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreString(json);
GD.Print("Game saved to: ", SavePath);
EventBus.EmitGameSaved();
}
catch (System.Exception e)
{
GD.PrintErr($"SaveSystem: Failed to save game: {e.Message}");
}
}
public bool LoadGame()
{
if (!FileAccess.FileExists(SavePath))
return false;
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
var saveDataObj = (Dictionary)file.GetVar();
if (saveDataObj.ContainsKey("version") && (int)saveDataObj["version"] != Version)
{
GD.Print($"Save file version mismatch. Expected: {Version}, Found: {saveDataObj["version"]}");
GD.Print("SaveSystem: No save file found.");
return false;
}
GD.Print("Game state loaded from: ", SavePath);
GD.Print("Player state: ", saveDataObj["player_state"]);
_gameManager.PlayerState = (Dictionary)saveDataObj["player_state"];
var skills = new Array<SkillData>();
foreach (var skill in (Array<SkillData>)_gameManager.PlayerState["unlocked_skills"])
try
{
skills.Add(skill);
}
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
var json = file.GetAsText();
var saveData = JsonSerializer.Deserialize<SaveData>(json, JsonOptions);
_gameManager.UnlockSkills(skills);
return true;
if (saveData == null)
{
GD.PrintErr("SaveSystem: Failed to deserialize save data.");
return false;
}
if (saveData.Version != Version)
{
GD.PrintErr($"SaveSystem: Version mismatch. Expected {Version}, found {saveData.Version}");
return false;
}
var store = GameStateStore.Instance;
if (store == null)
{
GD.PrintErr("SaveSystem: GameStateStore not available.");
return false;
}
// Apply loaded state
store.Player = saveData.Player ?? new PlayerState();
store.Session.CurrentLevel = saveData.CurrentLevel;
GD.Print("Game loaded from: ", SavePath);
return true;
}
catch (System.Exception e)
{
GD.PrintErr($"SaveSystem: Failed to load game: {e.Message}");
return false;
}
}
public bool CheckSaveExists() => FileAccess.FileExists(SavePath);
public void DeleteSave()
{
if (FileAccess.FileExists(SavePath))
{
DirAccess.RemoveAbsolute(ProjectSettings.GlobalizePath(SavePath));
GD.Print("Save file deleted.");
}
}
}
/// <summary>
/// Container for save data.
/// </summary>
public class SaveData
{
public int Version { get; set; }
public PlayerState Player { get; set; }
public int CurrentLevel { get; set; }
}