- Implemented LocalizationManager for handling multiple languages. - Updated LoadGameMenu, MainMenu, PauseMenu, and SettingsMenu to support localization. - Added InstanceSelectionMenu for selecting game instances. - Refactored SaveManager to handle new SaveInstance structure. - Created SaveDialog for saving games with user-defined names. - Enhanced SaveData to manage multiple save instances. - Added error handling and logging for save/load operations. - Updated UI elements to reflect localized text.
290 lines
11 KiB
C#
290 lines
11 KiB
C#
using Godot;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
|
|
namespace TheGame
|
|
{
|
|
public class SaveData
|
|
{
|
|
public GameSeed GameSeed { get; set; }
|
|
public List<SaveInstance> Instances { get; set; } = new List<SaveInstance>();
|
|
|
|
// Backward compatibility
|
|
public float GameTime { get; set; }
|
|
public DateTime SaveDate { get; set; }
|
|
public string SaveName { get; set; }
|
|
public string DisplayName { get; set; } = "";
|
|
|
|
public SaveData()
|
|
{
|
|
GameSeed = new GameSeed();
|
|
}
|
|
|
|
public SaveData(GameSeed gameSeed)
|
|
{
|
|
GameSeed = gameSeed;
|
|
}
|
|
|
|
public SaveInstance GetLatestInstance()
|
|
{
|
|
if (Instances.Count == 0) return null;
|
|
return Instances.OrderByDescending(i => i.SaveDate).First();
|
|
}
|
|
|
|
public SaveInstance GetInstanceWithHighestGameTime()
|
|
{
|
|
if (Instances.Count == 0) return null;
|
|
return Instances.OrderByDescending(i => i.GameTime).First();
|
|
}
|
|
}
|
|
|
|
public class SaveManager
|
|
{
|
|
private readonly string _saveDirectory;
|
|
|
|
public SaveManager()
|
|
{
|
|
// Använd user data directory istället för executable directory
|
|
string userDataDir = OS.GetUserDataDir();
|
|
_saveDirectory = Path.Combine(userDataDir, "saves");
|
|
|
|
// Skapa saves mapp om den inte finns
|
|
if (!Directory.Exists(_saveDirectory))
|
|
{
|
|
Directory.CreateDirectory(_saveDirectory);
|
|
GD.Print($"Created saves directory: {_saveDirectory}");
|
|
}
|
|
else
|
|
{
|
|
GD.Print($"Using saves directory: {_saveDirectory}");
|
|
}
|
|
}
|
|
|
|
public bool SaveGame(float gameTime, string displayName = "", GameSeed gameSeed = null, bool overwriteLatest = true)
|
|
{
|
|
try
|
|
{
|
|
// Om ingen seed angiven, använd nuvarande eller skapa ny
|
|
if (gameSeed == null)
|
|
{
|
|
gameSeed = GameState.CurrentGameSeed ?? new GameSeed();
|
|
GameState.CurrentGameSeed = gameSeed;
|
|
}
|
|
|
|
// Sök efter befintlig SaveData för denna seed
|
|
var existingSaveData = FindSaveDataBySeed(gameSeed);
|
|
|
|
if (existingSaveData == null)
|
|
{
|
|
// Skapa ny SaveData för denna seed
|
|
existingSaveData = new SaveData(gameSeed);
|
|
}
|
|
|
|
// Skapa ny instans
|
|
var newInstance = new SaveInstance(gameTime, displayName);
|
|
|
|
if (overwriteLatest && existingSaveData.Instances.Count > 0)
|
|
{
|
|
// Skriv över den senaste instansen
|
|
var latestInstance = existingSaveData.GetLatestInstance();
|
|
existingSaveData.Instances.Remove(latestInstance);
|
|
}
|
|
|
|
existingSaveData.Instances.Add(newInstance);
|
|
|
|
// Spara till fil
|
|
bool saveSuccess = SaveSeedToFile(existingSaveData);
|
|
|
|
if (saveSuccess)
|
|
{
|
|
GD.Print($"Game saved - Seed: {gameSeed.SeedValue}, Instance: {newInstance.DisplayName}");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
GD.PrintErr($"Failed to save game - Seed: {gameSeed.SeedValue}");
|
|
return false;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Error saving game: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public SaveData FindSaveDataBySeed(GameSeed gameSeed)
|
|
{
|
|
string fileName = $"seed_{gameSeed.SeedValue}.json";
|
|
string filePath = Path.Combine(_saveDirectory, fileName);
|
|
|
|
if (File.Exists(filePath))
|
|
{
|
|
try
|
|
{
|
|
string jsonString = File.ReadAllText(filePath);
|
|
var saveData = JsonSerializer.Deserialize<SaveData>(jsonString);
|
|
return saveData;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Failed to load seed file {filePath}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private bool SaveSeedToFile(SaveData saveData)
|
|
{
|
|
string fileName = $"seed_{saveData.GameSeed.SeedValue}.json";
|
|
string filePath = Path.Combine(_saveDirectory, fileName);
|
|
|
|
try
|
|
{
|
|
string jsonString = JsonSerializer.Serialize(saveData, new JsonSerializerOptions
|
|
{
|
|
WriteIndented = true
|
|
});
|
|
File.WriteAllText(filePath, jsonString);
|
|
GD.Print($"Seed saved to: {filePath}");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Failed to save seed file: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public List<SaveData> GetSavedGames()
|
|
{
|
|
var savedGames = new List<SaveData>();
|
|
|
|
GD.Print($"Looking for saves in: {_saveDirectory}");
|
|
|
|
if (!Directory.Exists(_saveDirectory))
|
|
{
|
|
GD.Print("Save directory does not exist");
|
|
return savedGames;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Ladda både nya seed-filer och gamla format för backward compatibility
|
|
var allFiles = Directory.GetFiles(_saveDirectory, "*.json");
|
|
GD.Print($"Found {allFiles.Length} save files");
|
|
|
|
foreach (string filePath in allFiles)
|
|
{
|
|
GD.Print($"Processing save file: {filePath}");
|
|
try
|
|
{
|
|
string jsonString = File.ReadAllText(filePath);
|
|
|
|
if (Path.GetFileName(filePath).StartsWith("seed_"))
|
|
{
|
|
// Ny seed-baserad fil
|
|
var saveData = JsonSerializer.Deserialize<SaveData>(jsonString);
|
|
if (saveData != null && saveData.Instances.Count > 0)
|
|
{
|
|
savedGames.Add(saveData);
|
|
GD.Print($"Successfully loaded seed: {saveData.GameSeed.SeedValue} with {saveData.Instances.Count} instances");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Gammal fil - konvertera till nytt format
|
|
var oldSaveData = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);
|
|
if (oldSaveData != null)
|
|
{
|
|
var convertedSave = ConvertOldSaveFormat(oldSaveData);
|
|
if (convertedSave != null)
|
|
{
|
|
savedGames.Add(convertedSave);
|
|
GD.Print($"Converted old save format");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Failed to load save file {filePath}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
// Sortera efter senaste aktivitet
|
|
savedGames = savedGames.OrderByDescending(s => s.GetLatestInstance()?.SaveDate ?? DateTime.MinValue).ToList();
|
|
GD.Print($"Total loaded saves: {savedGames.Count}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Failed to read saves directory: {ex.Message}");
|
|
}
|
|
|
|
return savedGames;
|
|
}
|
|
|
|
private SaveData ConvertOldSaveFormat(Dictionary<string, object> oldData)
|
|
{
|
|
try
|
|
{
|
|
// Extrahera värden från gammal format
|
|
var gameTime = Convert.ToSingle(oldData.GetValueOrDefault("GameTime", 0f));
|
|
var saveDateStr = oldData.GetValueOrDefault("SaveDate", "")?.ToString();
|
|
var displayName = oldData.GetValueOrDefault("DisplayName", "")?.ToString();
|
|
|
|
if (DateTime.TryParse(saveDateStr, out DateTime saveDate))
|
|
{
|
|
var gameSeed = new GameSeed($"OLD_{DateTime.Now.Ticks % 100000000:X}", "Converted Save");
|
|
var saveData = new SaveData(gameSeed);
|
|
|
|
var instance = new SaveInstance(gameTime, displayName ?? "Converted Save");
|
|
instance.SaveDate = saveDate;
|
|
saveData.Instances.Add(instance);
|
|
|
|
return saveData;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Failed to convert old save format: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public bool LoadGame(SaveData saveData, SaveInstance instance = null)
|
|
{
|
|
try
|
|
{
|
|
// Om ingen specifik instans angiven, ta den med högst speltid
|
|
if (instance == null)
|
|
{
|
|
instance = saveData.GetInstanceWithHighestGameTime();
|
|
}
|
|
|
|
if (instance == null)
|
|
{
|
|
GD.PrintErr("No valid instance found to load");
|
|
return false;
|
|
}
|
|
|
|
GameState.LoadedGameTime = instance.GameTime;
|
|
GameState.CurrentGameSeed = saveData.GameSeed;
|
|
GameState.LoadedInstance = instance;
|
|
|
|
GD.Print($"Loaded game - Seed: {saveData.GameSeed.SeedValue}, Instance: {instance.DisplayName}, Time: {instance.GetFormattedGameTime()}");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
GD.PrintErr($"Failed to load game: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} |