using Godot; using Godot.Collections; using System.Collections.Generic; public class ScriptGraphCompiler { private readonly GraphEdit editorWindow; private System.Collections.Generic.Dictionary runtimeNodes; public ScriptGraphCompiler(GraphEdit editorWindow) { this.editorWindow = editorWindow; } public List BuildProgram(out string errorMessage) { errorMessage = ""; NodeDisplay startNode = FindStartNode(); if (startNode == null) { errorMessage = "(FAILED) Script needs exactly one Start node"; return new List(); } BuildRuntimeNodeLookup(); return BuildScriptOrder( startNode, new List(), new HashSet() ); } private void BuildRuntimeNodeLookup() { runtimeNodes = new System.Collections.Generic.Dictionary(); for (int i = 0; i < editorWindow.GetChildCount(); i++) { NodeDisplay nodeDisplay = editorWindow.GetChild(i) as NodeDisplay; if (nodeDisplay == null) continue; nodeDisplay.ReadParameters(); runtimeNodes.Add( nodeDisplay.Name, nodeDisplay.node.DuplicateForRuntime(nodeDisplay.Name.ToString()) ); } } private List BuildScriptOrder( NodeDisplay node, List program, HashSet visitedNodes ) { if (node == null) return program; if (visitedNodes.Contains(node.Name)) return program; if (!runtimeNodes.ContainsKey(node.Name)) return program; visitedNodes.Add(node.Name); ProgramNode runtimeNode = runtimeNodes[node.Name]; program.Add(runtimeNode); List nextConnections = GetOutgoingConnections(node); if (nextConnections.Count <= 0) return program; runtimeNode.SetNextNode(nextConnections, runtimeNodes); foreach (Dictionary connection in nextConnections) { NodeDisplay nextNode = editorWindow.GetNodeOrNull( new NodePath(connection["to_node"].AsStringName()) ); program = BuildScriptOrder( nextNode, program, visitedNodes ); } return program; } private List GetOutgoingConnections(NodeDisplay node) { List result = new List(); Array connections = editorWindow.GetConnectionListFromNode(node.Name); for (int i = 0; i < connections.Count; i++) { if (connections[i]["from_node"].AsStringName() == node.Name) { result.Add(connections[i]); } } return result; } private NodeDisplay FindStartNode() { NodeDisplay startNode = null; for (int i = 0; i < editorWindow.GetChildCount(); i++) { NodeDisplay nodeDisplay = editorWindow.GetChild(i) as NodeDisplay; if (nodeDisplay == null) continue; if (nodeDisplay.node is not StartNode) continue; if (startNode != null) return null; startNode = nodeDisplay; } return startNode; } }