From 18b76f3cbcbcbfb04c996ef73bc91c6e453f8145 Mon Sep 17 00:00:00 2001 From: Nicola Date: Wed, 6 May 2026 09:29:28 +0200 Subject: [PATCH] Added ability to delete nodes from editor, added complete node load and save for the DSL --- Assets/Images/Sources/TrashSymbol.aseprite | Bin 0 -> 439 bytes Assets/Images/TrashSymbol.png | Bin 0 -> 258 bytes Assets/Images/TrashSymbol.png.import | 40 ++++++ Prefabs/DSL/CraftNode.tscn | 13 +- Prefabs/DSL/ExploreNode.tscn | 11 ++ Prefabs/DSL/HarvestNode.tscn | 13 +- Prefabs/DSL/MoveNode.tscn | 14 ++ Scenes/Game.tscn | 12 +- Scripts/DSL/CodingWindow.cs | 96 ++++++++++++- Scripts/DSL/NodeDisplay.cs | 96 ++++++++++++- Scripts/DSL/Nodes/CraftNode.cs | 151 ++++++++++++--------- Scripts/DSL/Nodes/ExploreNode.cs | 5 + Scripts/DSL/Nodes/HarvestNode.cs | 5 + Scripts/DSL/Nodes/MoveNode.cs | 5 + Scripts/DSL/Nodes/ProgramNode.cs | 1 + Scripts/Helpers/FileHandler.cs | 57 ++++++++ Scripts/Helpers/FileHandler.cs.uid | 1 + Scripts/Helpers/GameData.cs | 2 +- Scripts/Helpers/ResourceLoader.cs | 4 +- Scripts/Helpers/UIHandler.cs | 9 +- 20 files changed, 451 insertions(+), 84 deletions(-) create mode 100644 Assets/Images/Sources/TrashSymbol.aseprite create mode 100644 Assets/Images/TrashSymbol.png create mode 100644 Assets/Images/TrashSymbol.png.import create mode 100644 Scripts/Helpers/FileHandler.cs create mode 100644 Scripts/Helpers/FileHandler.cs.uid diff --git a/Assets/Images/Sources/TrashSymbol.aseprite b/Assets/Images/Sources/TrashSymbol.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..50575450b59b631937e130c9464764d1322fd829 GIT binary patch literal 439 zcmdna$iVPmDIyADL{+>3Ji=OK?VUJqy;bsn*Qq}3)of`pao(;%&q{o63MzO zAe)5&D5?NtC@Gk@YS^S%8uo`--!D|VxghP`;aR^wnaz9}khRaid`g~orhbsOijj?_ zy?u#6LQ{0!!{pij+;5ND{ElboQy`KLW8iZN;? zzuESs>tuUYCh%{POfWXyyy?NS!#7^M@-XDTtCRCgx%=Tpepg;AwWBp9Jvr>X25uG) zi}?*_1lU}-T$7X7*KeEkuftsaC|}T7^+Rt0KeJE#x#W>!k{vVi*HhJO3<0OUYXbmn Ct!qyJ literal 0 HcmV?d00001 diff --git a/Assets/Images/TrashSymbol.png b/Assets/Images/TrashSymbol.png new file mode 100644 index 0000000000000000000000000000000000000000..b3163e207bd4a868f8943017a9bb3c742d225f2e GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}dpunnLn2z= zPV?q!HV|-_ztsK3x-Si1*;*eh;*bluQ`uf}G31f@*%|ynGuRt%raUv8>Eal8$)L=& z_|$VDchQ~a8MgScANc&WuZ(BIt-)pBIxykA!@YOp^VgDkT DSLNodes; - Robot robot; - + [Export] OptionButton availableScripts; + [Export] LineEdit scriptName; + + //Renaming + [Export] LineEdit nameInput; // Called when the node enters the scene tree for the first time. public override void _Ready() @@ -20,10 +26,46 @@ public partial class CodingWindow : PanelContainer // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { - + + } + + public override void _Notification(int id) + { + if (id == NotificationVisibilityChanged) + { + if (Visible) LoadWindow(); + } + } + + private void LoadWindow() + { + nameInput.Text = robot.Name; + SetupScriptOptions(); + ClearWindow(); + } + + private void SetupScriptOptions() + { + availableScripts.Clear(); + availableScripts.AddItem("Select script to load..."); + List scripts = FileHandler.LoadProgramNames(); + scripts.Sort((a, b) => a.CompareTo(b)); + foreach (string script in scripts) + { + availableScripts.AddItem(script); + } + } + + public void SaveRobotName() + { + robot.Name = nameInput.Text; + } + + public void CloseWindow() + { + Hide(); } - //Move, Harvest, Craft public void GenerateCodingBlocks() { NodeDisplay nodeDisplay; @@ -39,6 +81,10 @@ public partial class CodingWindow : PanelContainer editorDisplay.node = node; editorWindow.AddChild(editorDisplay); editorDisplay.ShowEditorDisplay(); + editorDisplay.OnDeleteNode += () => + { + editorWindow.RemoveChild(editorDisplay); + }; }; } } @@ -50,6 +96,7 @@ public partial class CodingWindow : PanelContainer editorWindow.RemoveChild(node); node.QueueFree(); } + scriptName.Text = ""; } public void CompileProgram() @@ -62,15 +109,52 @@ public partial class CodingWindow : PanelContainer nodes.Add(editorWindow.GetChild(i).node.Duplicate()); if (i != 0) { - nodes[i-1].nextNode = nodes[i]; + nodes[i - 1].nextNode = nodes[i]; } } - if(nodes.Count > 0) robot.SetupExecution(nodes); + if (nodes.Count > 0) robot.SetupExecution(nodes); } public void SetRobot(Robot robot) { this.robot = robot; } + + public void LoadProgram(int index) + { + ClearWindow(); + string scriptContent = FileHandler.LoadProgram(availableScripts.GetItemText(index)); + string[] nodes = scriptContent.Split(";"); + foreach (string node in nodes) + { + NodeDisplay nodeDisplay = NodeDisplay.Load(node, DSLNodes); + if (nodeDisplay != null) + { + editorWindow.AddChild(nodeDisplay); + nodeDisplay.ShowEditorDisplay(); + nodeDisplay.OnDeleteNode += () => + { + editorWindow.RemoveChild(nodeDisplay); + }; + } + } + scriptName.Text = availableScripts.GetItemText(index); + availableScripts.Select(0); + } + + public void SaveProgram() + { + string result = ""; + for (int i = 0; i < editorWindow.GetChildCount(); i++) + { + editorWindow.GetChild(i).node.ReadParameters(editorWindow.GetChild(i)); + result += editorWindow.GetChild(i).node.Save(); + result += ";\r\n"; + } + if (result.Length <= 0) return; + string filename = scriptName.Text.Length <= 0 ? $"Script{availableScripts.ItemCount}" : scriptName.Text; + FileHandler.SaveProgram(filename, result); + SetupScriptOptions(); + } } diff --git a/Scripts/DSL/NodeDisplay.cs b/Scripts/DSL/NodeDisplay.cs index dc57b66..3765893 100644 --- a/Scripts/DSL/NodeDisplay.cs +++ b/Scripts/DSL/NodeDisplay.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Godot; public partial class NodeDisplay : PanelContainer @@ -7,6 +8,9 @@ public partial class NodeDisplay : PanelContainer [Export] public Button listDisplay; public ProgramNode node; + [Signal] + public delegate void OnDeleteNodeEventHandler(); + public void SetNode(ProgramNode node) { this.node = node; @@ -19,7 +23,7 @@ public partial class NodeDisplay : PanelContainer public override void _Process(double delta) { - + } public void ShowListDisplay() @@ -33,4 +37,94 @@ public partial class NodeDisplay : PanelContainer editorDisplay.Visible = true; listDisplay.Visible = false; } + + public void DeleteNodePressed() + { + EmitSignal(SignalName.OnDeleteNode); + } + + public static NodeDisplay Load(string content, Dictionary DSLNodes) + { + NodeDisplay result = null; + ProgramNode program; + string nodeName; + string nodeSanitized; + PackedScene prefab = null; + nodeSanitized = content.Replace("\r\n", ""); + nodeName = nodeSanitized.Split(",")[0].Replace("Name: ", "").ToLower(); + foreach (ProgramNode programNode in DSLNodes.Keys) + { + if (programNode.DisplayText.ToLower() == nodeName) + { + prefab = DSLNodes[programNode]; + break; + } + } + switch (nodeName) + { + case "move": + program = new MoveNode(); + result = prefab.Instantiate(); + result.node = program; + result.LoadMove(nodeSanitized); + break; + case "harvest": + program = new HarvestNode(); + result = prefab.Instantiate(); + result.node = program; + result.LoadHarvest(nodeSanitized); + break; + case "explore": + program = new ExploreNode(); + result = prefab.Instantiate(); + result.node = program; + result.LoadExplore(nodeSanitized); + break; + case "craft": + program = new CraftNode(); + result = prefab.Instantiate(); + result.node = program; + result.LoadCraft(nodeSanitized); + break; + } + return result; + } + + public void LoadHarvest(string content) + { + //Currently does nothing + } + + public void LoadMove(string content) + { + HBoxContainer valueContainer = GetNode("./EditorDisplay/HBoxContainer/"); + string[] parts = content.Split(","); + string positionValues = parts[1].Replace("Position: ", "").Replace("(", "").Replace(")", ""); + int posX = int.Parse(positionValues.Split("|")[0]); + int posY = int.Parse(positionValues.Split("|")[1]); + int posZ = int.Parse(positionValues.Split("|")[2]); + valueContainer.GetNode("./CoordinateX").Value = posX; + valueContainer.GetNode("./CoordinateY").Value = posY; + valueContainer.GetNode("./CoordinateZ").Value = posZ; + + (node as MoveNode).targetPosition = new Vector3I(posX, posY, posZ); + } + + public void LoadExplore(string content) + { + //Currently does nothing + } + + public void LoadCraft(string content) + { + HBoxContainer valueContainer = GetNode("./EditorDisplay/HBoxContainer/"); + string[] parts = content.Split(","); + string itemString = parts[1].Replace("Item: ", "").Replace(" ", ""); + if (itemString.ToLower() != "empty") + { + (node as CraftNode).selectedItem = new Item { data = GameData.availableItems[itemString] }; + } + string amountString = parts[2].Replace("Amount: ", ""); + valueContainer.GetNode("./Amount").Value = int.Parse(amountString); + } } diff --git a/Scripts/DSL/Nodes/CraftNode.cs b/Scripts/DSL/Nodes/CraftNode.cs index 7949e97..0e6e501 100644 --- a/Scripts/DSL/Nodes/CraftNode.cs +++ b/Scripts/DSL/Nodes/CraftNode.cs @@ -1,75 +1,92 @@ +using System.Linq; using Godot; public class CraftNode : ProgramNode { - Item selectedItem; - int amount; - public CraftNode() - { - DisplayText = "Craft"; - } - public override NodeResult Execute(Robot robot, double delta) - { - if (selectedItem == null) - { - lastExecutionMessage = "No Item selected"; - return NodeResult.FAILURE; - } - if (amount <= 0) - { - lastExecutionMessage = "Amount has to be atleast 1"; - return NodeResult.FAILURE; - } - if (!GameData.inventory.CanCraft(selectedItem.data.Inputs, amount)) - { - lastExecutionMessage = "Not enough items to craft this"; - return NodeResult.FAILURE; - } + public Item selectedItem; + public int amount; + public CraftNode() + { + DisplayText = "Craft"; + } + public override NodeResult Execute(Robot robot, double delta) + { + if (selectedItem == null) + { + lastExecutionMessage = "No Item selected"; + return NodeResult.FAILURE; + } + if (amount <= 0) + { + lastExecutionMessage = "Amount has to be atleast 1"; + return NodeResult.FAILURE; + } + if (!GameData.inventory.CanCraft(selectedItem.data.Inputs, amount)) + { + lastExecutionMessage = "Not enough items to craft this"; + return NodeResult.FAILURE; + } - switch (selectedItem.Craft(amount, delta)) - { - case CraftingResult.FAILED: - lastExecutionMessage = "Not enough space to add item to inventory"; - return NodeResult.FAILURE; - case CraftingResult.FINISHED: - return NodeResult.SUCCESS; - } - - return NodeResult.RUNNING; - } + switch (selectedItem.Craft(amount, delta)) + { + case CraftingResult.FAILED: + lastExecutionMessage = "Not enough space to add item to inventory"; + return NodeResult.FAILURE; + case CraftingResult.FINISHED: + return NodeResult.SUCCESS; + } - public override void ReadParameters(NodeDisplay display) - { - HBoxContainer valueContainer = display.GetNode("./EditorDisplay/HBoxContainer/"); - OptionButton items = valueContainer.GetNode("./Item"); - string readableItem = items.GetItemText(items.GetSelectedId()); - if (GameData.availableItems.ContainsKey(ItemData.GetIndex(readableItem))) - { - selectedItem = new Item { data = GameData.availableItems[ItemData.GetIndex(readableItem)]}; - } - amount = (int)valueContainer.GetNode("./Amount").Value; - } + return NodeResult.RUNNING; + } - public override ProgramNode Duplicate() - { - CraftNode duplicate = new CraftNode() - { - selectedItem = selectedItem, - amount = amount - }; - return duplicate; - } + public override void ReadParameters(NodeDisplay display) + { + HBoxContainer valueContainer = display.GetNode("./EditorDisplay/HBoxContainer/"); + OptionButton items = valueContainer.GetNode("./Item"); + string readableItem = items.GetItemText(items.GetSelectedId()); + if (GameData.availableItems.ContainsKey(ItemData.GetIndex(readableItem))) + { + selectedItem = new Item { data = GameData.availableItems[ItemData.GetIndex(readableItem)] }; + } + amount = (int)valueContainer.GetNode("./Amount").Value; + } - public override void Setup(NodeDisplay display) - { - OptionButton options = display.GetNode("./EditorDisplay/HBoxContainer/Item"); - options.AddItem("Select item..."); - foreach (ItemData item in GameData.availableItems.Values) - { - if (item.Inputs.Count > 0) - { - options.AddItem(item.GetReadableName()); - } - } - } -} \ No newline at end of file + public override ProgramNode Duplicate() + { + CraftNode duplicate = new CraftNode() + { + selectedItem = selectedItem, + amount = amount + }; + return duplicate; + } + + public override void Setup(NodeDisplay display) + { + OptionButton options = display.GetNode("./EditorDisplay/HBoxContainer/Item"); + options.AddItem("Select item..."); + foreach (ItemData item in GameData.availableItems.Values) + { + if (item.Inputs.Count > 0) + { + options.AddItem(item.GetReadableName()); + } + } + if (selectedItem != null) + { + for (int i = 0; i < options.ItemCount; i++) + { + if (ItemData.GetIndex(options.GetItemText(i)) == selectedItem.data.Id) + { + options.Select(i); + break; + } + } + } + } + + public override string Save() + { + return $"Name: {DisplayText}, Item: {(selectedItem == null ? "Empty" : selectedItem.data.Id)}, Amount: {amount}"; + } +} diff --git a/Scripts/DSL/Nodes/ExploreNode.cs b/Scripts/DSL/Nodes/ExploreNode.cs index a4d6235..2b5f1da 100644 --- a/Scripts/DSL/Nodes/ExploreNode.cs +++ b/Scripts/DSL/Nodes/ExploreNode.cs @@ -92,4 +92,9 @@ public class ExploreNode : ProgramNode { //Currently does nothing } + + public override string Save() + { + return $"Name: {DisplayText}"; + } } \ No newline at end of file diff --git a/Scripts/DSL/Nodes/HarvestNode.cs b/Scripts/DSL/Nodes/HarvestNode.cs index 76bd852..8082ced 100644 --- a/Scripts/DSL/Nodes/HarvestNode.cs +++ b/Scripts/DSL/Nodes/HarvestNode.cs @@ -49,4 +49,9 @@ public class HarvestNode : ProgramNode { //Currently does nothing } + + public override string Save() + { + return $"Name: {DisplayText}"; + } } \ No newline at end of file diff --git a/Scripts/DSL/Nodes/MoveNode.cs b/Scripts/DSL/Nodes/MoveNode.cs index 6e683d3..97ab613 100644 --- a/Scripts/DSL/Nodes/MoveNode.cs +++ b/Scripts/DSL/Nodes/MoveNode.cs @@ -77,4 +77,9 @@ public class MoveNode : ProgramNode { //Currently does nothing } + + public override string Save() + { + return $"Name: {DisplayText}, Position: ({targetPosition.X}|{targetPosition.Y}|{targetPosition.Z})"; + } } \ No newline at end of file diff --git a/Scripts/DSL/Nodes/ProgramNode.cs b/Scripts/DSL/Nodes/ProgramNode.cs index 5cef70b..091c1e7 100644 --- a/Scripts/DSL/Nodes/ProgramNode.cs +++ b/Scripts/DSL/Nodes/ProgramNode.cs @@ -10,4 +10,5 @@ public abstract class ProgramNode public abstract NodeResult Execute(Robot robot, double delta); public abstract void ReadParameters(NodeDisplay display); public abstract ProgramNode Duplicate(); + public abstract string Save(); } \ No newline at end of file diff --git a/Scripts/Helpers/FileHandler.cs b/Scripts/Helpers/FileHandler.cs new file mode 100644 index 0000000..befb9ce --- /dev/null +++ b/Scripts/Helpers/FileHandler.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using Godot; + +public class FileHandler() +{ + public static void CreateScriptDirectory() + { + DirAccess.MakeDirRecursiveAbsolute("user://scripts"); + } + + public static void SaveProgram(string filename, string content) + { + CreateScriptDirectory(); + string path = $"user://scripts/{filename}.dsl"; + + FileAccess file = FileAccess.Open(path, FileAccess.ModeFlags.Write); + file.StoreString(content); + } + + public static List LoadProgramNames() + { + CreateScriptDirectory(); + List programs = new List(); + + DirAccess dir = DirAccess.Open("user://scripts"); + if (dir == null) + return programs; + + dir.ListDirBegin(); + while (true) + { + string fileName = dir.GetNext(); + if (fileName == "") + break; + + if (!dir.CurrentIsDir() && fileName.EndsWith(".dsl")) + { + programs.Add(fileName.Replace(".dsl", "")); + } + } + dir.ListDirEnd(); + + return programs; + } + + public static string LoadProgram(string name) + { + CreateScriptDirectory(); + string path = $"user://scripts/{name}.dsl"; + + if (!FileAccess.FileExists(path)) + return ""; + + FileAccess file = FileAccess.Open(path, FileAccess.ModeFlags.Read); + return file.GetAsText(); + } +} \ No newline at end of file diff --git a/Scripts/Helpers/FileHandler.cs.uid b/Scripts/Helpers/FileHandler.cs.uid new file mode 100644 index 0000000..42726cd --- /dev/null +++ b/Scripts/Helpers/FileHandler.cs.uid @@ -0,0 +1 @@ +uid://y6p1ybasi0c2 diff --git a/Scripts/Helpers/GameData.cs b/Scripts/Helpers/GameData.cs index bb7be84..dccc216 100644 --- a/Scripts/Helpers/GameData.cs +++ b/Scripts/Helpers/GameData.cs @@ -18,7 +18,7 @@ public partial class GameData public static float robotSpeed = 20f; public static float tileWidth = 6; public static float tileHeight = 4; - public static Dictionary availableItems = ResourceLoader.LoadItems(); + public static SortedDictionary availableItems = ResourceLoader.LoadItems(); //--- PLAYER ADJUSTABLE VALUES --- //Color used in primary objects (e.g. Robots) diff --git a/Scripts/Helpers/ResourceLoader.cs b/Scripts/Helpers/ResourceLoader.cs index 4a881e1..2bed49c 100644 --- a/Scripts/Helpers/ResourceLoader.cs +++ b/Scripts/Helpers/ResourceLoader.cs @@ -85,13 +85,13 @@ public partial class ResourceLoader return symbols; } - public static Dictionary LoadItems() + public static SortedDictionary LoadItems() { FileAccess file = FileAccess.Open("res://Assets/Recipes.json", FileAccess.ModeFlags.Read); string json = file.GetAsText(); - Dictionary result = new(); + SortedDictionary result = new(); List items = JsonSerializer.Deserialize>(json); foreach (ItemData item in items) diff --git a/Scripts/Helpers/UIHandler.cs b/Scripts/Helpers/UIHandler.cs index 69c52b4..0508db1 100644 --- a/Scripts/Helpers/UIHandler.cs +++ b/Scripts/Helpers/UIHandler.cs @@ -26,6 +26,7 @@ public partial class UIHandler : Control // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { + DisplayStats(); robotList.OnRobotJumpTo += (robot) => { if(receivedRobotJumpSignal) return; @@ -35,11 +36,17 @@ public partial class UIHandler : Control OpenUIElement(codingWindow); }; + //Enable user to write in input fields + Control focused = GetViewport().GuiGetFocusOwner(); + + if (focused is LineEdit || focused is TextEdit) + return; + if (Input.IsActionJustPressed("map")) HandleMapButton(); if (Input.IsActionJustPressed("menu")) HandleMenuButton(); if (Input.IsActionJustPressed("robot_list")) HandleRobotListButton(); if (Input.IsActionJustPressed("inventory")) HandleInventoryButton(); - DisplayStats(); + } public void HandleMenuButton()