diff --git a/Autoloads/SaveSystem.cs b/Autoloads/SaveSystem.cs
index 56afb00..e48c031 100644
--- a/Autoloads/SaveSystem.cs
+++ b/Autoloads/SaveSystem.cs
@@ -1,16 +1,18 @@
+using System.Collections.Generic;
using System.Text.Json;
using Godot;
+using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
///
-/// Save system that serializes POCOs directly to JSON.
+/// Save system that serializes state to JSON using DTOs.
///
public partial class SaveSystem : Node
{
[Export] public string SavePath { get; set; } = "user://savegame.json";
- [Export] public int Version { get; set; } = 2; // Bumped version for new format
+ [Export] public int Version { get; set; } = 2;
private static readonly JsonSerializerOptions JsonOptions = new()
{
@@ -18,11 +20,6 @@ public partial class SaveSystem : Node
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
- public override void _Ready()
- {
- // No longer needs GameManager reference - works with GameStateStore directly
- }
-
public void SaveGame()
{
var store = GameStateStore.Instance;
@@ -32,11 +29,18 @@ public partial class SaveSystem : Node
return;
}
- var saveData = new SaveData
+ // Convert to DTO (only serializable data)
+ var saveData = new SaveDataDto
{
Version = Version,
- Player = store.Player,
- CurrentLevel = store.Session.CurrentLevel
+ Coins = store.Player.Coins,
+ Lives = store.Player.Lives,
+ CurrentLevel = store.Session.CurrentLevel,
+ CompletedLevels = [.. store.Player.CompletedLevels],
+ UnlockedLevels = new List(store.Player.UnlockedLevels),
+ UnlockedSkillNames = GetSkillNames(store.Player.UnlockedSkills),
+ UnlockedAchievements = new List(store.Player.UnlockedAchievements),
+ Statistics = new Dictionary(store.Player.Statistics)
};
try
@@ -65,7 +69,7 @@ public partial class SaveSystem : Node
{
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
var json = file.GetAsText();
- var saveData = JsonSerializer.Deserialize(json, JsonOptions);
+ var saveData = JsonSerializer.Deserialize(json, JsonOptions);
if (saveData == null)
{
@@ -87,9 +91,18 @@ public partial class SaveSystem : Node
}
// Apply loaded state
- store.Player = saveData.Player ?? new PlayerState();
+ store.Player.Coins = saveData.Coins;
+ store.Player.Lives = saveData.Lives;
store.Session.CurrentLevel = saveData.CurrentLevel;
+ store.Player.CompletedLevels = saveData.CompletedLevels ?? new List();
+ store.Player.UnlockedLevels = saveData.UnlockedLevels ?? new List { 0 };
+ store.Player.UnlockedAchievements = saveData.UnlockedAchievements ?? new List();
+ store.Player.Statistics = saveData.Statistics ?? new Dictionary();
+
+ // Reload skills by name from SkillManager
+ store.Player.UnlockedSkills = LoadSkillsByName(saveData.UnlockedSkillNames);
+
GD.Print("Game loaded from: ", SavePath);
return true;
}
@@ -100,6 +113,44 @@ public partial class SaveSystem : Node
}
}
+ private static List GetSkillNames(List skills)
+ {
+ var names = new List();
+ foreach (var skill in skills)
+ {
+ if (skill != null)
+ names.Add(skill.Name);
+ }
+ return names;
+ }
+
+ private List LoadSkillsByName(List skillNames)
+ {
+ var skills = new List();
+ if (skillNames == null) return skills;
+
+ var skillManager = GetNodeOrNull(Constants.SkillManagerPath);
+ if (skillManager == null)
+ {
+ GD.PrintErr("SaveSystem: SkillManager not available to resolve skill names.");
+ return skills;
+ }
+
+ foreach (var name in skillNames)
+ {
+ var skill = skillManager.GetSkillByName(name);
+ if (skill != null)
+ {
+ skills.Add(skill);
+ }
+ else
+ {
+ GD.PrintErr($"SaveSystem: Skill '{name}' not found in SkillManager.");
+ }
+ }
+ return skills;
+ }
+
public bool CheckSaveExists() => FileAccess.FileExists(SavePath);
public void DeleteSave()
@@ -113,11 +164,17 @@ public partial class SaveSystem : Node
}
///
-/// Container for save data.
+/// Serializable DTO for save data - no Godot types.
///
-public class SaveData
+public class SaveDataDto
{
public int Version { get; set; }
- public PlayerState Player { get; set; }
+ public int Coins { get; set; }
+ public int Lives { get; set; }
public int CurrentLevel { get; set; }
+ public List CompletedLevels { get; set; }
+ public List UnlockedLevels { get; set; }
+ public List UnlockedSkillNames { get; set; }
+ public List UnlockedAchievements { get; set; }
+ public Dictionary Statistics { get; set; }
}
\ No newline at end of file
diff --git a/scripts/components/PlayerDeathComponent.cs b/scripts/components/PlayerDeathComponent.cs
index 5f8eb7b..1482530 100644
--- a/scripts/components/PlayerDeathComponent.cs
+++ b/scripts/components/PlayerDeathComponent.cs
@@ -32,7 +32,7 @@ public partial class PlayerDeathComponent : Node2D
effect.Scale = EffectScale;
}
- _gameManager.RemoveLives(1);
- _gameManager.ResetCurrentSessionState();
+ // Lives are now decremented by LivesStateHandler via PlayerDied event
+ // Session state is reset by LivesStateHandler as well
}
}
\ No newline at end of file