Skip to content

Commit

Permalink
implement branching nodes, add menu bar; basic save and load
Browse files Browse the repository at this point in the history
  • Loading branch information
Swarkin committed Dec 23, 2024
1 parent 7531bc9 commit 1ce1bc9
Show file tree
Hide file tree
Showing 14 changed files with 267 additions and 42 deletions.
45 changes: 27 additions & 18 deletions graph.gd
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
class_name ScriptGraphEdit
extends GraphEdit

const TYPE_FLOW := 69
Expand All @@ -21,7 +22,14 @@ class GraphNodeRepr:
node = _node

func flow_next() -> GraphNodeRepr:
var conn := out_connections[0] as GraphNodeConnection if !out_connections.is_empty() else null
if out_connections.is_empty(): return null
var conn: GraphNodeConnection

if node is BranchingGraphNode:
conn = node.next_flow(out_connections)
else:
conn = out_connections[0] if !out_connections.is_empty() else null

return conn.to_node if conn else null

func get_input(port_idx: int, default = null) -> Variant:
Expand Down Expand Up @@ -70,23 +78,8 @@ func _process(_dt: float) -> void:

func execute() -> void:
if verbose: print("Executing...")
var nodes: Dictionary[StringName, GraphNodeRepr]

#region parsing
for node in get_children() as Array[Control]:
if node is not GraphNode: continue
nodes[node.name] = GraphNodeRepr.new(node)

for conn in connections:
var from := nodes[conn["from_node"] as StringName]
var to := nodes[conn["to_node"] as StringName]
var from_conn := GraphNodeConnection.new(conn, nodes)
var to_conn := GraphNodeConnection.new(conn, nodes)
from.out_connections.append(from_conn)
to.in_connections.append(to_conn)
#endregion
var nodes := parse()

#region logic
var ready_node := nodes["ReadyNode"]
if verbose:
print(ready_node.node.name)
Expand Down Expand Up @@ -127,10 +120,26 @@ func execute() -> void:

node.execute(args)
next = next.flow_next()
#endregion

if verbose: print("\nFinished")

func parse() -> Dictionary[StringName, GraphNodeRepr]:
var nodes: Dictionary[StringName, GraphNodeRepr]

for node in get_children() as Array[Control]:
if node is not GraphNode: continue
nodes[node.name] = GraphNodeRepr.new(node)

for conn in connections:
var from := nodes[conn["from_node"] as StringName]
var to := nodes[conn["to_node"] as StringName]
var from_conn := GraphNodeConnection.new(conn, nodes)
var to_conn := GraphNodeConnection.new(conn, nodes)
from.out_connections.append(from_conn)
to.in_connections.append(to_conn)

return nodes


func _on_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
var from := get_node(str(from_node)) as GraphNode
Expand Down
58 changes: 38 additions & 20 deletions main.tscn
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[gd_scene load_steps=7 format=3 uid="uid://qr52aafdvj5l"]
[gd_scene load_steps=8 format=3 uid="uid://qr52aafdvj5l"]

[ext_resource type="Script" uid="uid://ba0kyhwc8edji" path="res://menu_bar.gd" id="1_1bvp3"]
[ext_resource type="Script" uid="uid://c4xjo5mavlg5r" path="res://node_selection.gd" id="1_7mycd"]
[ext_resource type="Script" uid="uid://44j7r8skexf1" path="res://graph.gd" id="1_h2yge"]
[ext_resource type="Script" uid="uid://bgdixhn7vioeb" path="res://run.gd" id="4_1bvp3"]
Expand All @@ -13,17 +14,34 @@ keycode = 4194336
[sub_resource type="Shortcut" id="Shortcut_1bvp3"]
events = [SubResource("InputEventKey_h2yge")]

