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

349 lines
7.4 KiB
C#

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;
[Export] NodeTooltip nodeTooltip;
public System.Collections.Generic.Dictionary<ProgramNode, PackedScene> DSLNodes;
public NodeDisplay selectedNode;
public NodeDisplay highlightedNode;
public override void _Ready()
{
DSLNodes = ResourceLoader.LoadDSLNodes();
GenerateCodingBlocks();
}
public override void _Notification(int id)
{
if (id == NotificationVisibilityChanged)
{
if (Visible) LoadWindow();
}
}
public override void _Process(double delta)
{
if (Input.IsActionJustPressed("delete_node"))
{
Control focused = GetViewport().GuiGetFocusOwner();
if (focused is LineEdit || focused is TextEdit) return;
if (selectedNode == null) return;
if (selectedNode == highlightedNode) highlightedNode = null;
editorWindow.RemoveChild(selectedNode);
selectedNode.QueueFree();
}
if (robot != null && Visible && robot.isExecuting)
{
UpdateCurrentNode();
}
}
private void UpdateCurrentNode()
{
if (robot.currentNode == null)
{
ClearHighlightedNode();
return;
}
if (highlightedNode != null)
{
if (IsCurrentRuntimeNode(highlightedNode))
{
return;
}
ClearHighlightedNode();
}
foreach (Node child in editorWindow.GetChildren())
{
NodeDisplay nodeDisplay = child as NodeDisplay;
if (nodeDisplay == null) continue;
if (IsCurrentRuntimeNode(nodeDisplay))
{
nodeDisplay.SetHighlighted(true);
highlightedNode = nodeDisplay;
break;
}
}
}
private bool IsCurrentRuntimeNode(NodeDisplay nodeDisplay)
{
return robot.currentNode.EditorNodeId != null
&& robot.currentNode.EditorNodeId.Length > 0
&& nodeDisplay.Name.ToString() == robot.currentNode.EditorNodeId;
}
private void ClearHighlightedNode()
{
if (highlightedNode == null) return;
highlightedNode.SetHighlighted(false);
highlightedNode = null;
}
public void OnMapToggled(bool toggledOn)
{
if (robot == null) return;
robot.showOnMap = toggledOn;
}
public void OnNodeSelect(NodeDisplay node)
{
selectedNode = node;
}
public void OnNodeDeselect(NodeDisplay node)
{
selectedNode = null;
}
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<string> 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()
{
foreach (ProgramNode nodeTemplate in DSLNodes.Keys)
{
Button nodeListButton = new Button
{
Name = nodeTemplate.DisplayText,
Text = nodeTemplate.DisplayText
};
nodeListButton.Pressed += () =>
{
AddEditorNode(nodeTemplate);
};
nodeListButton.MouseEntered += () =>
{
nodeTooltip.ShowTooltip(nodeTemplate.DisplayText, nodeTemplate.TooltipText, nodeListButton);
};
nodeListButton.MouseExited += () =>
{
nodeTooltip.HideTooltip();
};
codeBlocks.AddChild(nodeListButton);
}
}
private void AddEditorNode(ProgramNode node)
{
NodeDisplay editorDisplay = DSLNodes[node].Instantiate<NodeDisplay>();
editorDisplay.PositionOffset = GetVisibleGraphCenter() - editorDisplay.Size / 2f;
editorWindow.AddChild(editorDisplay);
}
private Vector2 GetVisibleGraphCenter()
{
return (editorWindow.ScrollOffset + editorWindow.Size / 2f) / editorWindow.Zoom;
}
public void ClearWindow()
{
ClearHighlightedNode();
DisconnectAllNodes();
RemoveEditorNodes();
scriptName.Text = "";
}
private void DisconnectAllNodes()
{
foreach (Dictionary connection in editorWindow.GetConnectionList())
{
editorWindow.DisconnectNode(
connection["from_node"].AsStringName(),
(int)connection["from_port"],
connection["to_node"].AsStringName(),
(int)connection["to_port"]
);
}
}
private void RemoveEditorNodes()
{
foreach (Node child in editorWindow.GetChildren())
{
if (child is not GraphNode) continue;
editorWindow.RemoveChild(child);
child.QueueFree();
}
}
public void CompileProgram()
{
if (robot == null) return;
ScriptGraphCompiler compiler = new ScriptGraphCompiler(editorWindow);
string errorMessage;
List<ProgramNode> nodes = compiler.BuildProgram(out errorMessage);
if (errorMessage.Length > 0)
{
robot.StopExecution(errorMessage);
return;
}
if (nodes.Count > 0) robot.SetupExecution(nodes);
robot.currentProgram = GetCurrentScriptName();
}
private string GetCurrentScriptName()
{
if (scriptName.Text.Length > 0) return scriptName.Text;
return $"Script{availableScripts.ItemCount}";
}
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));
CreateSerializer().Load(scriptContent);
scriptName.Text = availableScripts.GetItemText(index);
availableScripts.Select(0);
}
private void AddLoadedNode(NodeDisplay nodeDisplay)
{
editorWindow.AddChild(nodeDisplay);
}
public void LoadTemporaryProgram()
{
if (robot == null) return;
ProgramNode rootNode = robot.programStartNode ?? robot.currentNode;
if (rootNode == null) return;
RunningProgramGraphBuilder builder = new RunningProgramGraphBuilder(
DSLNodes,
AddLoadedNode,
ConnectNodes
);
builder.Load(rootNode);
scriptName.Text = robot.currentProgram ?? "";
UpdateCurrentNode();
}
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 = CreateSerializer().Save();
if (result.Length <= 0) return;
FileHandler.SaveProgram(GetCurrentScriptName(), result);
SetupScriptOptions();
}
public void OnNodeConnect(StringName from, int fromPort, StringName to, int toPort)
{
if (to == from) return;
ConnectNodes(from, fromPort, to, toPort);
}
private void ConnectNodes(StringName from, int fromPort, StringName to, int toPort)
{
if (HasOutputConnection(from, fromPort)) return;
editorWindow.ConnectNode(from, fromPort, to, toPort);
}
private bool HasOutputConnection(StringName from, int fromPort)
{
foreach (Dictionary connection in editorWindow.GetConnectionList())
{
if (connection["from_node"].AsStringName() != from) continue;
if ((int)connection["from_port"] != fromPort) continue;
return true;
}
return false;
}
private ScriptGraphSerializer CreateSerializer()
{
return new ScriptGraphSerializer(
editorWindow,
DSLNodes,
AddLoadedNode,
ConnectNodes
);
}
public void OnNodeDisconnect(StringName from, int fromPort, StringName to, int toPort)
{
editorWindow.DisconnectNode(from, fromPort, to, toPort);
}
}