Files
RuinAdventurer/Scripts/Core/SaveGameManager.cs
T

358 lines
12 KiB
C#

using Godot;
using System.Collections.Generic;
using System.Text.Json;
public static class SaveGameManager
{
private const string SaveDirectory = "user://savegame";
private const string GameDataPath = SaveDirectory + "/gamedata.json";
private const string RobotsPath = SaveDirectory + "/robots.json";
private const string ResearchPath = SaveDirectory + "/research.json";
private const string LayerPrefix = SaveDirectory + "/layer_";
private const string JsonExtension = ".json";
private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{
WriteIndented = true
};
public static bool SaveExists()
{
return FileAccess.FileExists(GameDataPath);
}
public static void SaveGame()
{
SaveGameData saveGame = CreateSaveData();
CreateSaveDirectory();
ClearOldLayerFiles();
SaveJson(GameDataPath, CreateCoreSaveData(saveGame));
SaveJson(RobotsPath, saveGame.Robots);
SaveJson(ResearchPath, saveGame.Research);
foreach (LayerSaveData layer in saveGame.Layers)
{
SaveJson(GetLayerPath(layer.Level), layer);
}
}
public static SaveGameData LoadSaveData()
{
if (!SaveExists()) return null;
SaveGameData saveGame = LoadJson<SaveGameData>(GameDataPath);
if (saveGame == null) return null;
saveGame.Robots = LoadJson<List<RobotSaveData>>(RobotsPath) ?? new List<RobotSaveData>();
saveGame.Research = LoadJson<List<ResearchSaveData>>(ResearchPath) ?? new List<ResearchSaveData>();
saveGame.Layers = LoadLayerSaveData();
return saveGame;
}
public static SaveGameData CreateSaveData()
{
return new SaveGameData
{
Seed = GameData.seed,
CurrentLayer = GameData.currentLayer,
VisibleLayer = GameData.visibleLayer,
LowestLayer = GameData.lowestLayer,
MaxRobotCount = GameData.maxRobotCount,
CanMove = GameData.canMove,
Survival = CreateSurvivalSaveData(),
Inventory = CreateInventorySaveData(),
Research = CreateResearchSaveData(),
Layers = CreateLayerSaveData(),
Robots = CreateRobotSaveData()
};
}
public static void ApplyWorldData(SaveGameData saveGame)
{
if (saveGame == null) return;
GameData.seed = saveGame.Seed;
GameData.currentLayer = saveGame.CurrentLayer;
GameData.visibleLayer = saveGame.VisibleLayer;
GameData.lowestLayer = saveGame.LowestLayer;
GameData.maxRobotCount = saveGame.MaxRobotCount;
GameData.canMove = saveGame.CanMove;
ApplySurvivalData(saveGame.Survival);
ApplyInventoryData(saveGame.Inventory);
ApplyResearchData(saveGame.Research);
ApplyLayerData(saveGame.Layers);
}
private static SurvivalSaveData CreateSurvivalSaveData()
{
return new SurvivalSaveData
{
Hunger = GameData.survival.hunger,
Thirst = GameData.survival.thirst,
Energy = GameData.survival.energy,
IsDead = GameData.survival.isDead,
DeathReason = GameData.survival.deathReason,
CurrentStatus = GameData.survival.currentStatus
};
}
private static List<ItemSaveData> CreateInventorySaveData()
{
List<ItemSaveData> result = new List<ItemSaveData>();
foreach (Item item in GameData.inventory.items)
{
result.Add(new ItemSaveData
{
Id = item.data.Id,
Amount = item.currentAmount
});
}
return result;
}
private static List<ResearchSaveData> CreateResearchSaveData()
{
List<ResearchSaveData> result = new List<ResearchSaveData>();
foreach (Research research in GameData.availableResearch.Values)
{
result.Add(new ResearchSaveData
{
Id = research.data.Id,
State = research.state,
ElapsedResearchTime = research.elapsedResearchTime,
PaidResources = research.paidResources
});
}
return result;
}
private static List<LayerSaveData> CreateLayerSaveData()
{
List<LayerSaveData> result = new List<LayerSaveData>();
if (GameData.map == null) return result;
foreach (Layer layer in GameData.map)
{
if (layer == null) continue;
result.Add(new LayerSaveData
{
Level = layer.level,
IsGateOpen = layer.isGateOpen,
HasContentGenerated = layer.hasContentGenerated,
GateIngredients = new List<Ingredient>(layer.gateIngredients),
CurrentResources = new List<string>(layer.currentResources),
Tiles = CreateTileSaveData(layer)
});
}
return result;
}
private static List<TileSaveData> CreateTileSaveData(Layer layer)
{
List<TileSaveData> result = new List<TileSaveData>();
foreach (Tile tile in layer.tiles)
{
result.Add(new TileSaveData
{
X = tile.GridPosition.X,
Y = tile.GridPosition.Y,
CollapsedMesh = tile.collapsedMesh,
ContainsLight = tile.containsLight,
ContainsDecoration = tile.containsDecoration,
ContainsResource = tile.containsResource,
WasVisited = tile.wasVisited,
Resource = tile.resource == null ? null : tile.resource.CreateSaveData()
});
}
return result;
}
private static List<RobotSaveData> CreateRobotSaveData()
{
List<RobotSaveData> result = new List<RobotSaveData>();
foreach (Robot robot in GameData.robots)
{
result.Add(robot.CreateSaveData());
}
return result;
}
private static SaveGameData CreateCoreSaveData(SaveGameData saveGame)
{
return new SaveGameData
{
Seed = saveGame.Seed,
CurrentLayer = saveGame.CurrentLayer,
VisibleLayer = saveGame.VisibleLayer,
LowestLayer = saveGame.LowestLayer,
MaxRobotCount = saveGame.MaxRobotCount,
CanMove = saveGame.CanMove,
Survival = saveGame.Survival,
Inventory = saveGame.Inventory,
Research = new List<ResearchSaveData>(),
Layers = new List<LayerSaveData>(),
Robots = new List<RobotSaveData>()
};
}
private static List<LayerSaveData> LoadLayerSaveData()
{
List<LayerSaveData> result = new List<LayerSaveData>();
for (int i = 0; i < GameData.ruinSize; i++)
{
string path = GetLayerPath(i);
if (!FileAccess.FileExists(path)) continue;
LayerSaveData layer = LoadJson<LayerSaveData>(path);
if (layer != null)
{
result.Add(layer);
}
}
return result;
}
private static string GetLayerPath(int level)
{
return LayerPrefix + level + JsonExtension;
}
private static void CreateSaveDirectory()
{
DirAccess.MakeDirRecursiveAbsolute(SaveDirectory);
}
private static void ClearOldLayerFiles()
{
DirAccess directory = DirAccess.Open(SaveDirectory);
if (directory == null) return;
directory.ListDirBegin();
while (true)
{
string fileName = directory.GetNext();
if (fileName == "") break;
if (directory.CurrentIsDir()) continue;
if (!fileName.StartsWith("layer_") || !fileName.EndsWith(JsonExtension)) continue;
directory.Remove(fileName);
}
directory.ListDirEnd();
}
private static void SaveJson<T>(string path, T data)
{
string json = JsonSerializer.Serialize(data, JsonOptions);
FileAccess file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
file.StoreString(json);
file.Flush();
}
private static T LoadJson<T>(string path)
{
if (!FileAccess.FileExists(path)) return default;
FileAccess file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
string json = file.GetAsText();
return JsonSerializer.Deserialize<T>(json);
}
private static void ApplySurvivalData(SurvivalSaveData survival)
{
if (survival == null) return;
GameData.survival.hunger = survival.Hunger;
GameData.survival.thirst = survival.Thirst;
GameData.survival.energy = survival.Energy;
GameData.survival.isDead = survival.IsDead;
GameData.survival.deathReason = survival.DeathReason ?? "";
GameData.survival.currentStatus = survival.CurrentStatus ?? "";
}
private static void ApplyInventoryData(List<ItemSaveData> savedItems)
{
GameData.inventory = new Inventory();
if (savedItems == null) return;
foreach (ItemSaveData savedItem in savedItems)
{
if (!GameData.availableItems.ContainsKey(savedItem.Id)) continue;
GameData.inventory.AddItem(
new Item { data = GameData.availableItems[savedItem.Id] },
savedItem.Amount
);
}
}
private static void ApplyResearchData(List<ResearchSaveData> savedResearch)
{
if (savedResearch == null) return;
foreach (ResearchSaveData savedState in savedResearch)
{
if (!GameData.availableResearch.ContainsKey(savedState.Id)) continue;
Research research = GameData.availableResearch[savedState.Id];
research.state = savedState.State;
research.elapsedResearchTime = savedState.ElapsedResearchTime;
research.paidResources = savedState.PaidResources;
}
GameData.RebuildRobotStatsFromResearch();
}
private static void ApplyLayerData(List<LayerSaveData> savedLayers)
{
if (savedLayers == null || GameData.map == null) return;
foreach (LayerSaveData savedLayer in savedLayers)
{
if (savedLayer.Level < 0 || savedLayer.Level >= GameData.map.Length) continue;
Layer layer = GameData.map[savedLayer.Level];
layer.isGateOpen = savedLayer.IsGateOpen;
layer.hasContentGenerated = savedLayer.HasContentGenerated;
layer.gateIngredients = savedLayer.GateIngredients ?? new List<Ingredient>();
layer.currentResources = savedLayer.CurrentResources ?? new List<string>();
ApplyTileData(layer, savedLayer.Tiles);
}
}
private static void ApplyTileData(Layer layer, List<TileSaveData> savedTiles)
{
if (savedTiles == null) return;
foreach (TileSaveData savedTile in savedTiles)
{
if (savedTile.X < 0 || savedTile.X >= GameData.layerSize) continue;
if (savedTile.Y < 0 || savedTile.Y >= GameData.layerSize) continue;
Tile tile = layer.tiles[savedTile.X, savedTile.Y];
tile.collapsedMesh = savedTile.CollapsedMesh;
tile.containsLight = savedTile.ContainsLight;
tile.containsDecoration = savedTile.ContainsDecoration;
tile.containsResource = savedTile.ContainsResource;
tile.wasVisited = savedTile.WasVisited;
tile.resource = savedTile.Resource == null ? null : GameResource.FromSaveData(savedTile.Resource);
tile.ContentNode.Visible = savedTile.WasVisited || tile.collapsedMesh == "gate";
}
}
}