Files
RuinAdventurer/Scripts/UI/DSL/ScriptGraphCompiler.cs
T

114 lines
2.8 KiB
C#

using Godot;
using Godot.Collections;
using System.Collections.Generic;
public class ScriptGraphCompiler
{
private readonly GraphEdit editorWindow;
private System.Collections.Generic.Dictionary<StringName, ProgramNode> runtimeNodes;
public ScriptGraphCompiler(GraphEdit editorWindow)
{
this.editorWindow = editorWindow;
}
public List<ProgramNode> BuildProgram(out string errorMessage)
{
errorMessage = "";
NodeDisplay startNode = FindStartNode();
if (startNode == null)
{
errorMessage = "(FAILED) Script needs exactly one Start node";
return new List<ProgramNode>();
}
BuildRuntimeNodeLookup();
return BuildScriptOrder(
startNode,
new List<ProgramNode>(),
new HashSet<StringName>()
);
}
private void BuildRuntimeNodeLookup()
{
runtimeNodes = new System.Collections.Generic.Dictionary<StringName, ProgramNode>();
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<ProgramNode> BuildScriptOrder(
NodeDisplay node,
List<ProgramNode> program,
HashSet<StringName> 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<Dictionary> nextConnections = GetOutgoingConnections(node);
if (nextConnections.Count <= 0) return program;
runtimeNode.SetNextNode(nextConnections, runtimeNodes);
foreach (Dictionary connection in nextConnections)
{
NodeDisplay nextNode = editorWindow.GetNodeOrNull<NodeDisplay>(
new NodePath(connection["to_node"].AsStringName())
);
program = BuildScriptOrder(
nextNode,
program,
visitedNodes
);
}
return program;
}
private List<Dictionary> GetOutgoingConnections(NodeDisplay node)
{
List<Dictionary> result = new List<Dictionary>();
Array<Dictionary> 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;
}
}