633 lines
25 KiB
C#
633 lines
25 KiB
C#
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 grace period prevents early death", TestSurvivalGracePeriodPreventsEarlyDeath);
|
|
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("Research cannot start without resources", TestResearchCannotStartWithoutResources);
|
|
Run("Inventory add failure keeps inventory unchanged", TestInventoryAddFailureKeepsInventoryUnchanged);
|
|
Run("Saved scripts can be deleted", TestSavedScriptsCanBeDeleted);
|
|
Run("Resource extraction and save data roundtrip", TestResourceSaveRoundtrip);
|
|
Run("Endless resources extract slower", TestEndlessResourcesExtractSlower);
|
|
Run("Robot save data roundtrip keeps robot state", TestRobotSaveRoundtrip);
|
|
Run("Maintain node consumes matching gear", TestMaintainNodeConsumesMatchingGear);
|
|
Run("Sacrifice node makes resource endless", TestSacrificeNodeMakesResourceEndless);
|
|
Run("No robot recovery detects loss", TestNoRobotRecoveryDetectsLoss);
|
|
Run("Robot item prevents no robot loss", TestRobotItemPreventsNoRobotLoss);
|
|
Run("Save data captures and restores options", TestSaveDataRestoresOptions);
|
|
Run("Save data captures and restores global state", TestSaveDataRestoresGlobalState);
|
|
Run("Save data restores survival timer", TestSaveDataRestoresSurvivalTimer);
|
|
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("Start node succeeds immediately", TestStartNodeSucceedsImmediately);
|
|
Run("Split node connections restore both branches", TestSplitNodeConnectionsRestoreBothBranches);
|
|
Run("Linear node connection restores next node", TestLinearNodeConnectionRestoresNextNode);
|
|
Run("Paused world does not drain survival", TestPausedWorldDoesNotDrainSurvival);
|
|
Run("Open gate hides gate content", TestOpenGateHidesGateContent);
|
|
Run("Layer generation succeeds above threshold", TestLayerGenerationSuccessRate);
|
|
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.gracePeriodEnabled = false;
|
|
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 TestSurvivalGracePeriodPreventsEarlyDeath()
|
|
{
|
|
GameData.canMove = true;
|
|
GameData.survival.hunger = 0f;
|
|
GameData.survival.thirst = 0f;
|
|
GameData.survival.energy = 0f;
|
|
|
|
GameData.survival.Update(1.0);
|
|
|
|
AssertFalse(GameData.survival.isDead, "survival should not fail during grace period");
|
|
AssertTrue(GameData.canMove, "movement should stay enabled during grace period");
|
|
AssertTrue(GameData.survival.hunger > 0f, "hunger should be clamped above zero");
|
|
AssertTrue(GameData.survival.thirst > 0f, "thirst should be clamped above zero");
|
|
AssertTrue(GameData.survival.energy > 0f, "energy should be clamped above zero");
|
|
}
|
|
|
|
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 TestEndlessResourcesExtractSlower()
|
|
{
|
|
GameResource resource = new GameResource("stone");
|
|
|
|
AssertTrue(resource.Extract(1.0), "normal resource should extract after one second");
|
|
|
|
resource.MakeEndless();
|
|
|
|
AssertFalse(resource.Extract(1.0), "endless resource should not extract after one second");
|
|
AssertTrue(resource.Extract(3.0), "endless resource should extract after four total seconds");
|
|
AssertTrue(resource.IsEndless(), "resource should be endless");
|
|
AssertTrue(resource.GetExtractionSpeed() > 1f, "endless extraction speed should be slower");
|
|
}
|
|
|
|
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 TestMaintainNodeConsumesMatchingGear()
|
|
{
|
|
Robot robot = new Robot
|
|
{
|
|
robotType = "copper_robot",
|
|
maintenance = 50f,
|
|
isBroken = true
|
|
};
|
|
|
|
GameData.inventory.AddItem(new Item { data = GameData.availableItems["copper_gear"] }, 1);
|
|
MaintainNode node = new MaintainNode();
|
|
|
|
AssertEqual(NodeResult.SUCCESS, node.Execute(robot, 0), "maintain should succeed");
|
|
AssertClose(60f, robot.maintenance, 0.001f, "maintenance repaired by 10");
|
|
AssertFalse(robot.isBroken, "robot should be usable again");
|
|
AssertEqual(0, GameData.inventory.GetItemAmount("copper_gear"), "gear should be consumed");
|
|
}
|
|
|
|
private void TestSacrificeNodeMakesResourceEndless()
|
|
{
|
|
GameData.ruinSize = 1;
|
|
GameData.layerSize = 1;
|
|
GameData.map = new Layer[1];
|
|
GameData.map[0] = CreateTestLayer(0, "spawn");
|
|
GameData.map[0].tiles[0, 0].resource = new GameResource("stone");
|
|
GameData.map[0].tiles[0, 0].containsResource = true;
|
|
Pathfinding.BuildAStarGraph();
|
|
|
|
Robot robot = new Robot
|
|
{
|
|
Position = Vector3.Zero
|
|
};
|
|
GameData.robots.Add(robot);
|
|
|
|
SacrificeNode node = new SacrificeNode();
|
|
|
|
AssertEqual(NodeResult.SUCCESS, node.Execute(robot, 0), "sacrifice should succeed");
|
|
AssertTrue(GameData.map[0].tiles[0, 0].resource.IsEndless(), "resource should become endless");
|
|
AssertEqual(0, GameData.robots.Count, "robot should be removed");
|
|
}
|
|
|
|
private void TestNoRobotRecoveryDetectsLoss()
|
|
{
|
|
AssertTrue(GameData.HasNoRobotRecovery(), "no robots and no robot items should be a loss");
|
|
}
|
|
|
|
private void TestRobotItemPreventsNoRobotLoss()
|
|
{
|
|
GameData.inventory.AddItem(new Item { data = GameData.availableItems["stone_robot"] }, 1);
|
|
|
|
AssertFalse(GameData.HasNoRobotRecovery(), "robot item should prevent loss");
|
|
}
|
|
|
|
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 TestSaveDataRestoresOptions()
|
|
{
|
|
GameData.screenMode = 1;
|
|
GameData.soundVolume = 0.35f;
|
|
GameData.lightColor = new Color(0.2f, 0.4f, 0.6f, 1f);
|
|
|
|
SaveGameData saveData = SaveGameManager.CreateSaveData();
|
|
|
|
GameData.screenMode = 2;
|
|
GameData.soundVolume = 0.8f;
|
|
GameData.lightColor = Colors.White;
|
|
|
|
SaveGameManager.ApplyWorldData(saveData);
|
|
|
|
AssertEqual(1, GameData.screenMode, "saved screen mode");
|
|
AssertClose(0.35f, GameData.soundVolume, 0.001f, "saved sound volume");
|
|
AssertClose(0.2f, GameData.lightColor.R, 0.001f, "saved light red");
|
|
AssertClose(0.4f, GameData.lightColor.G, 0.001f, "saved light green");
|
|
AssertClose(0.6f, GameData.lightColor.B, 0.001f, "saved light blue");
|
|
}
|
|
|
|
private void TestSaveDataRestoresSurvivalTimer()
|
|
{
|
|
GameData.survival.elapsedSeconds = 321.5;
|
|
|
|
SaveGameData saveData = SaveGameManager.CreateSaveData();
|
|
|
|
GameData.ResetRunState();
|
|
SaveGameManager.ApplyWorldData(saveData);
|
|
|
|
AssertClose(321.5f, (float)GameData.survival.elapsedSeconds, 0.001f, "saved survival timer");
|
|
}
|
|
|
|
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 TestSavedScriptsCanBeDeleted()
|
|
{
|
|
string scriptName = "delete_test_script";
|
|
FileHandler.SaveProgram(scriptName, "{\"Nodes\":[],\"Connections\":[]}");
|
|
|
|
AssertTrue(FileHandler.LoadProgramNames().Contains(scriptName), "script should be listed");
|
|
AssertTrue(FileHandler.DeleteProgram(scriptName), "delete should succeed");
|
|
AssertEqual("", FileHandler.LoadProgram(scriptName), "deleted script should be empty");
|
|
AssertFalse(FileHandler.DeleteProgram(scriptName), "second delete should fail");
|
|
}
|
|
|
|
private void TestResearchCannotStartWithoutResources()
|
|
{
|
|
ResearchData researchData = new ResearchData
|
|
{
|
|
Id = "missing_stones",
|
|
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);
|
|
|
|
AssertFalse(research.CanStart(), "research should not be startable");
|
|
AssertEqual(ResearchResult.FAILED, result, "research should fail without resources");
|
|
AssertEqual(ResearchState.UNDEFINED, research.state, "research state should stay unchanged");
|
|
}
|
|
|
|
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 TestStartNodeSucceedsImmediately()
|
|
{
|
|
StartNode node = new StartNode();
|
|
|
|
AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "start should succeed");
|
|
AssertEqual("Name: Start", node.Save(), "start save");
|
|
}
|
|
|
|
private void TestSplitNodeConnectionsRestoreBothBranches()
|
|
{
|
|
MoveNode successNode = new MoveNode();
|
|
MoveNode negativeNode = new MoveNode();
|
|
System.Collections.Generic.Dictionary<StringName, ProgramNode> availableNodes =
|
|
new System.Collections.Generic.Dictionary<StringName, ProgramNode>
|
|
{
|
|
{ new StringName("success"), successNode },
|
|
{ new StringName("negative"), negativeNode }
|
|
};
|
|
List<Godot.Collections.Dictionary> connections = new List<Godot.Collections.Dictionary>
|
|
{
|
|
CreateConnection("split", 0, "success", 0),
|
|
CreateConnection("split", 1, "negative", 0)
|
|
};
|
|
|
|
IfNode ifNode = new IfNode();
|
|
ifNode.SetNextNode(connections, availableNodes);
|
|
|
|
AssertTrue(ReferenceEquals(successNode, ifNode.nextNode), "if success branch");
|
|
AssertTrue(ReferenceEquals(negativeNode, ifNode.NegativeNode), "if negative branch");
|
|
|
|
WhileNode whileNode = new WhileNode();
|
|
whileNode.SetNextNode(connections, availableNodes);
|
|
|
|
AssertTrue(ReferenceEquals(successNode, whileNode.nextNode), "while success branch");
|
|
AssertTrue(ReferenceEquals(negativeNode, whileNode.NegativeNode), "while negative branch");
|
|
|
|
ForNode forNode = new ForNode();
|
|
forNode.SetNextNode(connections, availableNodes);
|
|
|
|
AssertTrue(ReferenceEquals(successNode, forNode.nextNode), "for success branch");
|
|
AssertTrue(ReferenceEquals(negativeNode, forNode.NegativeNode), "for negative branch");
|
|
}
|
|
|
|
private void TestLinearNodeConnectionRestoresNextNode()
|
|
{
|
|
MoveNode targetNode = new MoveNode();
|
|
StartNode startNode = new StartNode();
|
|
System.Collections.Generic.Dictionary<StringName, ProgramNode> availableNodes =
|
|
new System.Collections.Generic.Dictionary<StringName, ProgramNode>
|
|
{
|
|
{ new StringName("target"), targetNode }
|
|
};
|
|
List<Godot.Collections.Dictionary> connections = new List<Godot.Collections.Dictionary>
|
|
{
|
|
CreateConnection("start", 0, "target", 0)
|
|
};
|
|
|
|
startNode.SetNextNode(connections, availableNodes);
|
|
|
|
AssertTrue(ReferenceEquals(targetNode, startNode.nextNode), "linear next node");
|
|
AssertTrue(startNode.NegativeNode == null, "linear node should not have negative branch");
|
|
}
|
|
|
|
private void TestPausedWorldDoesNotDrainSurvival()
|
|
{
|
|
GameData.isPaused = true;
|
|
GameData.canMove = false;
|
|
GameData.survival.energy = 100f;
|
|
|
|
World world = new World();
|
|
world.UpdateGameLoop(100.0);
|
|
|
|
AssertClose(100f, GameData.survival.energy, 0.001f, "energy should not drain while paused");
|
|
}
|
|
|
|
private void TestOpenGateHidesGateContent()
|
|
{
|
|
Layer layer = CreateTestLayer(0, "gate");
|
|
layer.gateCoordinate = new Vector2I(0, 0);
|
|
layer.tiles[0, 0].ContentNode = new Node3D
|
|
{
|
|
Visible = true
|
|
};
|
|
|
|
layer.OpenGate();
|
|
|
|
AssertTrue(layer.isGateOpen, "gate should be open");
|
|
AssertFalse(layer.tiles[0, 0].ContentNode.Visible, "gate content should be hidden");
|
|
}
|
|
|
|
private void TestLayerGenerationSuccessRate()
|
|
{
|
|
const int layerCount = 1000;
|
|
const float minimumSuccessRate = 0.90f;
|
|
|
|
GameData.layerSize = 20;
|
|
WFC.FillAdjacencies();
|
|
Dictionary<string, MeshInstance3D> tileMeshes = ResourceLoader.LoadTiles();
|
|
|
|
int successfulLayers = 0;
|
|
|
|
for (int i = 0; i < layerCount; i++)
|
|
{
|
|
Layer layer = new Layer();
|
|
layer._Ready();
|
|
layer.SetupLayer(GameData.layerSize, 0, tileMeshes, new Vector2I());
|
|
|
|
if (IsGeneratedLayerValid(layer))
|
|
{
|
|
successfulLayers++;
|
|
}
|
|
|
|
layer.Free();
|
|
}
|
|
|
|
float successRate = successfulLayers / (float)layerCount;
|
|
GD.Print($"Layer generation success rate: {successfulLayers}/{layerCount} ({successRate:P2})");
|
|
|
|
AssertTrue(
|
|
successRate >= minimumSuccessRate,
|
|
$"layer generation success rate should be at least {minimumSuccessRate:P0}, got {successRate:P2}"
|
|
);
|
|
}
|
|
|
|
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");
|
|
|
|
Dictionary<ProgramNode, PackedScene> dslNodes = ResourceLoader.LoadDSLNodes();
|
|
bool hasStartNode = false;
|
|
foreach (ProgramNode node in dslNodes.Keys)
|
|
{
|
|
if (node is StartNode)
|
|
{
|
|
hasStartNode = true;
|
|
break;
|
|
}
|
|
}
|
|
AssertTrue(hasStartNode, "start node prefab loaded");
|
|
}
|
|
|
|
}
|