Implement core game functionality with AppRoot, SaveClient, PlayerRepository, and LevelRepository classes
This commit is contained in:
103
data/SaveClient.cs
Normal file
103
data/SaveClient.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
|
||||
namespace Mr.BrickAdventures.data;
|
||||
|
||||
public sealed class SaveClient
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly int _version;
|
||||
|
||||
public SaveClient(string path, int version) { _path = path; _version = version; }
|
||||
|
||||
public bool Exists() => FileAccess.FileExists(_path);
|
||||
|
||||
public bool TryLoad(out PlayerState player, out LevelState level) {
|
||||
player = null!; level = null!;
|
||||
if (!Exists()) return false;
|
||||
|
||||
using var f = FileAccess.Open(_path, FileAccess.ModeFlags.Read);
|
||||
var dict = (Dictionary)f.GetVar();
|
||||
|
||||
if ((int)dict.GetValueOrDefault("version", -1) != _version) return false;
|
||||
|
||||
player = ToPlayer((Dictionary)dict["player"]);
|
||||
level = ToLevel((Dictionary)dict["level"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Strict load: requires version + player_state + level_state.
|
||||
// If anything is off, delete the file and act as "no save".
|
||||
public bool TryLoadStrict(out PlayerState player, out LevelState level) {
|
||||
player = default!;
|
||||
level = default!;
|
||||
|
||||
if (!Exists()) return false;
|
||||
|
||||
try {
|
||||
using var f = FileAccess.Open(_path, FileAccess.ModeFlags.Read);
|
||||
if (f == null) { Delete(); return false; }
|
||||
|
||||
var dictionary = (Dictionary)f.GetVar();
|
||||
|
||||
|
||||
if (!dictionary.TryGetValue("version", out var v) || (int)v != _version) { Delete(); return false; }
|
||||
|
||||
if (!dictionary.TryGetValue("player_state", out var pObj) || (Dictionary)pObj is not { } p) { Delete(); return false; }
|
||||
if (!dictionary.TryGetValue("level_state", out var lObj) || (Dictionary)lObj is not { } l) { Delete(); return false; }
|
||||
|
||||
player = ToPlayer(p);
|
||||
level = ToLevel(l);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
GD.PushWarning($"SaveClient: load failed — deleting bad save. {e.GetType().Name}: {e.Message}");
|
||||
Delete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(PlayerState player, LevelState level) {
|
||||
using var f = FileAccess.Open(_path, FileAccess.ModeFlags.Write);
|
||||
var dict = new Dictionary {
|
||||
{ "version", _version },
|
||||
{ "player", FromPlayer(player) },
|
||||
{ "level", FromLevel(level) }
|
||||
};
|
||||
f.StoreVar(dict);
|
||||
}
|
||||
|
||||
public void Delete() {
|
||||
if (!Exists()) return;
|
||||
var abs = ProjectSettings.GlobalizePath(_path);
|
||||
var ok = DirAccess.RemoveAbsolute(abs);
|
||||
if (ok != Error.Ok) GD.PushWarning($"SaveClient: failed to delete {_path}: {ok}");
|
||||
}
|
||||
|
||||
private static Dictionary FromPlayer(PlayerState s) => new() { { "coins", s.Coins }, { "lives", s.Lives } };
|
||||
private static PlayerState ToPlayer(Dictionary d) => new() {
|
||||
Coins = d.TryGetValue("coins", out var c) ? (int)c : 0,
|
||||
Lives = d.TryGetValue("lives", out var l) ? (int)l : 3
|
||||
};
|
||||
|
||||
private static Dictionary FromLevel(LevelState s) => new() {
|
||||
{ "current", s.Current },
|
||||
{ "unlocked", new Array<int>(s.Unlocked) },
|
||||
{ "completed", new Array<int>(s.Completed) },
|
||||
};
|
||||
|
||||
private static LevelState ToLevel(Dictionary d) => new() {
|
||||
Current = d.TryGetValue("current", out var cur) ? (int)cur : 0,
|
||||
Unlocked = d.TryGetValue("unlocked", out var ul) && (Array<int>)ul is { } a1 ? a1.ToArray() : [0],
|
||||
Completed = d.TryGetValue("completed", out var cl) && (Array<int>)cl is { } a2 ? a2.ToArray() : [],
|
||||
};
|
||||
}
|
||||
|
||||
public record SaveSnapshot {
|
||||
public required PlayerState PlayerState { get; init; }
|
||||
public required LevelState LevelState { get; init; }
|
||||
}
|
Reference in New Issue
Block a user