[node name="Main" type="Control"]
layout_mode = 3
[node name="Main" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0

[node name="GraphEdit" type="GraphEdit" parent="."]
offset_right = 1280.0
offset_bottom = 720.0
[node name="Menu" type="HBoxContainer" parent="." node_paths=PackedStringArray("graph", "file_menu")]
layout_mode = 2
script = ExtResource("1_1bvp3")
graph = NodePath("../ScriptGraphEdit")
file_menu = NodePath("FileMenu")

[node name="FileMenu" type="MenuButton" parent="Menu"]
layout_mode = 2
text = "File"
switch_on_hover = true
item_count = 3
popup/item_0/text = "New"
popup/item_0/id = 0
popup/item_1/text = "Open"
popup/item_1/id = 1
popup/item_2/text = "Save"
popup/item_2/id = 2

[node name="ScriptGraphEdit" type="GraphEdit" parent="."]
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxEmpty_7dm0k")
grid_pattern = 1
Expand All @@ -37,48 +55,48 @@ script = ExtResource("1_h2yge")
verbose = true
hide_scroll = true

[node name="Margin" type="MarginContainer" parent="."]
[node name="Controls" type="MarginContainer" parent="ScriptGraphEdit"]
layout_mode = 1
anchors_preset = 15
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -82.0
grow_horizontal = 2
grow_vertical = 2
grow_vertical = 0
mouse_filter = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 16
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 16

[node name="Bar" type="HBoxContainer" parent="Margin"]
[node name="Bar" type="HBoxContainer" parent="ScriptGraphEdit/Controls"]
layout_mode = 2
size_flags_vertical = 8

[node name="NodeSelection" type="ScrollContainer" parent="Margin/Bar" node_paths=PackedStringArray("graph", "buttons")]
[node name="NodeSelection" type="ScrollContainer" parent="ScriptGraphEdit/Controls/Bar" node_paths=PackedStringArray("graph", "buttons")]
layout_mode = 2
size_flags_horizontal = 3
follow_focus = true
horizontal_scroll_mode = 2
vertical_scroll_mode = 0
script = ExtResource("1_7mycd")
graph = NodePath("../../../GraphEdit")
graph = NodePath("../../..")
buttons = NodePath("ButtonContainer")

[node name="ButtonContainer" type="HBoxContainer" parent="Margin/Bar/NodeSelection"]
[node name="ButtonContainer" type="HBoxContainer" parent="ScriptGraphEdit/Controls/Bar/NodeSelection"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3

[node name="Run" type="Button" parent="Margin/Bar" node_paths=PackedStringArray("graph")]
[node name="Run" type="Button" parent="ScriptGraphEdit/Controls/Bar" node_paths=PackedStringArray("graph")]
custom_minimum_size = Vector2(100, 50)
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 8
shortcut = SubResource("Shortcut_1bvp3")
text = "Run"
script = ExtResource("4_1bvp3")
graph = NodePath("../../../GraphEdit")
graph = NodePath("../../..")

[connection signal="connection_request" from="GraphEdit" to="GraphEdit" method="_on_connection_request"]
[connection signal="delete_nodes_request" from="GraphEdit" to="GraphEdit" method="_on_delete_nodes_request"]
[connection signal="disconnection_request" from="GraphEdit" to="GraphEdit" method="_on_disconnection_request"]
[connection signal="connection_request" from="ScriptGraphEdit" to="ScriptGraphEdit" method="_on_connection_request"]
[connection signal="delete_nodes_request" from="ScriptGraphEdit" to="ScriptGraphEdit" method="_on_delete_nodes_request"]
[connection signal="disconnection_request" from="ScriptGraphEdit" to="ScriptGraphEdit" method="_on_disconnection_request"]
83 changes: 83 additions & 0 deletions menu_bar.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
extends Container

@export var graph: ScriptGraphEdit
@export var file_menu: MenuButton

func _enter_tree() -> void:
var file_popup := file_menu.get_popup()
file_popup.id_pressed.connect(_on_file_menu_id_pressed)

func _on_file_menu_id_pressed(id: int) -> void:
match id:
0:
print("New")
for c in graph.get_children():
if c is GraphNode:
c.queue_free()

graph.connections.clear()
1:
print("Open")
var file := FileAccess.open("user://test.json", FileAccess.READ)
var data := JSON.parse_string(file.get_as_text()) as Dictionary
print(data)

var in_conn_data: Array[Dictionary]
var out_conn_data: Array[Dictionary]

for name_key in data:
var node_data := data[name_key] as Dictionary
var graph_node := (load("res://nodes/"+node_data["t"]) as PackedScene).instantiate() as GraphNode
in_conn_data.append_array(node_data["i"])
out_conn_data.append_array(node_data["o"])
graph_node.position_offset = Vector2(node_data["px"], node_data["py"])
graph_node.size = Vector2(node_data["sx"], node_data["sy"])
if node_data.has("v"):
graph_node.set_value(node_data["v"])
graph.add_child(graph_node)

for in_conn in in_conn_data:
graph.connect_node(in_conn["fn"], in_conn["fp"], in_conn["tn"], in_conn["tp"])

for out_conn in out_conn_data:
graph.connect_node(out_conn["fn"], out_conn["fp"], out_conn["tn"], out_conn["tp"])
2:
print("Save")
var nodes := graph.parse()
var data := {}

for name_key in nodes:
var node := nodes[name_key]
var node_data := {
"t": node.node.scene_file_path.get_file(),
"px": node.node.position_offset.x,
"py": node.node.position_offset.y,
"sx": node.node.size.x,
"sy": node.node.size.y,
"i": [],
"o": [],
}

if node.node is ValueGraphNode:
node_data["v"] = node.node.get_value()

for in_conn in node.in_connections:
node_data["i"].append({
"fp": in_conn.from_port,
"tp": in_conn.to_port,
"fn": in_conn.from_node.node.name,
"tn": in_conn.to_node.node.name,
})

for out_conn in node.out_connections:
node_data["o"].append({
"fp": out_conn.from_port,
"tp": out_conn.to_port,
"fn": out_conn.from_node.node.name,
"tn": out_conn.to_node.node.name,
})

data[name_key] = node_data

var file := FileAccess.open("user://test.json", FileAccess.WRITE)
file.store_string(JSON.stringify(data))
1 change: 1 addition & 0 deletions menu_bar.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://ba0kyhwc8edji
2 changes: 1 addition & 1 deletion node_selection.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extends Container
const PATH := "res://nodes/"
const NODE_BUTTON := preload("res://ui/node_button.tscn") as PackedScene

@export var graph: GraphEdit
@export var graph: ScriptGraphEdit
@export var buttons: Container

var nodes: Dictionary[String, PackedScene]
Expand Down
7 changes: 7 additions & 0 deletions nodes/branching_node.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class_name BranchingGraphNode
extends ExecutableGraphNode

@warning_ignore("unused_parameter")
func next_flow(out_connections: Array[ScriptGraphEdit.GraphNodeConnection]) -> ScriptGraphEdit.GraphNodeConnection:
push_error("Cannot call function on abstract class")
return
1 change: 1 addition & 0 deletions nodes/branching_node.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://xwkltjh75otp
25 changes: 25 additions & 0 deletions nodes/equals_node.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class_name GraphNodeEquals
extends BranchingGraphNode

var _decision = null

func execute(args: Array) -> void:
print("exec called")
print(get_stack())
_decision = args[0] == args[1]

func next_flow(out_connections: Array[ScriptGraphEdit.GraphNodeConnection]) -> ScriptGraphEdit.GraphNodeConnection:
if out_connections.is_empty():
return
elif _decision == null:
push_error("Reading decision before execute")
return
else:
var target_from_port := 0 if _decision else 1
for conn in out_connections:
print(conn.from_port)
if conn.from_port == target_from_port:
return conn

push_error("Missing connection")
return
1 change: 1 addition & 0 deletions nodes/equals_node.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://eo4o7tqi5aw0
72 changes: 72 additions & 0 deletions nodes/equals_node.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
[gd_scene load_steps=4 format=3 uid="uid://cqe2legqrhp0k"]

[ext_resource type="Texture2D" uid="uid://bhop1cdyv78s4" path="res://ui/caret-right.svg" id="1_j257i"]
[ext_resource type="Script" uid="uid://dcx1bl2m38n6b" path="res://nodes/executable_node.gd" id="2_fi83x"]
[ext_resource type="Script" uid="uid://eo4o7tqi5aw0" path="res://nodes/equals_node.gd" id="2_nks3r"]

[node name="EqualsNode" type="GraphNode"]
resizable = true
title = "Equals"
ignore_invalid_connection_type = true
slot/0/left_enabled = true
slot/0/left_type = 69
slot/0/left_color = Color(1, 1, 1, 1)
slot/0/left_icon = ExtResource("1_j257i")
slot/0/right_enabled = false
slot/0/right_type = 69
slot/0/right_color = Color(1, 1, 1, 1)
slot/0/right_icon = ExtResource("1_j257i")
slot/0/draw_stylebox = true
slot/1/left_enabled = true
slot/1/left_type = 42
slot/1/left_color = Color(1, 1, 1, 1)
slot/1/left_icon = null
slot/1/right_enabled = true
slot/1/right_type = 69
slot/1/right_color = Color(1, 1, 1, 1)
slot/1/right_icon = ExtResource("1_j257i")
slot/1/draw_stylebox = true
slot/2/left_enabled = true
slot/2/left_type = 42
slot/2/left_color = Color(1, 1, 1, 1)
slot/2/left_icon = null
slot/2/right_enabled = true
slot/2/right_type = 69
slot/2/right_color = Color(1, 1, 1, 1)
slot/2/right_icon = ExtResource("1_j257i")
slot/2/draw_stylebox = true
script = ExtResource("2_nks3r")
metadata/_custom_type_script = ExtResource("2_fi83x")

[node name="Flow" type="Label" parent="."]
layout_mode = 2

[node name="A" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 16

[node name="Left" type="Label" parent="A"]
layout_mode = 2
size_flags_horizontal = 2
text = "A"

[node name="Right" type="Label" parent="A"]
layout_mode = 2
size_flags_horizontal = 10
text = "True"
horizontal_alignment = 2

[node name="B" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 16

[node name="Left" type="Label" parent="B"]
layout_mode = 2
size_flags_horizontal = 2
text = "B"

[node name="Right" type="Label" parent="B"]
layout_mode = 2
size_flags_horizontal = 10
text = "False"
horizontal_alignment = 2
2 changes: 1 addition & 1 deletion nodes/print_node.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ class_name GraphNodePrint
extends ExecutableGraphNode

func execute(args: Array) -> void:
prints(self, args[0])
print(args[0])
Loading

0 comments on commit 1ce1bc9

Please sign in to comment.