Finished first EA Version #1
@@ -427,6 +427,14 @@ text = "Continue"
|
||||
layout_mode = 2
|
||||
text = "Options"
|
||||
|
||||
[node name="SaveGame" type="Button" parent="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Save Game"
|
||||
|
||||
[node name="LoadGame" type="Button" parent="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Load Game"
|
||||
|
||||
[node name="Button3" type="Button" parent="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer" unique_id=2028306785]
|
||||
layout_mode = 2
|
||||
text = "Exit"
|
||||
@@ -600,6 +608,8 @@ texture_normal = ExtResource("12_3so38")
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/RobotList/VBoxContainer/Spawning/Button" to="CanvasLayer/UIHandler/MainUI/Content/RobotList" method="SpawnRobot"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer/Button" to="CanvasLayer/UIHandler" method="HandleMenu"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer/Button2" to="CanvasLayer/UIHandler" method="ShowOptions"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer/SaveGame" to="CanvasLayer/UIHandler" method="SaveGame"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer/LoadGame" to="CanvasLayer/UIHandler" method="LoadGame"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer/Button3" to="CanvasLayer/UIHandler" method="ExitGame"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/GameOver/VBoxContainer/HBoxContainer/Negative" to="CanvasLayer/UIHandler" method="ExitGame"]
|
||||
[connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/GameOver/VBoxContainer/HBoxContainer/Positive" to="CanvasLayer/UIHandler" method="HideGameOver"]
|
||||
|
||||
@@ -123,6 +123,12 @@ theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1")
|
||||
text = "Start Game"
|
||||
|
||||
[node name="btnLoad" type="Button" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1")
|
||||
text = "Load Game"
|
||||
|
||||
[node name="btnOptions" type="Button" parent="CenterContainer/VBoxContainer" unique_id=891656915]
|
||||
layout_mode = 2
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo")
|
||||
@@ -136,4 +142,5 @@ theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1")
|
||||
text = "Exit Game"
|
||||
|
||||
[connection signal="button_up" from="CenterContainer/VBoxContainer/btnPlay" to="." method="OnPlayPressed"]
|
||||
[connection signal="button_up" from="CenterContainer/VBoxContainer/btnLoad" to="." method="OnLoadPressed"]
|
||||
[connection signal="button_up" from="CenterContainer/VBoxContainer/btnExit" to="." method="OnQuitPressed"]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://Scripts/Tests/TestRunner.cs" id="1_test_runner"]
|
||||
|
||||
[node name="TestRunner" type="Node"]
|
||||
script = ExtResource("1_test_runner")
|
||||
@@ -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
|
||||
@@ -19,6 +19,30 @@ public class GameResource
|
||||
item = GameData.availableItems[name];
|
||||
}
|
||||
|
||||
public static GameResource FromSaveData(ResourceSaveData saveData)
|
||||
{
|
||||
GameResource resource = new GameResource(saveData.Name);
|
||||
resource.currentAmount = saveData.CurrentAmount;
|
||||
resource.maxAmount = saveData.MaxAmount;
|
||||
resource.isEndless = saveData.IsEndless;
|
||||
resource.extractionSpeed = saveData.ExtractionSpeed;
|
||||
resource.timeSinceLastExtraction = saveData.TimeSinceLastExtraction;
|
||||
return resource;
|
||||
}
|
||||
|
||||
public ResourceSaveData CreateSaveData()
|
||||
{
|
||||
return new ResourceSaveData
|
||||
{
|
||||
Name = name,
|
||||
CurrentAmount = currentAmount,
|
||||
MaxAmount = maxAmount,
|
||||
IsEndless = isEndless,
|
||||
ExtractionSpeed = extractionSpeed,
|
||||
TimeSinceLastExtraction = timeSinceLastExtraction
|
||||
};
|
||||
}
|
||||
|
||||
public bool Extract(double delta)
|
||||
{
|
||||
timeSinceLastExtraction += delta;
|
||||
|
||||
@@ -10,31 +10,49 @@ public class Inventory
|
||||
|
||||
public bool AddItem(Item item, int amount)
|
||||
{
|
||||
Item inventoryItem = items.Find(x => x.data.Id == item.data.Id && x.currentAmount + amount <= x.data.StackSize);
|
||||
if (inventoryItem != null)
|
||||
if (GetFreeCapacity(item.data.Id, item.data.StackSize) < amount)
|
||||
{
|
||||
inventoryItem.currentAmount += amount;
|
||||
NotifyInventoryChanged();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (items.Count < maxInventorySize * GameData.maxRobotCount)
|
||||
int remainingAmount = amount;
|
||||
|
||||
foreach (Item inventoryItem in items)
|
||||
{
|
||||
items.Add(item);
|
||||
items[items.Count - 1].currentAmount += amount;
|
||||
NotifyInventoryChanged();
|
||||
return true;
|
||||
if (inventoryItem.data.Id != item.data.Id) continue;
|
||||
if (inventoryItem.currentAmount >= inventoryItem.data.StackSize) continue;
|
||||
|
||||
int amountToAdd = Math.Min(
|
||||
remainingAmount,
|
||||
inventoryItem.data.StackSize - inventoryItem.currentAmount
|
||||
);
|
||||
|
||||
inventoryItem.currentAmount += amountToAdd;
|
||||
remainingAmount -= amountToAdd;
|
||||
|
||||
if (remainingAmount <= 0)
|
||||
{
|
||||
NotifyInventoryChanged();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
while (remainingAmount > 0 && items.Count < maxInventorySize * GameData.maxRobotCount)
|
||||
{
|
||||
int amountToAdd = Math.Min(remainingAmount, item.data.StackSize);
|
||||
items.Add(new Item { data = item.data, currentAmount = amountToAdd });
|
||||
remainingAmount -= amountToAdd;
|
||||
}
|
||||
|
||||
NotifyInventoryChanged();
|
||||
return remainingAmount <= 0;
|
||||
}
|
||||
|
||||
public bool CanCraft(List<Ingredient> neededIngredients, int amount)
|
||||
{
|
||||
foreach (Ingredient ingredient in neededIngredients)
|
||||
{
|
||||
Item item = items.Find(x => x.data.Id == ingredient.Item && x.currentAmount >= ingredient.Amount * amount);
|
||||
if (item == null)
|
||||
if (GetItemAmount(ingredient.Item) < ingredient.Amount * amount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -93,4 +111,21 @@ public class Inventory
|
||||
{
|
||||
OnInventoryUpdate?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private int GetFreeCapacity(string id, int stackSize)
|
||||
{
|
||||
int freeCapacity = 0;
|
||||
|
||||
foreach (Item item in items)
|
||||
{
|
||||
if (item.data.Id == id)
|
||||
{
|
||||
freeCapacity += stackSize - item.currentAmount;
|
||||
}
|
||||
}
|
||||
|
||||
int freeSlots = maxInventorySize * GameData.maxRobotCount - items.Count;
|
||||
freeCapacity += freeSlots * stackSize;
|
||||
return freeCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,37 @@ public partial class Robot : Node3D
|
||||
currentMessage = "";
|
||||
}
|
||||
|
||||
public RobotSaveData CreateSaveData()
|
||||
{
|
||||
return new RobotSaveData
|
||||
{
|
||||
Name = Name,
|
||||
CurrentProgram = currentProgram,
|
||||
CurrentMessage = currentMessage,
|
||||
RobotType = robotType,
|
||||
X = Position.X,
|
||||
Y = Position.Y,
|
||||
Z = Position.Z,
|
||||
Heat = heat,
|
||||
Maintenance = maintenance,
|
||||
IsCoolingDown = isCoolingDown,
|
||||
IsBroken = isBroken
|
||||
};
|
||||
}
|
||||
|
||||
public void LoadSaveData(RobotSaveData saveData)
|
||||
{
|
||||
Name = saveData.Name;
|
||||
currentProgram = saveData.CurrentProgram;
|
||||
currentMessage = saveData.CurrentMessage ?? "";
|
||||
robotType = saveData.RobotType ?? "stone_robot";
|
||||
Position = new Vector3(saveData.X, saveData.Y, saveData.Z);
|
||||
heat = saveData.Heat;
|
||||
maintenance = saveData.Maintenance;
|
||||
isCoolingDown = saveData.IsCoolingDown;
|
||||
isBroken = saveData.IsBroken;
|
||||
}
|
||||
|
||||
private bool CanExecute(double delta)
|
||||
{
|
||||
if (GameData.survival.isDead)
|
||||
|
||||
@@ -3,13 +3,13 @@ using System.Collections.Generic;
|
||||
|
||||
public class RobotStats
|
||||
{
|
||||
public readonly Dictionary<string, RobotTypeStats> RobotTypes = new()
|
||||
public readonly Dictionary<string, RobotTypeStats> RobotTypes = new Dictionary<string, RobotTypeStats>
|
||||
{
|
||||
["stone_robot"] = new RobotTypeStats(0.75f, 0.60f, 0.80f, 0.80f, 1.40f, -0.10f),
|
||||
["copper_robot"] = new RobotTypeStats(1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.00f),
|
||||
["tin_robot"] = new RobotTypeStats(1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.00f),
|
||||
["bronze_robot"] = new RobotTypeStats(1.15f, 1.10f, 0.90f, 1.10f, 0.80f, 0.05f),
|
||||
["iron_robot"] = new RobotTypeStats(1.35f, 1.25f, 1.15f, 1.20f, 0.65f, 0.10f)
|
||||
{ "stone_robot", new RobotTypeStats(0.75f, 0.60f, 0.80f, 0.80f, 1.40f, -0.10f) },
|
||||
{ "copper_robot", new RobotTypeStats(1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.00f) },
|
||||
{ "tin_robot", new RobotTypeStats(1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.00f) },
|
||||
{ "bronze_robot", new RobotTypeStats(1.15f, 1.10f, 0.90f, 1.10f, 0.80f, 0.05f) },
|
||||
{ "iron_robot", new RobotTypeStats(1.35f, 1.25f, 1.15f, 1.20f, 0.65f, 0.10f) }
|
||||
};
|
||||
private const float BaseMinimumEfficiency = 0.35f;
|
||||
|
||||
@@ -86,7 +86,7 @@ public class RobotStats
|
||||
break;
|
||||
case "robot_count_increase":
|
||||
GameData.maxRobotCount += (int)effect.Value;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public partial class TestRunner : Node
|
||||
{
|
||||
private int passedTests = 0;
|
||||
private int failedTests = 0;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Run("Inventory adds, stacks and removes items", TestInventoryStacksAndRemovesItems);
|
||||
Run("Inventory crafting checks stacked totals", TestInventoryCanCraftAcrossStacks);
|
||||
Run("Survival consumes stored food and water", TestSurvivalConsumesFoodAndWater);
|
||||
Run("Survival death disables movement", TestSurvivalDeathDisablesMovement);
|
||||
Run("Robot research effects change robot stats", TestRobotResearchEffects);
|
||||
Run("Research completion applies effects once", TestResearchCompletionAppliesEffectsOnce);
|
||||
Run("Research execution pays resources and finishes", TestResearchExecutionPaysResourcesAndFinishes);
|
||||
Run("Inventory add failure keeps inventory unchanged", TestInventoryAddFailureKeepsInventoryUnchanged);
|
||||
Run("Resource extraction and save data roundtrip", TestResourceSaveRoundtrip);
|
||||
Run("Robot save data roundtrip keeps robot state", TestRobotSaveRoundtrip);
|
||||
Run("Save data captures and restores global state", TestSaveDataRestoresGlobalState);
|
||||
Run("Split save files store and load data", TestSplitSaveFilesRoundtrip);
|
||||
Run("Split save files include one file per saved layer", TestSplitSaveFilesIncludeLayerFiles);
|
||||
Run("If node evaluates inventory comparisons", TestIfNodeEvaluatesInventoryComparisons);
|
||||
Run("While node reports false conditions", TestWhileNodeReportsFalseConditions);
|
||||
Run("For node stops after configured amount", TestForNodeStopsAfterConfiguredAmount);
|
||||
Run("Item data readable names are stable", TestItemDataReadableNames);
|
||||
Run("Resource files load core game data", TestResourceFilesLoadCoreData);
|
||||
|
||||
GD.Print($"Tests passed: {passedTests}, failed: {failedTests}");
|
||||
GetTree().Quit(failedTests == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
private void Run(string name, Action test)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameData.ResetRunState();
|
||||
test();
|
||||
passedTests++;
|
||||
GD.Print("[PASS] " + name);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
failedTests++;
|
||||
GD.PrintErr("[FAIL] " + name + ": " + exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void TestInventoryStacksAndRemovesItems()
|
||||
{
|
||||
ItemData stone = GameData.availableItems["stone"];
|
||||
GameData.inventory.AddItem(new Item { data = stone }, stone.StackSize + 5);
|
||||
|
||||
AssertEqual(stone.StackSize + 5, GameData.inventory.GetItemAmount("stone"), "stone amount");
|
||||
AssertEqual(2, GameData.inventory.items.Count, "stone stack count");
|
||||
|
||||
bool removed = GameData.inventory.TryRemoveItem("stone", stone.StackSize);
|
||||
|
||||
AssertTrue(removed, "remove should succeed");
|
||||
AssertEqual(5, GameData.inventory.GetItemAmount("stone"), "remaining stone amount");
|
||||
}
|
||||
|
||||
private void TestInventoryCanCraftAcrossStacks()
|
||||
{
|
||||
ItemData stone = GameData.availableItems["stone"];
|
||||
stone.StackSize = 5;
|
||||
GameData.inventory.AddItem(new Item { data = stone }, 8);
|
||||
|
||||
List<Ingredient> ingredients = new List<Ingredient>
|
||||
{
|
||||
new Ingredient { Item = "stone", Amount = 8 }
|
||||
};
|
||||
|
||||
AssertTrue(GameData.inventory.CanCraft(ingredients, 1), "crafting should see both stacks");
|
||||
}
|
||||
|
||||
private void TestSurvivalConsumesFoodAndWater()
|
||||
{
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["mushroom"] }, 1);
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["water"] }, 1);
|
||||
GameData.survival.hunger = 30f;
|
||||
GameData.survival.thirst = 30f;
|
||||
|
||||
GameData.survival.Update(0.1);
|
||||
|
||||
AssertTrue(GameData.survival.hunger > 60f, "hunger should recover");
|
||||
AssertTrue(GameData.survival.thirst > 65f, "thirst should recover");
|
||||
AssertEqual(0, GameData.inventory.GetItemAmount("mushroom"), "mushroom consumed");
|
||||
AssertEqual(0, GameData.inventory.GetItemAmount("water"), "water consumed");
|
||||
}
|
||||
|
||||
private void TestSurvivalDeathDisablesMovement()
|
||||
{
|
||||
GameData.canMove = true;
|
||||
GameData.survival.energy = 0.01f;
|
||||
|
||||
GameData.survival.Update(2.0);
|
||||
|
||||
AssertTrue(GameData.survival.isDead, "survival should be dead");
|
||||
AssertFalse(GameData.canMove, "movement should be disabled");
|
||||
}
|
||||
|
||||
private void TestRobotResearchEffects()
|
||||
{
|
||||
RobotStats stats = new RobotStats();
|
||||
List<ResearchEffect> effects = new List<ResearchEffect>
|
||||
{
|
||||
new ResearchEffect { Stat = "robot_speed_bonus", Value = 0.25f },
|
||||
new ResearchEffect { Stat = "robot_energy_use_reduction", Value = 0.20f },
|
||||
new ResearchEffect { Stat = "robot_cooling_bonus", Value = 0.50f }
|
||||
};
|
||||
|
||||
stats.Apply(effects);
|
||||
|
||||
AssertClose(12.5f, stats.GetMovementSpeed(10f), 0.001f, "movement speed");
|
||||
AssertClose(8f, stats.GetEnergyUse(10f), 0.001f, "energy use");
|
||||
AssertClose(15f, stats.GetCoolingRate(10f), 0.001f, "cooling");
|
||||
}
|
||||
|
||||
private void TestResearchCompletionAppliesEffectsOnce()
|
||||
{
|
||||
ResearchData data = new ResearchData
|
||||
{
|
||||
Id = "test_research",
|
||||
Inputs = new List<Ingredient>(),
|
||||
Research = "basics",
|
||||
CraftTime = 1.0,
|
||||
Texture = "",
|
||||
Effects = new List<ResearchEffect>
|
||||
{
|
||||
new ResearchEffect { Stat = "robot_speed_bonus", Value = 0.10f }
|
||||
}
|
||||
};
|
||||
|
||||
Research research = new Research(data);
|
||||
research.Complete();
|
||||
research.Complete();
|
||||
|
||||
AssertClose(11f, GameData.robotStats.GetMovementSpeed(10f), 0.001f, "research effect applied once");
|
||||
}
|
||||
|
||||
private void TestResourceSaveRoundtrip()
|
||||
{
|
||||
GameResource resource = new GameResource("stone");
|
||||
resource.Extract(2.0);
|
||||
|
||||
ResourceSaveData saveData = resource.CreateSaveData();
|
||||
GameResource loadedResource = GameResource.FromSaveData(saveData);
|
||||
|
||||
AssertEqual(saveData.Name, loadedResource.CreateSaveData().Name, "resource name");
|
||||
AssertEqual(saveData.CurrentAmount, loadedResource.CreateSaveData().CurrentAmount, "resource amount");
|
||||
}
|
||||
|
||||
private void TestRobotSaveRoundtrip()
|
||||
{
|
||||
Robot robot = new Robot
|
||||
{
|
||||
Name = "Ada",
|
||||
Position = new Vector3(1f, 2f, 3f),
|
||||
heat = 44f,
|
||||
maintenance = 55f,
|
||||
isBroken = true,
|
||||
isCoolingDown = true,
|
||||
currentProgram = "Mining",
|
||||
currentMessage = "Needs care",
|
||||
robotType = "bronze_robot"
|
||||
};
|
||||
|
||||
RobotSaveData saveData = robot.CreateSaveData();
|
||||
Robot loadedRobot = new Robot();
|
||||
loadedRobot.LoadSaveData(saveData);
|
||||
|
||||
AssertEqual("Ada", loadedRobot.Name.ToString(), "robot name");
|
||||
AssertEqual("Mining", loadedRobot.currentProgram, "robot program");
|
||||
AssertEqual("bronze_robot", loadedRobot.robotType, "robot type");
|
||||
AssertClose(44f, loadedRobot.heat, 0.001f, "robot heat");
|
||||
AssertClose(55f, loadedRobot.maintenance, 0.001f, "robot maintenance");
|
||||
AssertTrue(loadedRobot.isBroken, "robot broken state");
|
||||
AssertTrue(loadedRobot.isCoolingDown, "robot cooling state");
|
||||
}
|
||||
|
||||
private void TestSaveDataRestoresGlobalState()
|
||||
{
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["stone"] }, 12);
|
||||
GameData.survival.hunger = 42f;
|
||||
GameData.survival.thirst = 43f;
|
||||
GameData.survival.energy = 44f;
|
||||
GameData.lowestLayer = 3;
|
||||
GameData.availableResearch["stoneage"].Complete();
|
||||
|
||||
SaveGameData saveData = SaveGameManager.CreateSaveData();
|
||||
|
||||
GameData.ResetRunState();
|
||||
SaveGameManager.ApplyWorldData(saveData);
|
||||
|
||||
AssertEqual(12, GameData.inventory.GetItemAmount("stone"), "saved inventory");
|
||||
AssertClose(42f, GameData.survival.hunger, 0.001f, "saved hunger");
|
||||
AssertEqual(3, GameData.lowestLayer, "saved layer");
|
||||
AssertEqual(ResearchState.RESEARCHED, GameData.availableResearch["stoneage"].state, "saved research");
|
||||
}
|
||||
|
||||
private void TestResearchExecutionPaysResourcesAndFinishes()
|
||||
{
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["stone"] }, 5);
|
||||
ResearchData researchData = new ResearchData
|
||||
{
|
||||
Id = "stone_counterweight",
|
||||
Inputs = new List<Ingredient>
|
||||
{
|
||||
new Ingredient { Item = "stone", Amount = 3 }
|
||||
},
|
||||
Research = "basics",
|
||||
CraftTime = 1.0,
|
||||
Texture = "",
|
||||
Effects = new List<ResearchEffect>()
|
||||
};
|
||||
|
||||
Research research = new Research(researchData);
|
||||
ResearchResult result = research.Execute(1.0);
|
||||
|
||||
AssertEqual(ResearchResult.FINISHED, result, "research result");
|
||||
AssertEqual(2, GameData.inventory.GetItemAmount("stone"), "research cost");
|
||||
AssertEqual(ResearchState.RESEARCHED, research.state, "research state");
|
||||
}
|
||||
|
||||
private void TestInventoryAddFailureKeepsInventoryUnchanged()
|
||||
{
|
||||
GameData.maxRobotCount = 1;
|
||||
GameData.inventory.maxInventorySize = 1;
|
||||
ItemData stone = GameData.availableItems["stone"];
|
||||
|
||||
bool result = GameData.inventory.AddItem(new Item { data = stone }, stone.StackSize + 1);
|
||||
|
||||
AssertFalse(result, "add should fail");
|
||||
AssertEqual(0, GameData.inventory.GetItemAmount("stone"), "failed add should be atomic");
|
||||
AssertEqual(0, GameData.inventory.items.Count, "failed add should not create stacks");
|
||||
}
|
||||
|
||||
private void TestSplitSaveFilesRoundtrip()
|
||||
{
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["water"] }, 7);
|
||||
GameData.survival.energy = 77f;
|
||||
|
||||
SaveGameManager.SaveGame();
|
||||
SaveGameData saveData = SaveGameManager.LoadSaveData();
|
||||
|
||||
AssertTrue(SaveGameManager.SaveExists(), "save folder should exist");
|
||||
AssertTrue(FileAccess.FileExists("user://savegame/gamedata.json"), "gamedata file");
|
||||
AssertTrue(FileAccess.FileExists("user://savegame/robots.json"), "robots file");
|
||||
AssertTrue(FileAccess.FileExists("user://savegame/research.json"), "research file");
|
||||
AssertEqual(7, saveData.Inventory[0].Amount, "saved file inventory");
|
||||
AssertClose(77f, saveData.Survival.Energy, 0.001f, "saved file energy");
|
||||
}
|
||||
|
||||
private void TestSplitSaveFilesIncludeLayerFiles()
|
||||
{
|
||||
GameData.ruinSize = 2;
|
||||
GameData.map = new Layer[2];
|
||||
GameData.map[0] = CreateTestLayer(0, "spawn");
|
||||
GameData.map[1] = CreateTestLayer(1, "gate");
|
||||
|
||||
SaveGameManager.SaveGame();
|
||||
SaveGameData saveData = SaveGameManager.LoadSaveData();
|
||||
|
||||
AssertTrue(FileAccess.FileExists("user://savegame/layer_0.json"), "layer 0 file");
|
||||
AssertTrue(FileAccess.FileExists("user://savegame/layer_1.json"), "layer 1 file");
|
||||
AssertEqual(2, saveData.Layers.Count, "loaded layer count");
|
||||
AssertEqual("spawn", saveData.Layers[0].Tiles[0].CollapsedMesh, "layer 0 tile");
|
||||
AssertEqual("gate", saveData.Layers[1].Tiles[0].CollapsedMesh, "layer 1 tile");
|
||||
}
|
||||
|
||||
private void TestIfNodeEvaluatesInventoryComparisons()
|
||||
{
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["stone"] }, 5);
|
||||
IfNode node = new IfNode
|
||||
{
|
||||
selectedItem = new Item { data = GameData.availableItems["stone"] },
|
||||
amount = 3,
|
||||
comparator = "is bigger than"
|
||||
};
|
||||
|
||||
AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "if condition true");
|
||||
|
||||
node.amount = 8;
|
||||
AssertEqual(NodeResult.CONDITIONFALSE, node.Execute(null, 0), "if condition false");
|
||||
}
|
||||
|
||||
private void TestWhileNodeReportsFalseConditions()
|
||||
{
|
||||
GameData.inventory.AddItem(new Item { data = GameData.availableItems["water"] }, 2);
|
||||
WhileNode node = new WhileNode
|
||||
{
|
||||
selectedItem = new Item { data = GameData.availableItems["water"] },
|
||||
amount = 5,
|
||||
comparator = "is bigger than or equal to"
|
||||
};
|
||||
|
||||
AssertEqual(NodeResult.CONDITIONFALSE, node.Execute(null, 0), "while condition false");
|
||||
}
|
||||
|
||||
private void TestForNodeStopsAfterConfiguredAmount()
|
||||
{
|
||||
ForNode node = new ForNode
|
||||
{
|
||||
amount = 2
|
||||
};
|
||||
|
||||
AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "first iteration");
|
||||
AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "second iteration");
|
||||
AssertEqual(NodeResult.CONDITIONFALSE, node.Execute(null, 0), "loop finished");
|
||||
}
|
||||
|
||||
private void TestItemDataReadableNames()
|
||||
{
|
||||
AssertEqual("Iron gear", ItemData.GetReadableName("iron_gear"), "readable name");
|
||||
AssertEqual("iron_gear", ItemData.GetIndex("Iron Gear"), "index name");
|
||||
}
|
||||
|
||||
private void TestResourceFilesLoadCoreData()
|
||||
{
|
||||
AssertTrue(GameData.availableItems.ContainsKey("stone"), "stone item loaded");
|
||||
AssertTrue(GameData.availableItems.ContainsKey("water"), "water item loaded");
|
||||
AssertTrue(GameData.availableResearch.ContainsKey("basics"), "basics research loaded");
|
||||
AssertTrue(GameData.availableResearch.ContainsKey("iron_robotics"), "iron robotics research loaded");
|
||||
}
|
||||
|
||||
private Layer CreateTestLayer(int level, string collapsedMesh)
|
||||
{
|
||||
Layer layer = new Layer
|
||||
{
|
||||
level = level,
|
||||
currentResources = new List<string> { "stone" },
|
||||
gateIngredients = new List<Ingredient>(),
|
||||
tiles = new Tile[1, 1]
|
||||
};
|
||||
|
||||
Tile tile = new Tile
|
||||
{
|
||||
GridPosition = new Vector2I(0, 0),
|
||||
Position = Vector3.Zero,
|
||||
collapsedMesh = collapsedMesh,
|
||||
containsResource = true,
|
||||
resource = new GameResource("stone")
|
||||
};
|
||||
|
||||
layer.tiles[0, 0] = tile;
|
||||
return layer;
|
||||
}
|
||||
|
||||
private void AssertTrue(bool value, string message)
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertFalse(bool value, string message)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertEqual<T>(T expected, T actual, string message)
|
||||
{
|
||||
if (!EqualityComparer<T>.Default.Equals(expected, actual))
|
||||
{
|
||||
throw new Exception($"{message}: expected {expected}, got {actual}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertClose(float expected, float actual, float tolerance, string message)
|
||||
{
|
||||
if (Math.Abs(expected - actual) > tolerance)
|
||||
{
|
||||
throw new Exception($"{message}: expected {expected}, got {actual}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://bm6a1hivjtc8e
|
||||
@@ -107,6 +107,19 @@ public partial class UIHandler : Control
|
||||
GetTree().ChangeSceneToFile("res://Scenes/MainMenu.tscn");
|
||||
}
|
||||
|
||||
public void SaveGame()
|
||||
{
|
||||
SaveGameManager.SaveGame();
|
||||
}
|
||||
|
||||
public void LoadGame()
|
||||
{
|
||||
if (!SaveGameManager.SaveExists()) return;
|
||||
|
||||
GameData.loadSaveOnStart = true;
|
||||
GetTree().ChangeSceneToFile("res://Scenes/Game.tscn");
|
||||
}
|
||||
|
||||
public void OpenUIElement(Control element)
|
||||
{
|
||||
if (element.Visible)
|
||||
|
||||
@@ -4,6 +4,15 @@ public partial class MainMenu : Control
|
||||
{
|
||||
public void OnPlayPressed()
|
||||
{
|
||||
GameData.loadSaveOnStart = false;
|
||||
GetTree().ChangeSceneToFile("res://Scenes/Game.tscn");
|
||||
}
|
||||
|
||||
public void OnLoadPressed()
|
||||
{
|
||||
if (!SaveGameManager.SaveExists()) return;
|
||||
|
||||
GameData.loadSaveOnStart = true;
|
||||
GetTree().ChangeSceneToFile("res://Scenes/Game.tscn");
|
||||
}
|
||||
|
||||
|
||||
+46
-17
@@ -16,6 +16,13 @@ public partial class World : Node3D
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
bool shouldLoadSave = loadSaveOnStart && SaveGameManager.SaveExists();
|
||||
SaveGameData saveGame = shouldLoadSave ? SaveGameManager.LoadSaveData() : null;
|
||||
if (saveGame != null)
|
||||
{
|
||||
seed = saveGame.Seed;
|
||||
}
|
||||
|
||||
ResetRunState();
|
||||
WFC.FillAdjacencies();
|
||||
|
||||
@@ -41,18 +48,27 @@ public partial class World : Node3D
|
||||
|
||||
map = new Layer[ruinSize];
|
||||
GenerateWorld();
|
||||
SetGateRequirements();
|
||||
|
||||
if (shouldLoadSave && saveGame != null)
|
||||
{
|
||||
SaveGameManager.ApplyWorldData(saveGame);
|
||||
}
|
||||
|
||||
Pathfinding.BuildAStarGraph();
|
||||
|
||||
HandleRenderData(BuildRenderData(0));
|
||||
HandleRenderData(BuildRenderData(visibleLayer));
|
||||
|
||||
Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>();
|
||||
robot.Name = "Bob";
|
||||
robot.Position = map[0].tiles[0, 0].Position;
|
||||
AddChild(robot);
|
||||
robots.Add(robot);
|
||||
if (shouldLoadSave && saveGame != null)
|
||||
{
|
||||
SpawnSavedRobots(saveGame.Robots);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnDefaultRobot();
|
||||
}
|
||||
|
||||
SetGateRequirements();
|
||||
loadSaveOnStart = false;
|
||||
}
|
||||
|
||||
private Dictionary<string, MultiMeshInstance3D> CreateMultiMeshes(Dictionary<string, Mesh> meshLibrary)
|
||||
@@ -94,17 +110,30 @@ public partial class World : Node3D
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetRunState()
|
||||
private void SpawnDefaultRobot()
|
||||
{
|
||||
survival = new SurvivalState();
|
||||
robotStats = new RobotStats();
|
||||
inventory = new Inventory();
|
||||
availableResearch = ResourceLoader.LoadResearch();
|
||||
robots.Clear();
|
||||
currentLayer = 0;
|
||||
visibleLayer = 0;
|
||||
lowestLayer = 0;
|
||||
canMove = true;
|
||||
Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>();
|
||||
robot.Name = "Bob";
|
||||
robot.Position = map[0].tiles[0, 0].Position;
|
||||
AddChild(robot);
|
||||
robots.Add(robot);
|
||||
}
|
||||
|
||||
private void SpawnSavedRobots(List<RobotSaveData> savedRobots)
|
||||
{
|
||||
if (savedRobots == null || savedRobots.Count <= 0)
|
||||
{
|
||||
SpawnDefaultRobot();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (RobotSaveData savedRobot in savedRobots)
|
||||
{
|
||||
Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>();
|
||||
robot.LoadSaveData(savedRobot);
|
||||
AddChild(robot);
|
||||
robots.Add(robot);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateWorld()
|
||||
|
||||
Reference in New Issue
Block a user