Added testing and save/load mechanic to the game. Game is now entering final phase.
This commit is contained in:
@@ -23,6 +23,7 @@ public partial class GameData
|
||||
public static SurvivalState survival = new SurvivalState();
|
||||
public static RobotStats robotStats = new RobotStats();
|
||||
public static Dictionary<int, List<Ingredient>> gateUnlocks;
|
||||
public static bool loadSaveOnStart = false;
|
||||
|
||||
public static Color primaryColor = new Color("#276ac2");
|
||||
public static Color lightColor = new Color("#7efff5");
|
||||
@@ -32,4 +33,36 @@ public partial class GameData
|
||||
public static int seed = 12345;
|
||||
|
||||
public static Inventory inventory = new Inventory();
|
||||
|
||||
public static void ResetRunState()
|
||||
{
|
||||
seed = 12345;
|
||||
ruinSize = 10;
|
||||
layerSize = 20;
|
||||
rand = new Random(seed);
|
||||
survival = new SurvivalState();
|
||||
robotStats = new RobotStats();
|
||||
inventory = new Inventory();
|
||||
availableResearch = ResourceLoader.LoadResearch();
|
||||
robots.Clear();
|
||||
currentLayer = 0;
|
||||
visibleLayer = 0;
|
||||
lowestLayer = 0;
|
||||
maxRobotCount = 10;
|
||||
canMove = true;
|
||||
}
|
||||
|
||||
public static void RebuildRobotStatsFromResearch()
|
||||
{
|
||||
robotStats = new RobotStats();
|
||||
maxRobotCount = 10;
|
||||
|
||||
foreach (Research research in availableResearch.Values)
|
||||
{
|
||||
if (research.state == ResearchState.RESEARCHED)
|
||||
{
|
||||
robotStats.Apply(research.data.Effects);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,14 +99,14 @@ public partial class ResourceLoader
|
||||
{
|
||||
Dictionary<string, float[]> weights = new Dictionary<string, float[]>()
|
||||
{
|
||||
{ "iron_ore", [0.05f,1] },
|
||||
{ "tin_ore", [0.3f,0.7f] },
|
||||
{ "copper_ore", [0.3f,0.7f] },
|
||||
{ "mushroom", [0.3f,0.1f] },
|
||||
{ "spider_silk", [0.8f,0.4f] },
|
||||
{ "coal", [1,0.3f] },
|
||||
{ "water", [0.4f,0.2f] },
|
||||
{ "stone", [1,0.5f] },
|
||||
{ "iron_ore", new float[] { 0.05f, 1f } },
|
||||
{ "tin_ore", new float[] { 0.3f, 0.7f } },
|
||||
{ "copper_ore", new float[] { 0.3f, 0.7f } },
|
||||
{ "mushroom", new float[] { 0.3f, 0.1f } },
|
||||
{ "spider_silk", new float[] { 0.8f, 0.4f } },
|
||||
{ "coal", new float[] { 1f, 0.3f } },
|
||||
{ "water", new float[] { 0.4f, 0.2f } },
|
||||
{ "stone", new float[] { 1f, 0.5f } },
|
||||
};
|
||||
return weights;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class SaveGameData
|
||||
{
|
||||
public int Seed { get; set; }
|
||||
public int CurrentLayer { get; set; }
|
||||
public int VisibleLayer { get; set; }
|
||||
public int LowestLayer { get; set; }
|
||||
public int MaxRobotCount { get; set; }
|
||||
public bool CanMove { get; set; }
|
||||
public SurvivalSaveData Survival { get; set; }
|
||||
public List<ItemSaveData> Inventory { get; set; }
|
||||
public List<ResearchSaveData> Research { get; set; }
|
||||
public List<LayerSaveData> Layers { get; set; }
|
||||
public List<RobotSaveData> Robots { get; set; }
|
||||
}
|
||||
|
||||
public class SurvivalSaveData
|
||||
{
|
||||
public float Hunger { get; set; }
|
||||
public float Thirst { get; set; }
|
||||
public float Energy { get; set; }
|
||||
public bool IsDead { get; set; }
|
||||
public string DeathReason { get; set; }
|
||||
public string CurrentStatus { get; set; }
|
||||
}
|
||||
|
||||
public class ItemSaveData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public int Amount { get; set; }
|
||||
}
|
||||
|
||||
public class ResearchSaveData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public ResearchState State { get; set; }
|
||||
public double ElapsedResearchTime { get; set; }
|
||||
public bool PaidResources { get; set; }
|
||||
}
|
||||
|
||||
public class LayerSaveData
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public bool IsGateOpen { get; set; }
|
||||
public bool HasContentGenerated { get; set; }
|
||||
public List<Ingredient> GateIngredients { get; set; }
|
||||
public List<string> CurrentResources { get; set; }
|
||||
public List<TileSaveData> Tiles { get; set; }
|
||||
}
|
||||
|
||||
public class TileSaveData
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public string CollapsedMesh { get; set; }
|
||||
public bool ContainsLight { get; set; }
|
||||
public bool ContainsDecoration { get; set; }
|
||||
public bool ContainsResource { get; set; }
|
||||
public bool WasVisited { get; set; }
|
||||
public ResourceSaveData Resource { get; set; }
|
||||
}
|
||||
|
||||
public class ResourceSaveData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int CurrentAmount { get; set; }
|
||||
public int MaxAmount { get; set; }
|
||||
public bool IsEndless { get; set; }
|
||||
public float ExtractionSpeed { get; set; }
|
||||
public double TimeSinceLastExtraction { get; set; }
|
||||
}
|
||||
|
||||
public class RobotSaveData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string CurrentProgram { get; set; }
|
||||
public string CurrentMessage { get; set; }
|
||||
public string RobotType { get; set; }
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float Z { get; set; }
|
||||
public float Heat { get; set; }
|
||||
public float Maintenance { get; set; }
|
||||
public bool IsCoolingDown { get; set; }
|
||||
public bool IsBroken { get; set; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://k530yuk4xt1x
|
||||
@@ -0,0 +1,357 @@
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://quury78jfutk
|
||||
Reference in New Issue
Block a user