using Godot; using Godot.Collections; using System.Collections.Generic; public partial class CodingWindow : PanelContainer { private Robot robot; [Export] VBoxContainer codeBlocks; [Export] GraphEdit editorWindow; [Export] OptionButton availableScripts; [Export] LineEdit scriptName; [Export] LineEdit nameInput; public System.Collections.Generic.Dictionary DSLNodes; public override void _Ready() { DSLNodes = ResourceLoader.LoadDSLNodes(); GenerateCodingBlocks(); } public override void _Notification(int id) { if (id == NotificationVisibilityChanged) { if (Visible) LoadWindow(); } } private void LoadWindow() { if (robot == null) return; nameInput.Text = robot.Name; SetupScriptOptions(); ClearWindow(); LoadTemporaryProgram(); } 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() { if (robot == null) return; robot.Name = nameInput.Text; } public void CloseWindow() { Hide(); } public void GenerateCodingBlocks() { Button nodeListButton; foreach (ProgramNode nodeTemplate in DSLNodes.Keys) { nodeListButton = new Button { Name = nodeTemplate.DisplayText, Text = nodeTemplate.DisplayText }; nodeListButton.Pressed += () => { AddEditorNode(nodeTemplate); }; codeBlocks.AddChild(nodeListButton); } } private void AddEditorNode(ProgramNode node) { NodeDisplay editorDisplay = DSLNodes[node].Instantiate(); editorWindow.AddChild(editorDisplay); RegisterEditorNode(editorDisplay); } private void RegisterEditorNode(NodeDisplay editorDisplay) { editorDisplay.OnDeleteNode += () => { editorWindow.RemoveChild(editorDisplay); editorDisplay.QueueFree(); }; } public void ClearWindow() { foreach (Dictionary connection in editorWindow.GetConnectionList()) { editorWindow.DisconnectNode( connection["from_node"].AsStringName(), (int)connection["from_port"], connection["to_node"].AsStringName(), (int)connection["to_port"] ); } foreach (Node child in editorWindow.GetChildren()) { if (child is GraphNode) { editorWindow.RemoveChild(child); child.QueueFree(); } } scriptName.Text = ""; } public void CompileProgram() { List nodes = new List(); for (int i = 0; i < editorWindow.GetChildCount(); i++) { NodeDisplay nodeDisplay = editorWindow.GetChild(i) as NodeDisplay; if (nodeDisplay == null) continue; nodeDisplay.ReadParameters(); ProgramNode executableNode = nodeDisplay.node.Duplicate(); nodes.Add(executableNode); if (nodes.Count > 1) { nodes[nodes.Count - 2].nextNode = executableNode; } if (nodes.Count > 1) { executableNode.previousNode = nodes[nodes.Count - 2]; } } if (robot == null) return; if (nodes.Count > 0) robot.SetupExecution(nodes); robot.currentProgram = scriptName.Text.Length <= 0 ? $"Script{availableScripts.ItemCount}" : scriptName.Text; } public void SetRobot(Robot robot) { this.robot = robot; } public void LoadProgram(int index) { if (index <= 0) return; 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); RegisterEditorNode(nodeDisplay); } } scriptName.Text = availableScripts.GetItemText(index); availableScripts.Select(0); } public void LoadTemporaryProgram() { if (robot == null) return; if (robot.currentNode == null) return; ProgramNode firstNode = robot.currentNode; while (firstNode.previousNode != null) { firstNode = firstNode.previousNode; } HashSet loadedNodes = new HashSet(); ProgramNode nodeToLoad = firstNode; while (nodeToLoad != null && !loadedNodes.Contains(nodeToLoad)) { loadedNodes.Add(nodeToLoad); NodeDisplay nodeDisplay = NodeDisplay.Load(nodeToLoad.Save(), DSLNodes); if (nodeDisplay != null) { editorWindow.AddChild(nodeDisplay); RegisterEditorNode(nodeDisplay); } nodeToLoad = nodeToLoad.nextNode; } scriptName.Text = robot.currentProgram ?? ""; } public void DeleteProgram() { string filename = scriptName.Text; int selectedIndex = availableScripts.GetSelectedId(); if (selectedIndex > 0) { filename = availableScripts.GetItemText(selectedIndex); } if (filename.Length <= 0) return; if (!FileHandler.DeleteProgram(filename)) return; ClearWindow(); SetupScriptOptions(); } public void SaveProgram() { string result = ""; for (int i = 0; i < editorWindow.GetChildCount(); i++) { NodeDisplay nodeDisplay = editorWindow.GetChild(i) as NodeDisplay; if (nodeDisplay == null) continue; nodeDisplay.ReadParameters(); result += nodeDisplay.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(); } public void OnNodeConnect(StringName from, int fromPort, StringName to, int toPort) { GD.Print($"From {fromPort} to {toPort}"); if (to == from) return; foreach (Dictionary connection in editorWindow.GetConnectionList()) { if (connection["to_node"].AsStringName() == to && (int)connection["to_port"] == toPort) return; if (connection["from_node"].AsStringName() == from && (int)connection["from_port"] == fromPort) return; } editorWindow.ConnectNode(from, fromPort, to, toPort); } public void OnNodeDisconnect(StringName from, int fromPort, StringName to, int toPort) { editorWindow.DisconnectNode(from, fromPort, to, toPort); } }