360 lines
12 KiB
C#
360 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,
|
|
ElapsedSeconds = GameData.survival.elapsedSeconds
|
|
};
|
|
}
|
|
|
|
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 ?? "";
|
|
GameData.survival.elapsedSeconds = survival.ElapsedSeconds;
|
|
}
|
|
|
|
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";
|
|
}
|
|
}
|
|
}
|