diff --git a/classes/deck/deck.gd b/classes/deck/deck.gd index 963b9ba..af3a043 100644 --- a/classes/deck/deck.gd +++ b/classes/deck/deck.gd @@ -114,24 +114,42 @@ func get_node(uuid: String) -> DeckNode: return nodes.get(uuid) -## Attempt to connect two nodes. Returns [code]true[/code] if the connection succeeded. -func connect_nodes(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int) -> bool: +## Returns [code]true[/code] if the connection between two nodes is legal. +func is_valid_connection(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int) -> bool: + # do not connect to self + if from_node_id == to_node_id: + return false + var from_node := get_node(from_node_id) var to_node := get_node(to_node_id) - # check that we can do the type conversion - var type_a: DeckType.Types = from_node.get_output_ports()[from_output_port].type - var type_b: DeckType.Types = to_node.get_input_ports()[to_input_port].type - if !DeckType.can_convert(type_a, type_b): - DeckHolder.logger.log_deck( - "Can not convert from %s to %s." % [DeckType.type_str(type_a), DeckType.type_str(type_b)], - Logger.LogType.ERROR - ) + + var usage_from: Port.UsageType = from_node.get_output_ports()[from_output_port].usage_type + var usage_to: Port.UsageType = to_node.get_input_ports()[to_input_port].usage_type + # incompatible usages + if (usage_from != Port.UsageType.BOTH) && (usage_to != Port.UsageType.BOTH): + if usage_from != usage_to: + return false + + var type_from: DeckType.Types = from_node.get_output_ports()[from_output_port].type + var type_to: DeckType.Types = to_node.get_input_ports()[to_input_port].type + # incompatible types + if !DeckType.can_convert(type_from, type_to): return false - - # refuse duplicate connections + + # duplicate connection if from_node.has_outgoing_connection_exact(from_output_port, to_node_id, to_input_port): - print("no duplicates") return false + + return true + + +## Attempt to connect two nodes. Returns [code]true[/code] if the connection succeeded. +func connect_nodes(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int) -> bool: + if !is_valid_connection(from_node_id, to_node_id, from_output_port, to_input_port): + return false + + var from_node := get_node(from_node_id) + var to_node := get_node(to_node_id) if to_node.has_incoming_connection(to_input_port): var connection: Dictionary = to_node.incoming_connections[to_input_port] @@ -139,7 +157,6 @@ func connect_nodes(from_node_id: String, to_node_id: String, from_output_port: i var node_out_port: int = connection.values()[0] disconnect_nodes(node_id, to_node_id, node_out_port, to_input_port) - if is_group && emit_group_signals: nodes_connected_in_group.emit(from_node_id, to_node_id, from_output_port, to_input_port, self) diff --git a/classes/deck/deck_node.gd b/classes/deck/deck_node.gd index 459b12a..0036733 100644 --- a/classes/deck/deck_node.gd +++ b/classes/deck/deck_node.gd @@ -74,23 +74,40 @@ signal renamed(new_name: String) ## Add an input port to this node. Usually only used at initialization. -func add_input_port(type: DeckType.Types, label: String, descriptor: String = "") -> void: - add_port(type, label, PortType.INPUT, get_input_ports().size(), descriptor) +func add_input_port( + type: DeckType.Types, + label: String, + descriptor: String = "", + usage: Port.UsageType = Port.UsageType.BOTH) -> void: + add_port(type, label, PortType.INPUT, get_input_ports().size(), descriptor, usage) ## Add an output port to this node. Usually only used at initialization. -func add_output_port(type: DeckType.Types, label: String, descriptor: String = "") -> void: - add_port(type, label, PortType.OUTPUT, get_output_ports().size(), descriptor) +func add_output_port( + type: DeckType.Types, + label: String, + descriptor: String = "", + usage: Port.UsageType = Port.UsageType.BOTH) -> void: + add_port(type, label, PortType.OUTPUT, get_output_ports().size(), descriptor, usage) ## Add a virtual port to this node. Usually only used at initialization. -func add_virtual_port(type: DeckType.Types, label: String, descriptor: String = "") -> void: - add_port(type, label, PortType.VIRTUAL, get_virtual_ports().size(), descriptor) +func add_virtual_port( + type: DeckType.Types, + label: String, + descriptor: String = "", + usage: Port.UsageType = Port.UsageType.BOTH) -> void: + add_port(type, label, PortType.VIRTUAL, get_virtual_ports().size(), descriptor, usage) ## Add a port to this node. Usually only used at initialization. -func add_port(type: DeckType.Types, label: String, port_type: PortType, index_of_type: int, descriptor: String = "") -> void: - var port := Port.new(type, label, ports.size(), port_type, index_of_type, descriptor) +func add_port(type: DeckType.Types, + label: String, + port_type: PortType, + index_of_type: int, + descriptor: String = "", + usage: Port.UsageType = Port.UsageType.BOTH) -> void: + var port := Port.new(type, label, ports.size(), port_type, index_of_type, descriptor, usage) ports.append(port) port_added.emit(ports.size() - 1) port.value_updated.connect( diff --git a/classes/deck/nodes/general/is_equal.gd b/classes/deck/nodes/general/is_equal.gd index d1169fd..674eb40 100644 --- a/classes/deck/nodes/general/is_equal.gd +++ b/classes/deck/nodes/general/is_equal.gd @@ -32,3 +32,14 @@ func _value_request(on_port: int) -> Variant: var b = await resolve_input_port_value_async(1) return a == b + + +func _receive(on_port: int, data: Variant, extra_data: Array = []) -> void: + if on_port == 0: + var b = await resolve_input_port_value_async(1) + if data == b: + send(0, true) + else: + var b = await resolve_input_port_value_async(0) + if data == b: + send(0, true) diff --git a/classes/deck/nodes/test/test_connection_types.gd b/classes/deck/nodes/test/test_connection_types.gd new file mode 100644 index 0000000..1f075b8 --- /dev/null +++ b/classes/deck/nodes/test/test_connection_types.gd @@ -0,0 +1,33 @@ +# (c) 2023-present Eroax +# (c) 2023-present Yagich +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +extends DeckNode + + +func _init() -> void: + name = "Test Usage" + node_type = name.to_snake_case() + + add_input_port( + DeckType.Types.ANY, "Both", "", Port.UsageType.BOTH + ) + + add_input_port( + DeckType.Types.ANY, "Trigger", "", Port.UsageType.TRIGGER + ) + + add_input_port( + DeckType.Types.ANY, "Value", "", Port.UsageType.VALUE_REQUEST + ) + + add_output_port( + DeckType.Types.ANY, "Both", "", Port.UsageType.BOTH + ) + + add_output_port( + DeckType.Types.ANY, "Trigger", "", Port.UsageType.TRIGGER + ) + + add_output_port( + DeckType.Types.ANY, "Value", "", Port.UsageType.VALUE_REQUEST + ) diff --git a/classes/deck/nodes/twitch/twitch_chat_received.gd b/classes/deck/nodes/twitch/twitch_chat_received.gd index cc38e6b..1a693ac 100644 --- a/classes/deck/nodes/twitch/twitch_chat_received.gd +++ b/classes/deck/nodes/twitch/twitch_chat_received.gd @@ -14,10 +14,10 @@ func _init(): node_type = "twitch_chat_received" description = "Receives Twitch chat events from a Twitch connection." - add_output_port(DeckType.Types.STRING, "Username") - add_output_port(DeckType.Types.STRING, "Message") - add_output_port(DeckType.Types.STRING, "Channel") - add_output_port(DeckType.Types.DICTIONARY, "Tags") + add_output_port(DeckType.Types.STRING, "Username", "", Port.UsageType.TRIGGER) + add_output_port(DeckType.Types.STRING, "Message", "", Port.UsageType.TRIGGER) + add_output_port(DeckType.Types.STRING, "Channel", "", Port.UsageType.TRIGGER) + add_output_port(DeckType.Types.DICTIONARY, "Tags", "", Port.UsageType.TRIGGER) add_output_port( DeckType.Types.BOOL, @@ -37,7 +37,10 @@ func _event_received(event_name : StringName, event_data : Dictionary = {}): message = event_data.message channel = event_data.channel tags = event_data - + send(0, username) + send(1, message) + send(2, channel) + send(3, tags) send(4, true) diff --git a/classes/deck/port.gd b/classes/deck/port.gd index 110fcbc..3b4ea53 100644 --- a/classes/deck/port.gd +++ b/classes/deck/port.gd @@ -7,6 +7,12 @@ class_name Port ## Ports are used for connections between [DeckNode]s and can contain data that is passed between ## them on a node. +enum UsageType { + TRIGGER, ## Port can send or receive events, not request values. + VALUE_REQUEST, ## Port can request values and respond to value requests, not send or receive events. + BOTH, ## Port can send or receive events [b]and[/b] request and respond to value requests. +} + ## The type index of this port. var type: DeckType.Types ## The label of this port. Used by the renderer to display. How it's displayed depends on the renderer @@ -22,6 +28,8 @@ var value_callback: Callable ## The type of this port (input, output or virtual) var port_type: DeckNode.PortType +## The usage type of this port (see [enum UsageType]). +var usage_type: UsageType ## The local index of this port. var index_of_type: int ## The global index of this port. @@ -41,6 +49,7 @@ func _init( p_index_of_type: int, p_descriptor: String = "", # p_value_callback: Callable = Callable(), + p_usage_type: UsageType = UsageType.BOTH, ) -> void: type = p_type label = p_label @@ -50,6 +59,7 @@ func _init( port_type = p_port_type index_of_type = p_index_of_type index = p_index + usage_type = p_usage_type func set_value(v: Variant) -> void: diff --git a/graph_node_renderer/deck_node_renderer_graph_node.gd b/graph_node_renderer/deck_node_renderer_graph_node.gd index 377c594..d5131f6 100644 --- a/graph_node_renderer/deck_node_renderer_graph_node.gd +++ b/graph_node_renderer/deck_node_renderer_graph_node.gd @@ -19,6 +19,11 @@ const TYPE_COLORS := { DeckType.Types.ANY: Color.WHITE, } +const PORT_USAGE_ICONS := { + Port.UsageType.TRIGGER: preload("res://graph_node_renderer/textures/port_trigger_12.svg"), + Port.UsageType.VALUE_REQUEST: preload("res://graph_node_renderer/textures/port_data_request_12.svg"), + Port.UsageType.BOTH: preload("res://graph_node_renderer/textures/port_any_12.svg"), +} ## Setups up all the properties based off [member node]. Including looping through ## [method DeckNode.get_all_ports()] and setting up all the descriptors. @@ -204,4 +209,6 @@ func update_port(port: Port) -> void: port.port_type == DeckNode.PortType.OUTPUT, port.type, TYPE_COLORS[port.type], + PORT_USAGE_ICONS[port.usage_type], + PORT_USAGE_ICONS[port.usage_type], ) diff --git a/graph_node_renderer/deck_renderer_graph_edit.gd b/graph_node_renderer/deck_renderer_graph_edit.gd index c781cc2..02095d3 100644 --- a/graph_node_renderer/deck_renderer_graph_edit.gd +++ b/graph_node_renderer/deck_renderer_graph_edit.gd @@ -87,6 +87,13 @@ func attempt_connection(from_node_name: StringName, from_port: int, to_node_name ) dirty = true + +func _is_node_hover_valid(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> bool: + var from_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(from_node)) + var to_node_renderer: DeckNodeRendererGraphNode = get_node(NodePath(to_node)) + + return deck.is_valid_connection(from_node_renderer.node._id, to_node_renderer.node._id, from_port, to_port) + ## Receives [signal GraphEdit.disconnection_request] and attempts to disconnect the two [DeckNode]s ## involved, utilizes [NodeDB] for accessing them. func attempt_disconnect(from_node_name: StringName, from_port: int, to_node_name: StringName, to_port: int) -> void: diff --git a/graph_node_renderer/textures/port_any_12.svg b/graph_node_renderer/textures/port_any_12.svg new file mode 100644 index 0000000..cce736c --- /dev/null +++ b/graph_node_renderer/textures/port_any_12.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/graph_node_renderer/textures/port_any_12.svg.import b/graph_node_renderer/textures/port_any_12.svg.import new file mode 100644 index 0000000..74bdff6 --- /dev/null +++ b/graph_node_renderer/textures/port_any_12.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c0qh34njvu4xt" +path="res://.godot/imported/port_any_12.svg-64aca00b3bf28084e8a38d718780e0ff.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://graph_node_renderer/textures/port_any_12.svg" +dest_files=["res://.godot/imported/port_any_12.svg-64aca00b3bf28084e8a38d718780e0ff.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/graph_node_renderer/textures/port_data_request_12.svg b/graph_node_renderer/textures/port_data_request_12.svg new file mode 100644 index 0000000..e73eaab --- /dev/null +++ b/graph_node_renderer/textures/port_data_request_12.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/graph_node_renderer/textures/port_data_request_12.svg.import b/graph_node_renderer/textures/port_data_request_12.svg.import new file mode 100644 index 0000000..ebefa7d --- /dev/null +++ b/graph_node_renderer/textures/port_data_request_12.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b5houge8j0lij" +path="res://.godot/imported/port_data_request_12.svg-da181e9908b91f72dfc70f3dd7196720.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://graph_node_renderer/textures/port_data_request_12.svg" +dest_files=["res://.godot/imported/port_data_request_12.svg-da181e9908b91f72dfc70f3dd7196720.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/graph_node_renderer/textures/port_trigger_12.svg b/graph_node_renderer/textures/port_trigger_12.svg new file mode 100644 index 0000000..02cc890 --- /dev/null +++ b/graph_node_renderer/textures/port_trigger_12.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/graph_node_renderer/textures/port_trigger_12.svg.import b/graph_node_renderer/textures/port_trigger_12.svg.import new file mode 100644 index 0000000..be616c0 --- /dev/null +++ b/graph_node_renderer/textures/port_trigger_12.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://tbxgx46ch210" +path="res://.godot/imported/port_trigger_12.svg-ddfb2ada8bfb56f098470a2797045ba0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://graph_node_renderer/textures/port_trigger_12.svg" +dest_files=["res://.godot/imported/port_trigger_12.svg-ddfb2ada8bfb56f098470a2797045ba0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false