1 Commits

Author SHA1 Message Date
Nicola ad65698773 Merge pull request 'Finished first EA Version' (#1) from dev into main
Reviewed-on: #1
2026-05-19 20:01:10 +02:00
24 changed files with 63 additions and 388 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
-7
View File
@@ -15,10 +15,3 @@ script = ExtResource("2_j80uv")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1405728816] [node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1405728816]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.05883789, 0.29821777, -0.01171875) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.05883789, 0.29821777, -0.01171875)
shape = SubResource("BoxShape3D_vquur") shape = SubResource("BoxShape3D_vquur")
[node name="SpotLight3D" type="SpotLight3D" parent="." unique_id=1638370223]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.37939095, -0.31055498)
light_energy = 16.0
spot_range = 4.0
spot_attenuation = 0.0
spot_angle = 75.0
-24
View File
@@ -96,28 +96,4 @@ public class RobotSaveData
public float Maintenance { get; set; } public float Maintenance { get; set; }
public bool IsCoolingDown { get; set; } public bool IsCoolingDown { get; set; }
public bool IsBroken { get; set; } public bool IsBroken { get; set; }
public List<NodeSaveData> RunningProgramNodes { get; set; }
public string ProgramStartNode { get; set; }
public string CurrentNode { get; set; }
public bool IsExecuting { get; set; }
}
public class NodeSaveData
{
public string NodeType { get; set; }
public string EditorNodeId { get; set; }
public string NextEditorNodeId { get; set; }
public string NegativeEditorNodeId { get; set; }
public string NodeContent { get; set; }
public string LastExecutionMessage { get; set; }
public string ItemId { get; set; }
public string Comparator { get; set; }
public int Amount { get; set; }
public int AmountExecuted { get; set; }
public double ElapsedCraftTime { get; set; }
public int AmountCrafted { get; set; }
public bool HasTargetPosition { get; set; }
public int TargetX { get; set; }
public int TargetY { get; set; }
public int TargetZ { get; set; }
} }
+1
View File
@@ -137,6 +137,7 @@ public static class SaveGameDataApplier
Tile tile = layer.tiles[savedTile.X, savedTile.Y]; Tile tile = layer.tiles[savedTile.X, savedTile.Y];
tile.collapsedMesh = savedTile.CollapsedMesh; tile.collapsedMesh = savedTile.CollapsedMesh;
tile.containsLight = savedTile.ContainsLight;
tile.containsDecoration = savedTile.ContainsDecoration; tile.containsDecoration = savedTile.ContainsDecoration;
tile.containsResource = savedTile.ContainsResource; tile.containsResource = savedTile.ContainsResource;
tile.wasVisited = savedTile.WasVisited; tile.wasVisited = savedTile.WasVisited;
+1
View File
@@ -135,6 +135,7 @@ public static class SaveGameDataFactory
X = tile.GridPosition.X, X = tile.GridPosition.X,
Y = tile.GridPosition.Y, Y = tile.GridPosition.Y,
CollapsedMesh = tile.collapsedMesh, CollapsedMesh = tile.collapsedMesh,
ContainsLight = tile.containsLight,
ContainsDecoration = tile.containsDecoration, ContainsDecoration = tile.containsDecoration,
ContainsResource = tile.containsResource, ContainsResource = tile.containsResource,
WasVisited = tile.wasVisited, WasVisited = tile.wasVisited,
-28
View File
@@ -55,32 +55,4 @@ public class CraftNode : ProgramNode
{ {
return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Amount: {amount}"; return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Amount: {amount}";
} }
public override NodeSaveData CreateSaveData()
{
NodeSaveData saveData = base.CreateSaveData();
saveData.ItemId = selectedItem?.data.Id;
saveData.Amount = amount;
if (selectedItem != null)
{
saveData.ElapsedCraftTime = selectedItem.elapsedCraftTime;
saveData.AmountCrafted = selectedItem.amountCrafted;
}
return saveData;
}
public override void Load(NodeSaveData saveData)
{
base.Load(saveData);
amount = saveData.Amount;
if (string.IsNullOrEmpty(saveData.ItemId)) return;
if (!GameData.availableItems.ContainsKey(saveData.ItemId)) return;
selectedItem = new Item
{
data = GameData.availableItems[saveData.ItemId],
elapsedCraftTime = saveData.ElapsedCraftTime,
amountCrafted = saveData.AmountCrafted
};
}
} }
+4 -30
View File
@@ -7,7 +7,6 @@ public class ExploreNode : ProgramNode
public Vector3 startPosition; public Vector3 startPosition;
public Vector3I targetPosition; public Vector3I targetPosition;
public List<Vector3> pathPoints; public List<Vector3> pathPoints;
public bool hasTargetPosition;
public ExploreNode() public ExploreNode()
{ {
@@ -17,7 +16,7 @@ public class ExploreNode : ProgramNode
public override NodeResult Execute(Robot robot, double delta) public override NodeResult Execute(Robot robot, double delta)
{ {
if (pathPoints == null && !hasTargetPosition && !TrySelectTarget()) if (pathPoints == null && !TrySelectTarget())
{ {
lastExecutionMessage = "No tiles left to explore"; lastExecutionMessage = "No tiles left to explore";
return NodeResult.SUCCESS; return NodeResult.SUCCESS;
@@ -42,18 +41,18 @@ public class ExploreNode : ProgramNode
private bool TrySelectTarget() private bool TrySelectTarget()
{ {
int safetyCounter = 0; int safetyCounter = 0;
int layerRange = Math.Max(GameData.lowestLayer, 1);
int maximumAttempts = (int)Math.Pow(GameData.layerSize, 2) * 2; int maximumAttempts = (int)Math.Pow(GameData.layerSize, 2) * 2;
while (safetyCounter <= maximumAttempts) while (safetyCounter <= maximumAttempts)
{ {
targetPosition = new Vector3I( targetPosition = new Vector3I(
GameData.rand.Next(GameData.layerSize), GameData.rand.Next(GameData.layerSize),
GameData.rand.Next(GameData.lowestLayer + 1), GameData.rand.Next(layerRange),
GameData.rand.Next(GameData.layerSize) GameData.rand.Next(GameData.layerSize)
); );
if (!GameData.map[targetPosition.Y].tiles[targetPosition.X, targetPosition.Z].wasVisited) if (!GameData.map[targetPosition.Y].tiles[targetPosition.X, targetPosition.Z].wasVisited)
{ {
hasTargetPosition = true;
return true; return true;
} }
@@ -96,7 +95,6 @@ public class ExploreNode : ProgramNode
lastExecutionMessage = "Current exploration finished"; lastExecutionMessage = "Current exploration finished";
pathPoints = null; pathPoints = null;
hasTargetPosition = false;
return NodeResult.RUNNING; return NodeResult.RUNNING;
} }
@@ -122,8 +120,7 @@ public class ExploreNode : ProgramNode
{ {
return new ExploreNode return new ExploreNode
{ {
targetPosition = targetPosition, targetPosition = targetPosition
hasTargetPosition = hasTargetPosition
}; };
} }
@@ -131,27 +128,4 @@ public class ExploreNode : ProgramNode
{ {
return $"Name: {DisplayText}"; return $"Name: {DisplayText}";
} }
public override NodeSaveData CreateSaveData()
{
NodeSaveData saveData = base.CreateSaveData();
saveData.TargetX = targetPosition.X;
saveData.TargetY = targetPosition.Y;
saveData.TargetZ = targetPosition.Z;
saveData.HasTargetPosition = hasTargetPosition || pathPoints != null;
return saveData;
}
public override void Load(NodeSaveData saveData)
{
base.Load(saveData);
hasTargetPosition = saveData.HasTargetPosition;
if (!hasTargetPosition) return;
targetPosition = new Vector3I(
saveData.TargetX,
saveData.TargetY,
saveData.TargetZ
);
}
} }
+2 -17
View File
@@ -14,12 +14,12 @@ public class ForNode : ProgramNode
public override NodeResult Execute(Robot robot, double delta) public override NodeResult Execute(Robot robot, double delta)
{ {
bool isConditionFulfilled = DetermineCondition(); bool isConditionFulfilled = DetermineCondition();
amountExecuted++;
if (isConditionFulfilled) if (isConditionFulfilled)
{ {
amountExecuted = 0; amountExecuted = 0;
} }
amountExecuted++; return isConditionFulfilled ? NodeResult.SUCCESS : NodeResult.CONDITIONFALSE;
return isConditionFulfilled ? NodeResult.CONDITIONFALSE : NodeResult.SUCCESS;
} }
private bool DetermineCondition() private bool DetermineCondition()
@@ -42,21 +42,6 @@ public class ForNode : ProgramNode
return $"Name: {DisplayText}, AmountExecuted: {amountExecuted}, Amount: {amount}"; return $"Name: {DisplayText}, AmountExecuted: {amountExecuted}, Amount: {amount}";
} }
public override NodeSaveData CreateSaveData()
{
NodeSaveData saveData = base.CreateSaveData();
saveData.Amount = amount;
saveData.AmountExecuted = amountExecuted;
return saveData;
}
public override void Load(NodeSaveData saveData)
{
base.Load(saveData);
amount = saveData.Amount;
amountExecuted = saveData.AmountExecuted;
}
public override void SetNextNode( public override void SetNextNode(
List<Godot.Collections.Dictionary> connections, List<Godot.Collections.Dictionary> connections,
Dictionary<StringName, ProgramNode> availableNodes Dictionary<StringName, ProgramNode> availableNodes
-20
View File
@@ -60,26 +60,6 @@ public class IfNode : ProgramNode
return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Comparator: {comparator}, Amount: {amount}"; return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Comparator: {comparator}, Amount: {amount}";
} }
public override NodeSaveData CreateSaveData()
{
NodeSaveData saveData = base.CreateSaveData();
saveData.ItemId = selectedItem?.data.Id;
saveData.Comparator = comparator;
saveData.Amount = amount;
return saveData;
}
public override void Load(NodeSaveData saveData)
{
base.Load(saveData);
amount = saveData.Amount;
comparator = saveData.Comparator;
if (string.IsNullOrEmpty(saveData.ItemId)) return;
if (!GameData.availableItems.ContainsKey(saveData.ItemId)) return;
selectedItem = new Item { data = GameData.availableItems[saveData.ItemId] };
}
public override void SetNextNode( public override void SetNextNode(
List<Godot.Collections.Dictionary> connections, List<Godot.Collections.Dictionary> connections,
Dictionary<StringName, ProgramNode> availableNodes Dictionary<StringName, ProgramNode> availableNodes
-22
View File
@@ -98,26 +98,4 @@ public class MoveNode : ProgramNode
{ {
return $"Name: {DisplayText}, Position: ({targetPosition.X}|{targetPosition.Y}|{targetPosition.Z})"; return $"Name: {DisplayText}, Position: ({targetPosition.X}|{targetPosition.Y}|{targetPosition.Z})";
} }
public override NodeSaveData CreateSaveData()
{
NodeSaveData saveData = base.CreateSaveData();
saveData.TargetX = targetPosition.X;
saveData.TargetY = targetPosition.Y;
saveData.TargetZ = targetPosition.Z;
saveData.HasTargetPosition = true;
return saveData;
}
public override void Load(NodeSaveData saveData)
{
base.Load(saveData);
if (!saveData.HasTargetPosition) return;
targetPosition = new Vector3I(
saveData.TargetX,
saveData.TargetY,
saveData.TargetZ
);
}
} }
-57
View File
@@ -14,52 +14,6 @@ public abstract class ProgramNode
public abstract ProgramNode Duplicate(); public abstract ProgramNode Duplicate();
public abstract string Save(); public abstract string Save();
public virtual NodeSaveData CreateSaveData()
{
return new NodeSaveData
{
NodeType = DisplayText,
EditorNodeId = EditorNodeId,
NextEditorNodeId = nextNode?.EditorNodeId,
NegativeEditorNodeId = NegativeNode?.EditorNodeId,
NodeContent = Save(),
LastExecutionMessage = lastExecutionMessage
};
}
public virtual void Load(NodeSaveData saveData)
{
EditorNodeId = saveData.EditorNodeId;
lastExecutionMessage = saveData.LastExecutionMessage;
}
public void RestoreConnections(
NodeSaveData saveData,
Dictionary<string, ProgramNode> availableNodes
)
{
nextNode = GetSavedConnection(saveData.NextEditorNodeId, availableNodes);
NegativeNode = GetSavedConnection(saveData.NegativeEditorNodeId, availableNodes);
}
public static ProgramNode CreateFromSaveData(NodeSaveData saveData)
{
return saveData.NodeType?.ToLower() switch
{
"start" => new StartNode(),
"move" => new MoveNode(),
"explore" => new ExploreNode(),
"harvest" => new HarvestNode(),
"craft" => new CraftNode(),
"if" => new IfNode(),
"while" => new WhileNode(),
"for" => new ForNode(),
"maintain" => new MaintainNode(),
"sacrifice" => new SacrificeNode(),
_ => null
};
}
public ProgramNode DuplicateForRuntime(string editorNodeId) public ProgramNode DuplicateForRuntime(string editorNodeId)
{ {
ProgramNode duplicate = Duplicate(); ProgramNode duplicate = Duplicate();
@@ -111,15 +65,4 @@ public abstract class ProgramNode
return availableNodes[nodeName]; return availableNodes[nodeName];
} }
private ProgramNode GetSavedConnection(
string nodeId,
Dictionary<string, ProgramNode> availableNodes
)
{
if (string.IsNullOrEmpty(nodeId)) return null;
if (!availableNodes.ContainsKey(nodeId)) return null;
return availableNodes[nodeId];
}
} }
-20
View File
@@ -60,26 +60,6 @@ public class WhileNode : ProgramNode
return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Comparator: {comparator}, Amount: {amount}"; return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Comparator: {comparator}, Amount: {amount}";
} }
public override NodeSaveData CreateSaveData()
{
NodeSaveData saveData = base.CreateSaveData();
saveData.ItemId = selectedItem?.data.Id;
saveData.Comparator = comparator;
saveData.Amount = amount;
return saveData;
}
public override void Load(NodeSaveData saveData)
{
base.Load(saveData);
amount = saveData.Amount;
comparator = saveData.Comparator;
if (string.IsNullOrEmpty(saveData.ItemId)) return;
if (!GameData.availableItems.ContainsKey(saveData.ItemId)) return;
selectedItem = new Item { data = GameData.availableItems[saveData.ItemId] };
}
public override void SetNextNode( public override void SetNextNode(
List<Godot.Collections.Dictionary> connections, List<Godot.Collections.Dictionary> connections,
Dictionary<StringName, ProgramNode> availableNodes Dictionary<StringName, ProgramNode> availableNodes
+1 -82
View File
@@ -23,7 +23,6 @@ public partial class Robot : Node3D
public bool isBroken = false; public bool isBroken = false;
public string robotType = "iron_robot"; public string robotType = "iron_robot";
public bool showOnMap = true; public bool showOnMap = true;
public List<ProgramNode> nodes = new List<ProgramNode>();
private RobotTypeStats TypeStats => private RobotTypeStats TypeStats =>
GameData.robotStats.RobotTypes.TryGetValue(robotType, out RobotTypeStats stats) GameData.robotStats.RobotTypes.TryGetValue(robotType, out RobotTypeStats stats)
@@ -108,7 +107,6 @@ public partial class Robot : Node3D
programStartNode = nodes[0]; programStartNode = nodes[0];
currentNode = nodes[0]; currentNode = nodes[0];
currentMessage = ""; currentMessage = "";
this.nodes = nodes;
} }
public void StopExecution(string message) public void StopExecution(string message)
@@ -175,11 +173,7 @@ public partial class Robot : Node3D
Heat = heat, Heat = heat,
Maintenance = maintenance, Maintenance = maintenance,
IsCoolingDown = isCoolingDown, IsCoolingDown = isCoolingDown,
IsBroken = isBroken, IsBroken = isBroken
RunningProgramNodes = CreateProgramSaveData(),
ProgramStartNode = programStartNode?.EditorNodeId,
CurrentNode = currentNode?.EditorNodeId,
IsExecuting = isExecuting
}; };
} }
@@ -194,7 +188,6 @@ public partial class Robot : Node3D
maintenance = saveData.Maintenance; maintenance = saveData.Maintenance;
isCoolingDown = saveData.IsCoolingDown; isCoolingDown = saveData.IsCoolingDown;
isBroken = saveData.IsBroken; isBroken = saveData.IsBroken;
LoadProgramState(saveData);
} }
private bool CanExecute(double delta) private bool CanExecute(double delta)
@@ -290,78 +283,4 @@ public partial class Robot : Node3D
isCoolingDown = false; isCoolingDown = false;
} }
} }
private List<NodeSaveData> CreateProgramSaveData()
{
List<NodeSaveData> result = new List<NodeSaveData>();
if (nodes == null) return result;
foreach (ProgramNode node in nodes)
{
if (node == null) continue;
result.Add(node.CreateSaveData());
}
return result;
}
private void LoadProgramState(RobotSaveData saveData)
{
nodes = new List<ProgramNode>();
programStartNode = null;
currentNode = null;
isExecuting = false;
if (saveData.RunningProgramNodes == null || saveData.RunningProgramNodes.Count <= 0)
{
return;
}
Dictionary<string, ProgramNode> loadedNodes = new Dictionary<string, ProgramNode>();
foreach (NodeSaveData nodeSaveData in saveData.RunningProgramNodes)
{
ProgramNode node = ProgramNode.CreateFromSaveData(nodeSaveData);
if (node == null) continue;
node.Load(nodeSaveData);
nodes.Add(node);
if (!string.IsNullOrEmpty(node.EditorNodeId) && !loadedNodes.ContainsKey(node.EditorNodeId))
{
loadedNodes.Add(node.EditorNodeId, node);
}
}
foreach (NodeSaveData nodeSaveData in saveData.RunningProgramNodes)
{
if (string.IsNullOrEmpty(nodeSaveData.EditorNodeId)) continue;
if (!loadedNodes.ContainsKey(nodeSaveData.EditorNodeId)) continue;
loadedNodes[nodeSaveData.EditorNodeId].RestoreConnections(nodeSaveData, loadedNodes);
}
programStartNode = GetLoadedNode(saveData.ProgramStartNode, loadedNodes);
if (programStartNode == null && nodes.Count > 0)
{
programStartNode = nodes[0];
}
currentNode = GetLoadedNode(saveData.CurrentNode, loadedNodes);
if (currentNode == null)
{
currentNode = programStartNode;
}
isExecuting = saveData.IsExecuting && currentNode != null;
}
private ProgramNode GetLoadedNode(
string nodeId,
Dictionary<string, ProgramNode> loadedNodes
)
{
if (string.IsNullOrEmpty(nodeId)) return null;
if (!loadedNodes.ContainsKey(nodeId)) return null;
return loadedNodes[nodeId];
}
} }
+1 -1
View File
@@ -24,7 +24,7 @@ public class SurvivalState
public void Update(double delta) public void Update(double delta)
{ {
if (isDead || GameData.debugMode) return; if (isDead) return;
elapsedSeconds += delta; elapsedSeconds += delta;
float drainModifier = IsInGracePeriod() ? 0.35f : 1f; float drainModifier = IsInGracePeriod() ? 0.35f : 1f;
+3 -64
View File
@@ -23,7 +23,6 @@ public partial class TestRunner : Node
Run("Resource extraction and save data roundtrip", TestResourceSaveRoundtrip); Run("Resource extraction and save data roundtrip", TestResourceSaveRoundtrip);
Run("Endless resources extract slower", TestEndlessResourcesExtractSlower); Run("Endless resources extract slower", TestEndlessResourcesExtractSlower);
Run("Robot save data roundtrip keeps robot state", TestRobotSaveRoundtrip); Run("Robot save data roundtrip keeps robot state", TestRobotSaveRoundtrip);
Run("Robot save data roundtrip keeps running program", TestRobotProgramSaveRoundtrip);
Run("Maintain node consumes matching gear", TestMaintainNodeConsumesMatchingGear); Run("Maintain node consumes matching gear", TestMaintainNodeConsumesMatchingGear);
Run("Sacrifice node makes resource endless", TestSacrificeNodeMakesResourceEndless); Run("Sacrifice node makes resource endless", TestSacrificeNodeMakesResourceEndless);
Run("No robot recovery detects loss", TestNoRobotRecoveryDetectsLoss); Run("No robot recovery detects loss", TestNoRobotRecoveryDetectsLoss);
@@ -230,66 +229,6 @@ public partial class TestRunner : Node
AssertTrue(loadedRobot.isCoolingDown, "robot cooling state"); AssertTrue(loadedRobot.isCoolingDown, "robot cooling state");
} }
private void TestRobotProgramSaveRoundtrip()
{
StartNode startNode = new StartNode { EditorNodeId = "start" };
ForNode forNode = new ForNode
{
EditorNodeId = "loop",
amount = 3,
amountExecuted = 2
};
CraftNode craftNode = new CraftNode
{
EditorNodeId = "craft",
selectedItem = new Item
{
data = GameData.availableItems["stone"],
elapsedCraftTime = 0.5,
amountCrafted = 1
},
amount = 2
};
HarvestNode harvestNode = new HarvestNode { EditorNodeId = "done" };
startNode.nextNode = forNode;
forNode.nextNode = craftNode;
forNode.NegativeNode = harvestNode;
craftNode.nextNode = forNode;
Robot robot = new Robot
{
Name = "Loop Bot",
currentProgram = "Looping",
isExecuting = true,
programStartNode = startNode,
currentNode = craftNode,
nodes = new List<ProgramNode> { startNode, forNode, craftNode, harvestNode }
};
RobotSaveData saveData = robot.CreateSaveData();
Robot loadedRobot = new Robot();
loadedRobot.LoadSaveData(saveData);
AssertTrue(loadedRobot.isExecuting, "loaded robot should still execute");
AssertEqual("craft", loadedRobot.currentNode.EditorNodeId, "current node id");
AssertEqual("start", loadedRobot.programStartNode.EditorNodeId, "start node id");
AssertEqual(4, loadedRobot.nodes.Count, "loaded node count");
ForNode loadedForNode = loadedRobot.nodes[1] as ForNode;
CraftNode loadedCraftNode = loadedRobot.nodes[2] as CraftNode;
AssertEqual(3, loadedForNode.amount, "for amount");
AssertEqual(2, loadedForNode.amountExecuted, "for amount executed");
AssertTrue(ReferenceEquals(loadedCraftNode, loadedForNode.nextNode), "for success branch");
AssertTrue(ReferenceEquals(loadedRobot.nodes[3], loadedForNode.NegativeNode), "for negative branch");
AssertTrue(ReferenceEquals(loadedForNode, loadedCraftNode.nextNode), "craft loops back");
AssertEqual("stone", loadedCraftNode.selectedItem.data.Id, "craft item");
AssertEqual(2, loadedCraftNode.amount, "craft amount");
AssertClose(0.5f, (float)loadedCraftNode.selectedItem.elapsedCraftTime, 0.001f, "craft elapsed");
AssertEqual(1, loadedCraftNode.selectedItem.amountCrafted, "craft crafted amount");
}
private void TestMaintainNodeConsumesMatchingGear() private void TestMaintainNodeConsumesMatchingGear()
{ {
Robot robot = new Robot Robot robot = new Robot
@@ -536,9 +475,9 @@ public partial class TestRunner : Node
amount = 2 amount = 2
}; };
AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "first iteration"); AssertEqual(NodeResult.CONDITIONFALSE, node.Execute(null, 0), "first iteration");
AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "second iteration"); AssertEqual(NodeResult.CONDITIONFALSE, node.Execute(null, 0), "second iteration");
AssertEqual(NodeResult.CONDITIONFALSE, node.Execute(null, 0), "loop finished"); AssertEqual(NodeResult.SUCCESS, node.Execute(null, 0), "loop finished");
} }
private void TestRuntimeNodeKeepsEditorIdWithoutSharingState() private void TestRuntimeNodeKeepsEditorIdWithoutSharingState()
-1
View File
@@ -39,7 +39,6 @@ public partial class CodingWindow : PanelContainer
if (focused is LineEdit || focused is TextEdit) return; if (focused is LineEdit || focused is TextEdit) return;
if (selectedNode == null) return; if (selectedNode == null) return;
if (selectedNode == highlightedNode) highlightedNode = null;
editorWindow.RemoveChild(selectedNode); editorWindow.RemoveChild(selectedNode);
selectedNode.QueueFree(); selectedNode.QueueFree();
} }
+1 -2
View File
@@ -101,11 +101,10 @@ public partial class RobotList : PanelContainer
robot.Position = GameData.map[0].tiles[0, 0].Position; robot.Position = GameData.map[0].tiles[0, 0].Position;
robot.robotType = spawnId; robot.robotType = spawnId;
GetNode("/root/Main/World").AddChild(robot); GetNode("/root/Main/World").AddChild(robot);
LightHandler.lights.Add(robot.GetNode("./SpotLight3D") as SpotLight3D);
robot.Name = $"Robot #{GameData.robots.Count}"; robot.Name = $"Robot #{GameData.robots.Count}";
GameData.robots.Add(robot); GameData.robots.Add(robot);
spawnId = ""; spawnId = "";
LightHandler.RedrawLights(GameData.lightColor);
ReloadRobots(); ReloadRobots();
ReloadSelectableRobots(); ReloadSelectableRobots();
} }
-1
View File
@@ -49,7 +49,6 @@ public partial class TutorialBubble : PanelContainer
"The inventory (Default: [I]) stores everything your robots collect. Gates and research both consume items from it.", "The inventory (Default: [I]) stores everything your robots collect. Gates and research both consume items from it.",
"Each gate blocks the next layer. When you have the required items, use Open Gate in the top bar.", "Each gate blocks the next layer. When you have the required items, use Open Gate in the top bar.",
"The required ingredients can be found by hovering over the Open Gate button.", "The required ingredients can be found by hovering over the Open Gate button.",
"You can swap between layers using the [Q] (up one layer) and [E] (down one layer) keys.",
"The map (Default: [M]) shows what your robots have discovered. Exploration matters because resources are hidden in the ruin.", "The map (Default: [M]) shows what your robots have discovered. Exploration matters because resources are hidden in the ruin.",
"Deeper layers contain more advanced resources. Unlocking the gate at the lowest point allows you to leave the ruin.", "Deeper layers contain more advanced resources. Unlocking the gate at the lowest point allows you to leave the ruin.",
"Be careful: resources are not endless at the beginning. Converting a robot to a drill unit with Sacrifice makes them endless.", "Be careful: resources are not endless at the beginning. Converting a robot to a drill unit with Sacrifice makes them endless.",
+3 -3
View File
@@ -3,13 +3,13 @@ using System.Collections.Generic;
public static class LightHandler public static class LightHandler
{ {
public static List<SpotLight3D> lights = new List<SpotLight3D>(); public static List<OmniLight3D> lights = new List<OmniLight3D>();
public static void RedrawLights(Color color) public static void RedrawLights(Color color)
{ {
List<SpotLight3D> availableLights = new List<SpotLight3D>(); List<OmniLight3D> availableLights = new List<OmniLight3D>();
foreach (SpotLight3D light in lights) foreach (OmniLight3D light in lights)
{ {
if (!GodotObject.IsInstanceValid(light)) continue; if (!GodotObject.IsInstanceValid(light)) continue;
-5
View File
@@ -270,11 +270,6 @@ public partial class Map : PanelContainer
tooltipText += GetRobotTooltip(robotsOnTile, tooltipText.Length > 0); tooltipText += GetRobotTooltip(robotsOnTile, tooltipText.Length > 0);
} }
if (tile.collapsedMesh == "gate" && tile.wasVisited)
{
tileTexture = AddBorder(tileTexture, Colors.Red, RobotBorderWidth);
}
texture.Texture = tileTexture; texture.Texture = tileTexture;
texture.TooltipText = tooltipText; texture.TooltipText = tooltipText;
} }
+28 -2
View File
@@ -11,7 +11,7 @@ public partial class Tile
public Vector2I GridPosition; public Vector2I GridPosition;
public Node3D ContentNode; public Node3D ContentNode;
public bool containsDecoration, containsResource; public bool containsLight, containsDecoration, containsResource;
public GameResource resource; public GameResource resource;
public bool wasVisited; public bool wasVisited;
public event EventHandler OnTileVisited; public event EventHandler OnTileVisited;
@@ -74,6 +74,7 @@ public partial class Tile
public void Reset(Dictionary<string, MeshInstance3D> tileMeshes) public void Reset(Dictionary<string, MeshInstance3D> tileMeshes)
{ {
collapsedMesh = null; collapsedMesh = null;
containsLight = false;
containsDecoration = false; containsDecoration = false;
containsResource = false; containsResource = false;
resource = null; resource = null;
@@ -85,11 +86,36 @@ public partial class Tile
{ {
foreach (Placeholder placeholder in placeholders) foreach (Placeholder placeholder in placeholders)
{ {
if (containsResource && placeholder.name == "resource") SpawnResource(contentMeshes["resource"], placeholder, transform); if (containsLight && placeholder.name == "light") SpawnLight(contentMeshes["light"], placeholder, transform);
else if (containsResource && placeholder.name == "resource") SpawnResource(contentMeshes["resource"], placeholder, transform);
else if (containsDecoration && placeholder.name != "light" && placeholder.name != "resource") SpawnDecorations(contentMeshes, placeholder, transform); else if (containsDecoration && placeholder.name != "light" && placeholder.name != "resource") SpawnDecorations(contentMeshes, placeholder, transform);
} }
} }
private void SpawnLight(MeshInstance3D lightMesh, Placeholder placeholder, Transform3D transform)
{
MeshInstance3D light = new MeshInstance3D
{
Mesh = lightMesh.Mesh,
Position = placeholder.transform.Origin
};
OmniLight3D lightSource = new OmniLight3D()
{
OmniAttenuation = 2f,
LightColor = GameData.lightColor,
ShadowEnabled = true,
LightEnergy = 100f,
LightIndirectEnergy = 1.5f,
OmniRange = 20f,
Position = placeholder.transform.Origin
};
lightSource.Position.MoveToward(transform.Origin, 0.1f);
LightHandler.lights.Add(lightSource);
light.AddChild(lightSource);
ContentNode.AddChild(light);
light.LookAt(transform.Origin, Vector3.Up);
}
private void SpawnResource(MeshInstance3D resourceMesh, Placeholder placeholder, Transform3D transform) private void SpawnResource(MeshInstance3D resourceMesh, Placeholder placeholder, Transform3D transform)
{ {
MeshInstance3D resource = new MeshInstance3D MeshInstance3D resource = new MeshInstance3D
+18 -2
View File
@@ -142,8 +142,6 @@ public partial class World : Node3D
robot.Position = map[0].tiles[0, 0].Position; robot.Position = map[0].tiles[0, 0].Position;
AddChild(robot); AddChild(robot);
robots.Add(robot); robots.Add(robot);
LightHandler.lights.Add(robot.GetNode("./SpotLight3D") as SpotLight3D);
LightHandler.RedrawLights(lightColor);
} }
private void SpawnSavedRobots(List<RobotSaveData> savedRobots) private void SpawnSavedRobots(List<RobotSaveData> savedRobots)
@@ -195,6 +193,7 @@ public partial class World : Node3D
{ {
map[0].tiles[0, 0].wasVisited = true; map[0].tiles[0, 0].wasVisited = true;
map[0].tiles[0, 0].containsDecoration = true; map[0].tiles[0, 0].containsDecoration = true;
map[0].tiles[0, 0].containsLight = true;
map[0].tiles[0, 0].containsResource = false; map[0].tiles[0, 0].containsResource = false;
map[0].tiles[0, 0].ContentNode.Visible = true; map[0].tiles[0, 0].ContentNode.Visible = true;
} }
@@ -235,10 +234,22 @@ public partial class World : Node3D
private void DistributeTileContent(Layer layer) private void DistributeTileContent(Layer layer)
{ {
int currentDecoration = 0; int currentDecoration = 0;
int currentLight = 0;
int currentResource = 0; int currentResource = 0;
int posX, posY; int posX, posY;
while (currentLight < layerSize * 3)
{
posX = rand.Next(layerSize);
posY = rand.Next(layerSize);
if (CannotContainLight(layer.tiles[posX, posY])) continue;
if (layer.tiles[posX, posY].containsLight) continue;
layer.tiles[posX, posY].containsLight = true;
currentLight++;
}
while (currentDecoration < layerSize) while (currentDecoration < layerSize)
{ {
posX = rand.Next(layerSize); posX = rand.Next(layerSize);
@@ -262,6 +273,11 @@ public partial class World : Node3D
} }
} }
private bool CannotContainLight(Tile tile)
{
return tile.collapsedMesh == "junction" || tile.collapsedMesh == "gate";
}
private void HandleRenderData(List<TileRenderData> renderData) private void HandleRenderData(List<TileRenderData> renderData)
{ {
multiMeshHandler.Build(renderData); multiMeshHandler.Build(renderData);