From 6c41c6f70c1d233e90be6bdb92b606c54036375f Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 7 Oct 2024 21:20:53 +0200 Subject: [PATCH 01/52] Baseline code for CallNode --- addons/dialogue_nodes/editor/calls.gd | 11 ++ addons/dialogue_nodes/nodes/CallNode.tscn | 109 ++++++++++++++++++ addons/dialogue_nodes/nodes/callNode.gd | 54 +++++++++ .../nodes/sub_nodes/CallNodeParameter.tscn | 16 +++ .../nodes/sub_nodes/CallNodeReturn.tscn | 11 ++ 5 files changed, 201 insertions(+) create mode 100644 addons/dialogue_nodes/editor/calls.gd create mode 100644 addons/dialogue_nodes/nodes/CallNode.tscn create mode 100644 addons/dialogue_nodes/nodes/callNode.gd create mode 100644 addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn create mode 100644 addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd new file mode 100644 index 0000000..52cc2be --- /dev/null +++ b/addons/dialogue_nodes/editor/calls.gd @@ -0,0 +1,11 @@ +@tool +extends Script +## +## Default Function Database for CallNodes +## +## Method database for all function calls made from DialogueCalls. +## This script is expected to be expanded by the user, customized to their needs. +## Alternativelly, the user may create new scripts to call methods from. + +func sample_method(nickname: String, age: int, height: float = 5.1) -> Array: + return [nickname, age, height] diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn new file mode 100644 index 0000000..d050017 --- /dev/null +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -0,0 +1,109 @@ +[gd_scene load_steps=5 format=3 uid="uid://cu71fiowdkvmr"] + +[ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/callNode.gd" id="1_hul2k"] +[ext_resource type="Texture2D" uid="uid://buayifvickq2l" path="res://addons/dialogue_nodes/icons/Folder.svg" id="2_w7ejb"] +[ext_resource type="PackedScene" uid="uid://bskh1vm7i45nw" path="res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn" id="3_tyq50"] +[ext_resource type="PackedScene" uid="uid://djc2ggs14vbxc" path="res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn" id="4_mw7lj"] + +[node name="CallNode" type="GraphNode"] +custom_minimum_size = Vector2(300, 0) +offset_right = 300.0 +offset_bottom = 110.0 +title = "CallNode" +slot/0/left_enabled = true +slot/0/left_type = 0 +slot/0/left_color = Color(1, 1, 1, 1) +slot/0/left_icon = null +slot/0/right_enabled = false +slot/0/right_type = 0 +slot/0/right_color = Color(1, 1, 1, 1) +slot/0/right_icon = null +slot/0/draw_stylebox = true +slot/1/left_enabled = false +slot/1/left_type = 0 +slot/1/left_color = Color(1, 1, 1, 1) +slot/1/left_icon = null +slot/1/right_enabled = true +slot/1/right_type = 0 +slot/1/right_color = Color(0.647059, 0.937255, 0.67451, 1) +slot/1/right_icon = null +slot/1/draw_stylebox = true +slot/2/left_enabled = false +slot/2/left_type = 0 +slot/2/left_color = Color(1, 1, 1, 1) +slot/2/left_icon = null +slot/2/right_enabled = false +slot/2/right_type = 0 +slot/2/right_color = Color(1, 1, 1, 1) +slot/2/right_icon = null +slot/2/draw_stylebox = true +slot/3/left_enabled = false +slot/3/left_type = 0 +slot/3/left_color = Color(1, 1, 1, 1) +slot/3/left_icon = null +slot/3/right_enabled = false +slot/3/right_type = 0 +slot/3/right_color = Color(1, 1, 1, 1) +slot/3/right_icon = null +slot/3/draw_stylebox = true +script = ExtResource("1_hul2k") + +[node name="MethodSetup" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="SourceContainer" type="HBoxContainer" parent="MethodSetup"] +visible = false +layout_mode = 2 + +[node name="FilePath" type="LineEdit" parent="MethodSetup/SourceContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 + +[node name="BrowseButton" type="Button" parent="MethodSetup/SourceContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +icon = ExtResource("2_w7ejb") + +[node name="MethodSelection" type="OptionButton" parent="MethodSetup"] +unique_name_in_owner = true +layout_mode = 2 +selected = 1 +item_count = 2 +popup/item_0/text = "method_a" +popup/item_1/text = "method_b" +popup/item_1/id = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="MethodSetup"] +layout_mode = 2 +size_flags_vertical = 4 + +[node name="VSeparator" type="VSeparator" parent="MethodSetup/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="ParameterContainer" type="VBoxContainer" parent="MethodSetup/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="CallNodeParameter" parent="MethodSetup/HBoxContainer/ParameterContainer" instance=ExtResource("3_tyq50")] +layout_mode = 2 + +[node name="CallNodeParameter2" parent="MethodSetup/HBoxContainer/ParameterContainer" instance=ExtResource("3_tyq50")] +layout_mode = 2 + +[node name="CallNodeParameter3" parent="MethodSetup/HBoxContainer/ParameterContainer" instance=ExtResource("3_tyq50")] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="ReturnsLabel" type="Label" parent="."] +layout_mode = 2 +text = "Returns" + +[node name="CallNodeReturn" parent="." instance=ExtResource("4_mw7lj")] +layout_mode = 2 + +[node name="FileDialog" type="FileDialog" parent="."] +size = Vector2i(303, 180) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd new file mode 100644 index 0000000..da340f9 --- /dev/null +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -0,0 +1,54 @@ +@tool +extends GraphNode +## +## Call Node +## +## This Node calls a function from an expandable library of callables to determine which Node +## output to take based on what the method returned. +## The library, a static object called [DialogueCalls], is expected to be expanded by the user. + +const DEFAULT_CALLS: Script = preload("res://addons/dialogue_nodes/editor/calls.gd") + +signal modified + +var undo_redo: EditorUndoRedoManager + +var _calls_script: Script = null +var _calls: Dictionary = {} + +@onready var _method_button: OptionButton = %MethodSelection + + +func _ready() -> void: + _reload_library(DEFAULT_CALLS) + + +func _reload_library(script: Script) -> bool: + if script == null: + push_error("Cannot reload CallNode's library with a NULL script!") + return false + + # Swap signal connections + if _calls_script != null and _calls_script.script_changed.is_connected(_on_calls_script_changed): + _calls_script.script_changed.disconnect(_on_calls_script_changed) + script.script_changed.connect(_on_calls_script_changed, ConnectFlags.CONNECT_DEFERRED) + + # Reconnect Script Signal + _calls.clear() + for method: Dictionary in script.get_script_method_list(): + _calls[method.name] = method + + return true + + +func _to_dict(graph: GraphEdit) -> Dictionary: + return {} + + +func _from_dict(dict: Dictionary) -> Array[String]: + return [] as Array[String] + + +func _on_calls_script_changed() -> void: + pass + #_reload_library() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn new file mode 100644 index 0000000..0fd4991 --- /dev/null +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn @@ -0,0 +1,16 @@ +[gd_scene format=3 uid="uid://bskh1vm7i45nw"] + +[node name="CallNodeParameter" type="HBoxContainer"] +size_flags_horizontal = 3 +size_flags_vertical = 4 + +[node name="ParameterLabel" type="Label" parent="."] +layout_mode = 2 +size_flags_horizontal = 0 +text = "Parameter" + +[node name="ParameterInput" type="LineEdit" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +placeholder_text = "Type" diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn new file mode 100644 index 0000000..6bc2857 --- /dev/null +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -0,0 +1,11 @@ +[gd_scene format=3 uid="uid://djc2ggs14vbxc"] + +[node name="CallNodeReturn" type="HBoxContainer"] +size_flags_horizontal = 3 +size_flags_vertical = 4 + +[node name="ParameterInput" type="LineEdit" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +placeholder_text = "Type" From b5bc5b335ac4afdb38b00ef20b00563c370c21ea Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 7 Oct 2024 21:42:08 +0200 Subject: [PATCH 02/52] Evolved CallNode to update its UI (not finished) --- addons/dialogue_nodes/editor/calls.gd | 2 +- addons/dialogue_nodes/editor/graph.gd | 3 ++- addons/dialogue_nodes/nodes/CallNode.tscn | 9 ++++----- addons/dialogue_nodes/nodes/callNode.gd | 20 +++++++++++++++++--- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index 52cc2be..eb0015c 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -1,7 +1,7 @@ @tool extends Script ## -## Default Function Database for CallNodes +## Default Method Database for CallNodes ## ## Method database for all function calls made from DialogueCalls. ## This script is expected to be expanded by the user, customized to their needs. diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 87498b5..8b7c44c 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -14,7 +14,8 @@ signal run_requested(start_node_idx : int) preload('res://addons/dialogue_nodes/nodes/SetNode.tscn'), preload('res://addons/dialogue_nodes/nodes/ConditionNode.tscn'), preload('res://addons/dialogue_nodes/nodes/NestNode.tscn'), - preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn') + preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn'), + preload('res://addons/dialogue_nodes/nodes/CallNode.tscn') ] @onready var popup_menu = $PopupMenu diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index d050017..d3d5713 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -68,11 +68,10 @@ icon = ExtResource("2_w7ejb") [node name="MethodSelection" type="OptionButton" parent="MethodSetup"] unique_name_in_owner = true layout_mode = 2 -selected = 1 -item_count = 2 -popup/item_0/text = "method_a" -popup/item_1/text = "method_b" -popup/item_1/id = 1 +selected = 0 +allow_reselect = true +item_count = 1 +popup/item_0/text = "sample_method" [node name="HBoxContainer" type="HBoxContainer" parent="MethodSetup"] layout_mode = 2 diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index da340f9..78a4d4f 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -27,17 +27,31 @@ func _reload_library(script: Script) -> bool: if script == null: push_error("Cannot reload CallNode's library with a NULL script!") return false - + # Swap signal connections if _calls_script != null and _calls_script.script_changed.is_connected(_on_calls_script_changed): _calls_script.script_changed.disconnect(_on_calls_script_changed) script.script_changed.connect(_on_calls_script_changed, ConnectFlags.CONNECT_DEFERRED) - # Reconnect Script Signal + # Re-write Calls Library with new methods _calls.clear() for method: Dictionary in script.get_script_method_list(): _calls[method.name] = method - + + # Update Method Button UI + var curr_method_name: String = "" + if _method_button.selected > -1: + curr_method_name = _method_button.get_item_text(_method_button.selected) + + _method_button.clear() + var idx: int = 0 + for method: Dictionary in _calls.values(): + _method_button.add_item(method.name, idx) + if method.name == curr_method_name: + _method_button.selected = idx + idx += 1 + + _calls_script = script return true From cab1640531eba3120ac9683a656c8635d1769eff Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Tue, 8 Oct 2024 18:16:29 +0200 Subject: [PATCH 03/52] Updated CallNode parameters UI section --- addons/dialogue_nodes/nodes/CallNode.tscn | 24 ++--- addons/dialogue_nodes/nodes/callNode.gd | 73 +++++++++++++-- .../nodes/sub_nodes/CallNodeArgument.tscn | 33 +++++++ .../nodes/sub_nodes/CallNodeParameter.tscn | 16 ---- .../nodes/sub_nodes/call_node_argument.gd | 88 +++++++++++++++++++ 5 files changed, 198 insertions(+), 36 deletions(-) create mode 100644 addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn delete mode 100644 addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn create mode 100644 addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index d3d5713..2965dec 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=5 format=3 uid="uid://cu71fiowdkvmr"] +[gd_scene load_steps=4 format=3 uid="uid://cu71fiowdkvmr"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/callNode.gd" id="1_hul2k"] [ext_resource type="Texture2D" uid="uid://buayifvickq2l" path="res://addons/dialogue_nodes/icons/Folder.svg" id="2_w7ejb"] -[ext_resource type="PackedScene" uid="uid://bskh1vm7i45nw" path="res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn" id="3_tyq50"] [ext_resource type="PackedScene" uid="uid://djc2ggs14vbxc" path="res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn" id="4_mw7lj"] [node name="CallNode" type="GraphNode"] @@ -65,13 +64,14 @@ layout_mode = 2 size_flags_horizontal = 8 icon = ExtResource("2_w7ejb") -[node name="MethodSelection" type="OptionButton" parent="MethodSetup"] +[node name="MethodSelector" type="OptionButton" parent="MethodSetup"] unique_name_in_owner = true layout_mode = 2 selected = 0 allow_reselect = true -item_count = 1 -popup/item_0/text = "sample_method" +item_count = 2 +popup/item_1/text = "sample_method" +popup/item_1/id = 1 [node name="HBoxContainer" type="HBoxContainer" parent="MethodSetup"] layout_mode = 2 @@ -81,19 +81,11 @@ size_flags_vertical = 4 layout_mode = 2 size_flags_horizontal = 0 -[node name="ParameterContainer" type="VBoxContainer" parent="MethodSetup/HBoxContainer"] +[node name="ArgumentsContainer" type="VBoxContainer" parent="MethodSetup/HBoxContainer"] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -[node name="CallNodeParameter" parent="MethodSetup/HBoxContainer/ParameterContainer" instance=ExtResource("3_tyq50")] -layout_mode = 2 - -[node name="CallNodeParameter2" parent="MethodSetup/HBoxContainer/ParameterContainer" instance=ExtResource("3_tyq50")] -layout_mode = 2 - -[node name="CallNodeParameter3" parent="MethodSetup/HBoxContainer/ParameterContainer" instance=ExtResource("3_tyq50")] -layout_mode = 2 - [node name="HSeparator" type="HSeparator" parent="."] layout_mode = 2 @@ -106,3 +98,5 @@ layout_mode = 2 [node name="FileDialog" type="FileDialog" parent="."] size = Vector2i(303, 180) + +[connection signal="item_selected" from="MethodSetup/MethodSelector" to="." method="_on_method_selector_item_selected"] diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 78a4d4f..69c6ec6 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -15,12 +15,19 @@ var undo_redo: EditorUndoRedoManager var _calls_script: Script = null var _calls: Dictionary = {} +var _active_method: Dictionary = {} -@onready var _method_button: OptionButton = %MethodSelection +var _arg_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn") +var _return_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn") + +@onready var _method_button: OptionButton = %MethodSelector +@onready var _args_container: Container = %ArgumentsContainer func _ready() -> void: _reload_library(DEFAULT_CALLS) + _reload_method_ui() + _reload_args_ui() func _reload_library(script: Script) -> bool: @@ -38,21 +45,61 @@ func _reload_library(script: Script) -> bool: for method: Dictionary in script.get_script_method_list(): _calls[method.name] = method - # Update Method Button UI + _calls_script = script + return true + + +func _reload_method_ui() -> void: var curr_method_name: String = "" if _method_button.selected > -1: curr_method_name = _method_button.get_item_text(_method_button.selected) _method_button.clear() - var idx: int = 0 + _method_button.selected = -1 + _method_button.add_item("", 0) + + var idx: int = 1 for method: Dictionary in _calls.values(): _method_button.add_item(method.name, idx) if method.name == curr_method_name: _method_button.selected = idx idx += 1 + + +func _reload_args_ui() -> void: + if _active_method.is_empty(): + for child: Node in _args_container.get_children(): + child.queue_free() + _args_container.remove_child(child) + return - _calls_script = script - return true + var _old_args: Dictionary = {} + for node in _args_container.get_children(): + _old_args[node.arg_name] = node + + var args_size: int = _active_method.args.size() + var non_def_args: int = args_size - _active_method.default_args.size() + for i: int in range(0, args_size): + var arg: Dictionary = _active_method.args[i] + if ( + _old_args.has(arg.name) + and arg.type == _old_args[arg.name].type + and (i < non_def_args or _active_method.default_args[i - non_def_args] == _old_args[arg.name].default_arg) + ): + _old_args.erase(arg.name) + continue + + var new_arg_slot: Control = _arg_scene.instantiate() + _args_container.add_child(new_arg_slot) + new_arg_slot.set_data( + arg.name, + arg.type, + null if i < non_def_args else _active_method.default_args[i - non_def_args] + ) + + for outdated_arg: Node in _old_args.values(): + _args_container.remove_child(outdated_arg) + outdated_arg.queue_free() func _to_dict(graph: GraphEdit) -> Dictionary: @@ -66,3 +113,19 @@ func _from_dict(dict: Dictionary) -> Array[String]: func _on_calls_script_changed() -> void: pass #_reload_library() + #_reload_method_ui() + #_reload_args_ui() + + +func _on_method_selector_item_selected(index: int): + if index == -1: + _active_method = {} + else: + var method_name: String = _method_button.get_item_text(index) + if method_name.is_empty(): + _active_method = {} + elif !_calls.has(method_name): + push_error("CallNode's selected method <%s> is not in calls library!" % method_name) + else: + _active_method = _calls[method_name] + _reload_args_ui() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn new file mode 100644 index 0000000..fcf5750 --- /dev/null +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=3 format=3 uid="uid://bskh1vm7i45nw"] + +[ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd" id="1_mqq8d"] +[ext_resource type="Texture2D" uid="uid://cf5ef41yo6jl6" path="res://addons/dialogue_nodes/icons/Reload.svg" id="2_h0t8n"] + +[node name="CallNodeArgument" type="HBoxContainer"] +size_flags_horizontal = 3 +size_flags_vertical = 4 +script = ExtResource("1_mqq8d") + +[node name="ArgumentLabel" type="Label" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 0 +text = "ArgName" + +[node name="ArgumentInput" type="LineEdit" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Default" +placeholder_text = "Type" + +[node name="ResetButton" type="Button" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_vertical = 4 +icon = ExtResource("2_h0t8n") + +[connection signal="text_changed" from="ArgumentInput" to="." method="_on_argument_input_text_changed"] +[connection signal="pressed" from="ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn deleted file mode 100644 index 0fd4991..0000000 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeParameter.tscn +++ /dev/null @@ -1,16 +0,0 @@ -[gd_scene format=3 uid="uid://bskh1vm7i45nw"] - -[node name="CallNodeParameter" type="HBoxContainer"] -size_flags_horizontal = 3 -size_flags_vertical = 4 - -[node name="ParameterLabel" type="Label" parent="."] -layout_mode = 2 -size_flags_horizontal = 0 -text = "Parameter" - -[node name="ParameterInput" type="LineEdit" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -placeholder_text = "Type" diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd new file mode 100644 index 0000000..3247ce2 --- /dev/null +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -0,0 +1,88 @@ +@tool +extends Container +## +## Call Node Argument +## +## Represents an argument in a CallNode's method. Used to store the argument's data, as well as +## react to user editing based on said data. + +## A copy-paste of the enum, but I can't do .keys() on that one... dumb. +enum TYPE_STR { + TYPE_NIL = 0, + TYPE_BOOL = 1, + TYPE_INT = 2, + TYPE_FLOAT = 3, + TYPE_STRING = 4, + TYPE_VECTOR2 = 5, + TYPE_VECTOR2I = 6, + TYPE_RECT2 = 7, + TYPE_RECT2I = 8, + TYPE_VECTOR3 = 9, + TYPE_VECTOR3I = 10, + TYPE_TRANSFORM2D = 11, + TYPE_VECTOR4 = 12, + TYPE_VECTOR4I = 13, + TYPE_PLANE = 14, + TYPE_QUATERNION = 15, + TYPE_AABB = 16, + TYPE_BASIS = 17, + TYPE_TRANSFORM3D = 18, + TYPE_PROJECTION = 19, + TYPE_COLOR = 20, + TYPE_STRING_NAME = 21, + TYPE_NODE_PATH = 22, + TYPE_RID = 23, + TYPE_OBJECT = 24, + TYPE_CALLABLE = 25, + TYPE_SIGNAL = 26, + TYPE_DICTIONARY = 27, + TYPE_ARRAY = 28, + TYPE_PACKED_BYTE_ARRAY = 29, + TYPE_PACKED_INT32_ARRAY = 30, + TYPE_PACKED_INT64_ARRAY = 31, + TYPE_PACKED_FLOAT32_ARRAY = 32, + TYPE_PACKED_FLOAT64_ARRAY = 33, + TYPE_PACKED_STRING_ARRAY = 34, + TYPE_PACKED_VECTOR2_ARRAY = 35, + TYPE_PACKED_VECTOR3_ARRAY = 36, + TYPE_PACKED_COLOR_ARRAY = 37, + TYPE_PACKED_VECTOR4_ARRAY = 38, + TYPE_MAX = 39 +} + +var arg_name: String = "" +var type: Variant.Type = Variant.Type.TYPE_NIL +var default_arg = null + +@onready var _label: Label = %ArgumentLabel +@onready var _input: LineEdit = %ArgumentInput +@onready var _button: Button = %ResetButton + + +func get_data() -> Dictionary: + return { "name": arg_name, "type": type, "default": default_arg } + + +func set_data(new_name: String, new_type: Variant.Type, new_default) -> void: + # Set Name + _label.text = new_name + arg_name = new_name + + # Set Type + _input.placeholder_text = ( + "" if new_type == Variant.Type.TYPE_NIL else TYPE_STR.keys()[int(new_type)] + ) + type = new_type + + # Set Default Value + _input.text = str(new_default if new_default != null else "") + default_arg = new_default + + +func _on_argument_input_text_changed(new_text: String) -> void: + _button.visible = new_text != (str(default_arg) if default_arg != null else "") + + +func _on_reset_button_pressed() -> void: + _input.text = str(default_arg) if default_arg != null else "" + _on_argument_input_text_changed(_input.text) From d3c8b5841006f1f9fd5196cfff182d3b0707a345 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Wed, 9 Oct 2024 17:49:13 +0200 Subject: [PATCH 04/52] CallNode base UI code finished (still missing features) --- addons/dialogue_nodes/editor/calls.gd | 4 +- addons/dialogue_nodes/nodes/CallNode.tscn | 50 +++++-- addons/dialogue_nodes/nodes/callNode.gd | 140 +++++++++++++++--- .../nodes/sub_nodes/CallNodeArgument.tscn | 4 + .../nodes/sub_nodes/CallNodeReturn.tscn | 20 ++- .../nodes/sub_nodes/call_node_argument.gd | 70 ++++----- .../nodes/sub_nodes/call_node_return.gd | 45 ++++++ 7 files changed, 255 insertions(+), 78 deletions(-) create mode 100644 addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index eb0015c..0cdedde 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -7,5 +7,5 @@ extends Script ## This script is expected to be expanded by the user, customized to their needs. ## Alternativelly, the user may create new scripts to call methods from. -func sample_method(nickname: String, age: int, height: float = 5.1) -> Array: - return [nickname, age, height] +func sample_method(nickname, age: int, height: float = 5.1) -> String: + return "My name is %s, I am %d years old, and I am %.2 feet tall!" % [nickname, age, height] diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index 2965dec..0cda4e4 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -1,13 +1,14 @@ -[gd_scene load_steps=4 format=3 uid="uid://cu71fiowdkvmr"] +[gd_scene load_steps=5 format=3 uid="uid://cu71fiowdkvmr"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/callNode.gd" id="1_hul2k"] [ext_resource type="Texture2D" uid="uid://buayifvickq2l" path="res://addons/dialogue_nodes/icons/Folder.svg" id="2_w7ejb"] +[ext_resource type="Texture2D" uid="uid://cj0m8q8hgm0ed" path="res://addons/dialogue_nodes/icons/Add.svg" id="4_hca56"] [ext_resource type="PackedScene" uid="uid://djc2ggs14vbxc" path="res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn" id="4_mw7lj"] [node name="CallNode" type="GraphNode"] -custom_minimum_size = Vector2(300, 0) -offset_right = 300.0 -offset_bottom = 110.0 +custom_minimum_size = Vector2(200, 180) +offset_right = 192.0 +offset_bottom = 183.0 title = "CallNode" slot/0/left_enabled = true slot/0/left_type = 0 @@ -22,9 +23,9 @@ slot/1/left_enabled = false slot/1/left_type = 0 slot/1/left_color = Color(1, 1, 1, 1) slot/1/left_icon = null -slot/1/right_enabled = true +slot/1/right_enabled = false slot/1/right_type = 0 -slot/1/right_color = Color(0.647059, 0.937255, 0.67451, 1) +slot/1/right_color = Color(1, 1, 1, 1) slot/1/right_icon = null slot/1/draw_stylebox = true slot/2/left_enabled = false @@ -45,6 +46,15 @@ slot/3/right_type = 0 slot/3/right_color = Color(1, 1, 1, 1) slot/3/right_icon = null slot/3/draw_stylebox = true +slot/4/left_enabled = false +slot/4/left_type = 0 +slot/4/left_color = Color(1, 1, 1, 1) +slot/4/left_icon = null +slot/4/right_enabled = false +slot/4/right_type = 0 +slot/4/right_color = Color(1, 1, 1, 1) +slot/4/right_icon = null +slot/4/draw_stylebox = true script = ExtResource("1_hul2k") [node name="MethodSetup" type="VBoxContainer" parent="."] @@ -73,15 +83,16 @@ item_count = 2 popup/item_1/text = "sample_method" popup/item_1/id = 1 -[node name="HBoxContainer" type="HBoxContainer" parent="MethodSetup"] +[node name="ArgumentsSectionContainer" type="HBoxContainer" parent="MethodSetup"] +unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 -[node name="VSeparator" type="VSeparator" parent="MethodSetup/HBoxContainer"] +[node name="VSeparator" type="VSeparator" parent="MethodSetup/ArgumentsSectionContainer"] layout_mode = 2 size_flags_horizontal = 0 -[node name="ArgumentsContainer" type="VBoxContainer" parent="MethodSetup/HBoxContainer"] +[node name="ArgumentsContainer" type="VBoxContainer" parent="MethodSetup/ArgumentsSectionContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -93,10 +104,27 @@ layout_mode = 2 layout_mode = 2 text = "Returns" -[node name="CallNodeReturn" parent="." instance=ExtResource("4_mw7lj")] +[node name="AddReturnButton" type="Button" parent="."] +unique_name_in_owner = true +layout_mode = 2 +icon = ExtResource("4_hca56") +icon_alignment = 1 + +[node name="DefaultReturn" parent="." instance=ExtResource("4_mw7lj")] +unique_name_in_owner = true layout_mode = 2 +[node name="ReturnInput" parent="DefaultReturn" index="0"] +placeholder_text = "NoValidReturn" +editable = false + +[node name="RemoveButton" parent="DefaultReturn" index="1"] +visible = false + [node name="FileDialog" type="FileDialog" parent="."] -size = Vector2i(303, 180) +size = Vector2i(800, 400) [connection signal="item_selected" from="MethodSetup/MethodSelector" to="." method="_on_method_selector_item_selected"] +[connection signal="pressed" from="AddReturnButton" to="." method="_on_add_return_button_pressed"] + +[editable path="DefaultReturn"] diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 69c6ec6..a8e47e6 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -16,20 +16,41 @@ var undo_redo: EditorUndoRedoManager var _calls_script: Script = null var _calls: Dictionary = {} var _active_method: Dictionary = {} +var _num_rets: int = 0 var _arg_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn") -var _return_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn") +var _ret_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn") @onready var _method_button: OptionButton = %MethodSelector +@onready var _args_section_container: Container = %ArgumentsSectionContainer @onready var _args_container: Container = %ArgumentsContainer +@onready var _ret_button: Button = %AddReturnButton +@onready var _ret_idx_start: int = _ret_button.get_index() + + +# ------------------------------------------------------------------------------------------------- +# Core +# ------------------------------------------------------------------------------------------------- func _ready() -> void: _reload_library(DEFAULT_CALLS) _reload_method_ui() _reload_args_ui() + _reload_rets_ui() + + +func _to_dict(graph: GraphEdit) -> Dictionary: + return {} +func _from_dict(dict: Dictionary) -> Array[String]: + return [] as Array[String] + + +# ------------------------------------------------------------------------------------------------- +# Method +# ------------------------------------------------------------------------------------------------- func _reload_library(script: Script) -> bool: if script == null: push_error("Cannot reload CallNode's library with a NULL script!") @@ -66,50 +87,124 @@ func _reload_method_ui() -> void: idx += 1 +# ------------------------------------------------------------------------------------------------- +# Arguments +# ------------------------------------------------------------------------------------------------- func _reload_args_ui() -> void: + # If no active method, clear all arguments if _active_method.is_empty(): - for child: Node in _args_container.get_children(): - child.queue_free() - _args_container.remove_child(child) + _clear_arguments() return + # Remove old arguments (don't destroy them yet) var _old_args: Dictionary = {} - for node in _args_container.get_children(): + for node: Node in _args_container.get_children(): _old_args[node.arg_name] = node + _args_container.remove_child(node) + # Instantiate all new resources, recover data from old ones if duplicated var args_size: int = _active_method.args.size() var non_def_args: int = args_size - _active_method.default_args.size() for i: int in range(0, args_size): + # If old argument exactly matches new one, re-add it to container and unmark it as "old". var arg: Dictionary = _active_method.args[i] if ( _old_args.has(arg.name) and arg.type == _old_args[arg.name].type and (i < non_def_args or _active_method.default_args[i - non_def_args] == _old_args[arg.name].default_arg) ): + _args_container.add_child(_old_args[arg.name]) _old_args.erase(arg.name) continue - var new_arg_slot: Control = _arg_scene.instantiate() - _args_container.add_child(new_arg_slot) - new_arg_slot.set_data( - arg.name, - arg.type, - null if i < non_def_args else _active_method.default_args[i - non_def_args] - ) + # If new argument, instantiate a UI for it and set it up. + _add_argument(arg, null if i < non_def_args else _active_method.default_args[i - non_def_args]) + # Remove old arguments that do not exist in new method. for outdated_arg: Node in _old_args.values(): - _args_container.remove_child(outdated_arg) outdated_arg.queue_free() + # Show/Hide Arguments section based on prevailing arguments + _args_section_container.visible = _args_container.get_child_count() > 0 + + +func _add_argument(arg_data: Dictionary, default_arg = null) -> Control: + var new_arg_ui: Control = _arg_scene.instantiate() + _args_container.add_child(new_arg_ui) + new_arg_ui.set_call_node(self) + new_arg_ui.set_data( + arg_data.name, + arg_data.type, + default_arg + ) + return new_arg_ui + + +func _remove_argument(arg: Control) -> Control: + _args_container.remove_child(arg) + arg.queue_free() + reset_size() + return arg + + +func _clear_arguments() -> void: + for arg: Node in _args_container.get_children(): + _remove_argument(arg) + + +# ------------------------------------------------------------------------------------------------- +# Returns +# ------------------------------------------------------------------------------------------------- +func _reload_rets_ui() -> void: + # @Choms-TODO: Add "automatic" bool returns, disallow adding more or less. + #if !_active_method.is_empty() and _active_method.return.type == Variant.Type.TYPE_BOOL: + #clear_returns() + #var true_ret: Control = _add_return() + #var false_ret: Control = _add_return() + #return + + # Reset the type hint on all existing returns. + for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): + var ret: Node = get_child(idx) + if ret != null: + ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) + + +func _add_return() -> Control: + var new_ret: Control = _ret_scene.instantiate() + add_child(new_ret) + move_child(new_ret, _ret_button.get_index()) + _num_rets += 1 + + new_ret.requested_removal.connect(_on_return_requested_removal) + new_ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) + return new_ret + -func _to_dict(graph: GraphEdit) -> Dictionary: - return {} +func _remove_return(ret: Control) -> Control: + if ret.requested_removal.is_connected(_on_return_requested_removal): + ret.requested_removal.disconnect(_on_return_requested_removal) + + if ret.get_parent() != self: + push_error("Cannot remove Return <%s> from CallNode <%s>. Not a child!" % [ret.name, title]) + else: + remove_child(ret) + _num_rets -= 1 + ret.queue_free() + reset_size() + return ret -func _from_dict(dict: Dictionary) -> Array[String]: - return [] as Array[String] +func clear_returns() -> void: + for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): + var ret: Node = get_child(idx) + if ret != null: + _remove_return(ret) +# ------------------------------------------------------------------------------------------------- +# Signals +# ------------------------------------------------------------------------------------------------- func _on_calls_script_changed() -> void: pass #_reload_library() @@ -117,7 +212,7 @@ func _on_calls_script_changed() -> void: #_reload_args_ui() -func _on_method_selector_item_selected(index: int): +func _on_method_selector_item_selected(index: int) -> void: if index == -1: _active_method = {} else: @@ -129,3 +224,12 @@ func _on_method_selector_item_selected(index: int): else: _active_method = _calls[method_name] _reload_args_ui() + _reload_rets_ui() + + +func _on_add_return_button_pressed() -> void: + _add_return() + + +func _on_return_requested_removal(ret: Control) -> void: + _remove_return(ret) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index fcf5750..f4aa632 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -21,13 +21,17 @@ size_flags_horizontal = 3 size_flags_vertical = 4 text = "Default" placeholder_text = "Type" +expand_to_text_length = true [node name="ResetButton" type="Button" parent="."] unique_name_in_owner = true visible = false layout_mode = 2 +size_flags_horizontal = 4 size_flags_vertical = 4 icon = ExtResource("2_h0t8n") +flat = true +[connection signal="focus_exited" from="ArgumentInput" to="." method="_on_argument_input_focus_exited"] [connection signal="text_changed" from="ArgumentInput" to="." method="_on_argument_input_text_changed"] [connection signal="pressed" from="ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index 6bc2857..a4d3d13 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -1,11 +1,27 @@ -[gd_scene format=3 uid="uid://djc2ggs14vbxc"] +[gd_scene load_steps=3 format=3 uid="uid://djc2ggs14vbxc"] + +[ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd" id="1_lcs7a"] +[ext_resource type="Texture2D" uid="uid://b8o81rn8q8a43" path="res://addons/dialogue_nodes/icons/Remove.svg" id="1_t7mvg"] [node name="CallNodeReturn" type="HBoxContainer"] size_flags_horizontal = 3 size_flags_vertical = 4 +script = ExtResource("1_lcs7a") -[node name="ParameterInput" type="LineEdit" parent="."] +[node name="ReturnInput" type="LineEdit" parent="."] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 placeholder_text = "Type" +expand_to_text_length = true + +[node name="RemoveButton" type="Button" parent="."] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +icon = ExtResource("1_t7mvg") +flat = true + +[connection signal="focus_exited" from="ReturnInput" to="." method="_on_return_input_focus_exited"] +[connection signal="pressed" from="RemoveButton" to="." method="_on_remove_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 3247ce2..9da3c95 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -6,59 +6,25 @@ extends Container ## Represents an argument in a CallNode's method. Used to store the argument's data, as well as ## react to user editing based on said data. -## A copy-paste of the enum, but I can't do .keys() on that one... dumb. -enum TYPE_STR { - TYPE_NIL = 0, - TYPE_BOOL = 1, - TYPE_INT = 2, - TYPE_FLOAT = 3, - TYPE_STRING = 4, - TYPE_VECTOR2 = 5, - TYPE_VECTOR2I = 6, - TYPE_RECT2 = 7, - TYPE_RECT2I = 8, - TYPE_VECTOR3 = 9, - TYPE_VECTOR3I = 10, - TYPE_TRANSFORM2D = 11, - TYPE_VECTOR4 = 12, - TYPE_VECTOR4I = 13, - TYPE_PLANE = 14, - TYPE_QUATERNION = 15, - TYPE_AABB = 16, - TYPE_BASIS = 17, - TYPE_TRANSFORM3D = 18, - TYPE_PROJECTION = 19, - TYPE_COLOR = 20, - TYPE_STRING_NAME = 21, - TYPE_NODE_PATH = 22, - TYPE_RID = 23, - TYPE_OBJECT = 24, - TYPE_CALLABLE = 25, - TYPE_SIGNAL = 26, - TYPE_DICTIONARY = 27, - TYPE_ARRAY = 28, - TYPE_PACKED_BYTE_ARRAY = 29, - TYPE_PACKED_INT32_ARRAY = 30, - TYPE_PACKED_INT64_ARRAY = 31, - TYPE_PACKED_FLOAT32_ARRAY = 32, - TYPE_PACKED_FLOAT64_ARRAY = 33, - TYPE_PACKED_STRING_ARRAY = 34, - TYPE_PACKED_VECTOR2_ARRAY = 35, - TYPE_PACKED_VECTOR3_ARRAY = 36, - TYPE_PACKED_COLOR_ARRAY = 37, - TYPE_PACKED_VECTOR4_ARRAY = 38, - TYPE_MAX = 39 -} - var arg_name: String = "" var type: Variant.Type = Variant.Type.TYPE_NIL var default_arg = null +var _call_node: GraphNode = null + @onready var _label: Label = %ArgumentLabel @onready var _input: LineEdit = %ArgumentInput @onready var _button: Button = %ResetButton +func _ready() -> void: + _button.visible = false + + +func set_call_node(call_node: GraphNode) -> void: + _call_node = call_node + + func get_data() -> Dictionary: return { "name": arg_name, "type": type, "default": default_arg } @@ -70,7 +36,7 @@ func set_data(new_name: String, new_type: Variant.Type, new_default) -> void: # Set Type _input.placeholder_text = ( - "" if new_type == Variant.Type.TYPE_NIL else TYPE_STR.keys()[int(new_type)] + type_string(new_type) if new_type != Variant.Type.TYPE_NIL else "" ) type = new_type @@ -86,3 +52,17 @@ func _on_argument_input_text_changed(new_text: String) -> void: func _on_reset_button_pressed() -> void: _input.text = str(default_arg) if default_arg != null else "" _on_argument_input_text_changed(_input.text) + + +func _on_argument_input_focus_exited() -> void: + if type == Variant.Type.TYPE_NIL: + return + var convert_test = type_convert(_input.text, type) + if typeof(convert_test) != type: + push_warning( + "Argument <%s> in CallNode <%s> cannot be converted to type <%s>!" + % [name, _call_node.title, type_string(type)] + ) + + print(_input.text) + print(convert_test) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd new file mode 100644 index 0000000..8a84e35 --- /dev/null +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -0,0 +1,45 @@ +@tool +extends Container +## +## Call Node Return +## +## Represents a possible return of a CallNode's method. Used mainly to manage itself and to +## pass interactions to the CallNode that owns it. + +signal text_changed(ret: Control, new_text: String) +signal requested_removal(ret: Control) + +var type: Variant.Type = Variant.Type.TYPE_NIL + +var _call_node: GraphNode = null + +@onready var _input: LineEdit = %ReturnInput + + +func set_call_node(call_node: GraphNode) -> void: + if call_node != get_parent_control(): + push_error("A CallNodeReturn MUST be a direct child of the CallNode is bound to!") + return + _call_node = call_node + + +func set_type(new_type: Variant.Type) -> void: + _input.placeholder_text = ( + type_string(new_type) if new_type != Variant.Type.TYPE_NIL else "" + ) + type = new_type + + +func _on_return_input_focus_exited() -> void: + if type == Variant.Type.TYPE_NIL: + return + var convert_test = type_convert(_input.text, type) + if typeof(convert_test) != type: + push_warning( + "Return <%s> in CallNode <%s> cannot be converted to type <%s>!" + % [name, _call_node.title, type_string(type)] + ) + + +func _on_remove_button_pressed() -> void: + requested_removal.emit(self) From 7764d67f851e2d5555f5224060865d1d06775bc2 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Wed, 9 Oct 2024 17:54:22 +0200 Subject: [PATCH 05/52] Removed editing "type check", wasn't working --- addons/dialogue_nodes/nodes/callNode.gd | 1 + .../nodes/sub_nodes/call_node_argument.gd | 12 +----------- .../nodes/sub_nodes/call_node_return.gd | 9 +-------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index a8e47e6..5e346ab 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -94,6 +94,7 @@ func _reload_args_ui() -> void: # If no active method, clear all arguments if _active_method.is_empty(): _clear_arguments() + _args_section_container.visible = false return # Remove old arguments (don't destroy them yet) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 9da3c95..0bfe37b 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -55,14 +55,4 @@ func _on_reset_button_pressed() -> void: func _on_argument_input_focus_exited() -> void: - if type == Variant.Type.TYPE_NIL: - return - var convert_test = type_convert(_input.text, type) - if typeof(convert_test) != type: - push_warning( - "Argument <%s> in CallNode <%s> cannot be converted to type <%s>!" - % [name, _call_node.title, type_string(type)] - ) - - print(_input.text) - print(convert_test) + pass diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 8a84e35..f1cf3c1 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -31,14 +31,7 @@ func set_type(new_type: Variant.Type) -> void: func _on_return_input_focus_exited() -> void: - if type == Variant.Type.TYPE_NIL: - return - var convert_test = type_convert(_input.text, type) - if typeof(convert_test) != type: - push_warning( - "Return <%s> in CallNode <%s> cannot be converted to type <%s>!" - % [name, _call_node.title, type_string(type)] - ) + pass func _on_remove_button_pressed() -> void: From 8db7deca6cc87d4b70f789830cde26341d0253a7 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Wed, 9 Oct 2024 23:31:19 +0200 Subject: [PATCH 06/52] ExampleCall backup --- examples/ExampleCall.tres | 193 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 examples/ExampleCall.tres diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres new file mode 100644 index 0000000..133b1c2 --- /dev/null +++ b/examples/ExampleCall.tres @@ -0,0 +1,193 @@ +[gd_resource type="Resource" script_class="DialogueData" load_steps=2 format=3 uid="uid://c2271nfd3mqia"] + +[ext_resource type="Script" path="res://addons/dialogue_nodes/objects/DialogueData.gd" id="1_n0lp3"] + +[resource] +script = ExtResource("1_n0lp3") +starts = { +"START": &"0_1" +} +nodes = { +"0_1": { +"link": &"8_1", +"offset": Vector2(-220, -140), +"start_id": "START" +}, +"1_1": { +"dialogue": "I rolled 1!", +"offset": Vector2(360, -300), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"1_2": { +"dialogue": "I rolled 2!", +"offset": Vector2(700, -300), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"1_3": { +"dialogue": "I rolled 3!", +"offset": Vector2(1020, -300), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"1_4": { +"dialogue": "I rolled 4!", +"offset": Vector2(360, 0), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"1_5": { +"dialogue": "I rolled 5!", +"offset": Vector2(700, 0), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"1_6": { +"dialogue": "I rolled 6!", +"offset": Vector2(1020, 0), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"1_7": { +"dialogue": "I rolled above 6! This wasn't a regular dice!", +"offset": Vector2(360, 300), +"options": { +0: { +"condition": Array[Dictionary]([]), +"link": "END", +"text": "" +} +}, +"size": Vector2(300, 240), +"speaker": "Announcer" +}, +"2_1": { +"comment": "A Call allows you to call a method from the calls.gd +script or another script of your choice with custom +arguments, listing any possible outputs to +determine where the node will connect to +depending on what the method returns, +with a default output if no return case matches.", +"offset": Vector2(-440, 40), +"size": Vector2(380, 200) +}, +"8_1": { +"args": { +0: "", +1: "6" +}, +"default": &"1_7", +"library": "res://addons/dialogue_nodes/editor/calls.gd", +"method": { +"args": Array[Dictionary]([{ +"class_name": &"", +"hint": 0, +"hint_string": "", +"name": "die_name", +"type": 4, +"usage": 0 +}, { +"class_name": &"", +"hint": 0, +"hint_string": "", +"name": "faces", +"type": 2, +"usage": 0 +}]), +"default_args": [6], +"flags": 1, +"id": 0, +"name": "roll_dice", +"return": { +"class_name": &"", +"hint": 0, +"hint_string": "", +"name": "", +"type": 2, +"usage": 0 +} +}, +"offset": Vector2(80, -240), +"rets": { +0: { +"link": &"1_1", +"value": "1" +}, +1: { +"link": &"1_2", +"value": "2" +}, +2: { +"link": &"1_3", +"value": "3" +}, +3: { +"link": &"1_4", +"value": "4" +}, +4: { +"link": &"1_5", +"value": "5" +}, +5: { +"link": &"1_6", +"value": "6" +} +} +} +} +variables = { +"blue_pill": { +"type": 1, +"value": false +}, +"red_pill": { +"type": 1, +"value": false +} +} +strays = Array[String](["2_1"]) +characters = "" From 3a0bf8cd2a55c1f456b8e23e58e725e028aa766b Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Thu, 10 Oct 2024 00:41:08 +0200 Subject: [PATCH 07/52] CallNode is operational! Missing some features though... --- addons/dialogue_nodes/editor/calls.gd | 9 +- addons/dialogue_nodes/nodes/CallNode.tscn | 21 +-- addons/dialogue_nodes/nodes/callNode.gd | 139 +++++++++++++++--- .../nodes/sub_nodes/call_node_argument.gd | 32 ++-- .../nodes/sub_nodes/call_node_return.gd | 12 +- .../dialogue_nodes/objects/DialogueParser.gd | 20 ++- examples/ExampleCall.tres | 7 +- 7 files changed, 188 insertions(+), 52 deletions(-) diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index 0cdedde..a4578cf 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -6,6 +6,11 @@ extends Script ## Method database for all function calls made from DialogueCalls. ## This script is expected to be expanded by the user, customized to their needs. ## Alternativelly, the user may create new scripts to call methods from. +## [br][br] +## Note: Cannot return void nor a container (must always return something that is not Array/Dict). -func sample_method(nickname, age: int, height: float = 5.1) -> String: - return "My name is %s, I am %d years old, and I am %.2 feet tall!" % [nickname, age, height] + +static func roll_dice(die_name: String, faces: int = 6) -> int: + var result: int = randi_range(1, faces) + print("Die %s rolled (<%d> faces). Result is <%d>." % [die_name, faces, result]) + return result diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index 0cda4e4..8b2a264 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -1,9 +1,8 @@ -[gd_scene load_steps=5 format=3 uid="uid://cu71fiowdkvmr"] +[gd_scene load_steps=4 format=3 uid="uid://cu71fiowdkvmr"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/callNode.gd" id="1_hul2k"] [ext_resource type="Texture2D" uid="uid://buayifvickq2l" path="res://addons/dialogue_nodes/icons/Folder.svg" id="2_w7ejb"] [ext_resource type="Texture2D" uid="uid://cj0m8q8hgm0ed" path="res://addons/dialogue_nodes/icons/Add.svg" id="4_hca56"] -[ext_resource type="PackedScene" uid="uid://djc2ggs14vbxc" path="res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn" id="4_mw7lj"] [node name="CallNode" type="GraphNode"] custom_minimum_size = Vector2(200, 180) @@ -50,7 +49,7 @@ slot/4/left_enabled = false slot/4/left_type = 0 slot/4/left_color = Color(1, 1, 1, 1) slot/4/left_icon = null -slot/4/right_enabled = false +slot/4/right_enabled = true slot/4/right_type = 0 slot/4/right_color = Color(1, 1, 1, 1) slot/4/right_icon = null @@ -80,11 +79,12 @@ layout_mode = 2 selected = 0 allow_reselect = true item_count = 2 -popup/item_1/text = "sample_method" +popup/item_1/text = "roll_dice" popup/item_1/id = 1 [node name="ArgumentsSectionContainer" type="HBoxContainer" parent="MethodSetup"] unique_name_in_owner = true +visible = false layout_mode = 2 size_flags_vertical = 4 @@ -110,21 +110,14 @@ layout_mode = 2 icon = ExtResource("4_hca56") icon_alignment = 1 -[node name="DefaultReturn" parent="." instance=ExtResource("4_mw7lj")] +[node name="DefaultLabel" type="Label" parent="."] unique_name_in_owner = true layout_mode = 2 - -[node name="ReturnInput" parent="DefaultReturn" index="0"] -placeholder_text = "NoValidReturn" -editable = false - -[node name="RemoveButton" parent="DefaultReturn" index="1"] -visible = false +text = "Default Exit" +horizontal_alignment = 2 [node name="FileDialog" type="FileDialog" parent="."] size = Vector2i(800, 400) [connection signal="item_selected" from="MethodSetup/MethodSelector" to="." method="_on_method_selector_item_selected"] [connection signal="pressed" from="AddReturnButton" to="." method="_on_add_return_button_pressed"] - -[editable path="DefaultReturn"] diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 5e346ab..7f960ab 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -18,6 +18,8 @@ var _calls: Dictionary = {} var _active_method: Dictionary = {} var _num_rets: int = 0 +var base_color: Color = Color.WHITE + var _arg_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn") var _ret_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn") @@ -26,6 +28,7 @@ var _ret_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nod @onready var _args_container: Container = %ArgumentsContainer @onready var _ret_button: Button = %AddReturnButton +@onready var _def_ret: Control = %DefaultLabel @onready var _ret_idx_start: int = _ret_button.get_index() @@ -38,18 +41,95 @@ func _ready() -> void: _reload_method_ui() _reload_args_ui() _reload_rets_ui() + _update_slots() func _to_dict(graph: GraphEdit) -> Dictionary: - return {} + var dict := {} + + # Export Library + dict['library'] = _calls_script.resource_path + + # Export Method + dict['method'] = _active_method + + # Export Arguments + var args_arr := [] + for arg: Control in _args_container.get_children(): + args_arr.push_back(arg.get_arg()) + dict['args'] = args_arr + + # Export Connected Returns + var rets_dict := {} + for connection: Dictionary in graph.get_connections(name): + var idx: int = connection['from_port'] # this returns index starting from 0 + + if idx == _num_rets: + dict['default'] = connection['to_node'] + continue + + rets_dict[idx] = {} + rets_dict[idx]['value'] = get_child(_ret_idx_start + idx).get_ret() + rets_dict[idx]['link'] = connection['to_node'] + + # Export Unconnected Returns + for idx: int in _num_rets: + if rets_dict.has(idx): continue + + rets_dict[idx] = {} + rets_dict[idx]['value'] = get_child(_ret_idx_start + idx).get_ret() + rets_dict[idx]['link'] = 'END' + + dict['rets'] = rets_dict + + # Export Default Return + if not dict.has('default'): dict['default'] = 'END' + + return dict func _from_dict(dict: Dictionary) -> Array[String]: - return [] as Array[String] + # Import Library + _reload_library(load(dict['library'])) + + # Import Method + _reload_method_ui() + if dict['method'].is_empty(): + _clear_method() + else: + _set_method(dict['method'].name) + for i: int in _method_button.item_count: + if _method_button.get_item_text(i) == dict['method'].name: + _method_button.select(i) + break + + # Import Arguments + var args: Array[Node] = _args_container.get_children() + for idx: int in dict['args'].size(): + args[idx].set_arg(dict['args'][idx]) + + # Import Returns + var next_nodes: Array[String] = [] + for idx: int in dict['rets']: + var ret: Control = _add_return() + ret.set_ret(dict['rets'][idx]['value']) + next_nodes.append(dict['rets'][idx]['link']) + next_nodes.append(dict['default']) + + return next_nodes + + +func _update_slots() -> void: + var children: Array[Node] = get_children() + for i: int in range(_ret_idx_start, _ret_idx_start + _num_rets): + var ret: Control = children[i] + set_slot(ret.get_index(), false, 0, base_color, true, 0, base_color) + set_slot(_ret_button.get_index(), false, 0, base_color, false, 0, base_color) + set_slot(_def_ret.get_index(), false, 0, base_color, true, 0, base_color) # ------------------------------------------------------------------------------------------------- -# Method +# Library # ------------------------------------------------------------------------------------------------- func _reload_library(script: Script) -> bool: if script == null: @@ -70,14 +150,17 @@ func _reload_library(script: Script) -> bool: return true +# ------------------------------------------------------------------------------------------------- +# Method +# ------------------------------------------------------------------------------------------------- func _reload_method_ui() -> void: - var curr_method_name: String = "" + var curr_method_name: String = '' if _method_button.selected > -1: curr_method_name = _method_button.get_item_text(_method_button.selected) _method_button.clear() _method_button.selected = -1 - _method_button.add_item("", 0) + _method_button.add_item('', 0) var idx: int = 1 for method: Dictionary in _calls.values(): @@ -87,6 +170,21 @@ func _reload_method_ui() -> void: idx += 1 +func _set_method(method_name: String) -> void: + if !_calls.has(method_name): + push_error("CallNode's selected method <%s> is not in calls library!" % method_name) + return + _active_method = _calls[method_name] + _reload_args_ui() + _reload_rets_ui() + + +func _clear_method() -> void: + _active_method = {} + _reload_args_ui() + _reload_rets_ui() + + # ------------------------------------------------------------------------------------------------- # Arguments # ------------------------------------------------------------------------------------------------- @@ -119,7 +217,7 @@ func _reload_args_ui() -> void: continue # If new argument, instantiate a UI for it and set it up. - _add_argument(arg, null if i < non_def_args else _active_method.default_args[i - non_def_args]) + _add_argument(arg, "", null if i < non_def_args else _active_method.default_args[i - non_def_args]) # Remove old arguments that do not exist in new method. for outdated_arg: Node in _old_args.values(): @@ -129,13 +227,14 @@ func _reload_args_ui() -> void: _args_section_container.visible = _args_container.get_child_count() > 0 -func _add_argument(arg_data: Dictionary, default_arg = null) -> Control: +func _add_argument(arg_data: Dictionary, arg: String = "", default_arg = null) -> Control: var new_arg_ui: Control = _arg_scene.instantiate() _args_container.add_child(new_arg_ui) new_arg_ui.set_call_node(self) new_arg_ui.set_data( arg_data.name, arg_data.type, + arg, default_arg ) return new_arg_ui @@ -179,6 +278,8 @@ func _add_return() -> Control: new_ret.requested_removal.connect(_on_return_requested_removal) new_ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) + + _update_slots() return new_ret @@ -187,12 +288,14 @@ func _remove_return(ret: Control) -> Control: ret.requested_removal.disconnect(_on_return_requested_removal) if ret.get_parent() != self: - push_error("Cannot remove Return <%s> from CallNode <%s>. Not a child!" % [ret.name, title]) + push_error('Cannot remove Return <%s> from CallNode <%s>. Not a child!' % [ret.name, title]) else: remove_child(ret) _num_rets -= 1 ret.queue_free() reset_size() + + _update_slots() return ret @@ -215,17 +318,15 @@ func _on_calls_script_changed() -> void: func _on_method_selector_item_selected(index: int) -> void: if index == -1: - _active_method = {} - else: - var method_name: String = _method_button.get_item_text(index) - if method_name.is_empty(): - _active_method = {} - elif !_calls.has(method_name): - push_error("CallNode's selected method <%s> is not in calls library!" % method_name) - else: - _active_method = _calls[method_name] - _reload_args_ui() - _reload_rets_ui() + _clear_method() + return + + var method_name: String = _method_button.get_item_text(index) + if method_name.is_empty(): + _clear_method() + return + + _set_method(method_name) func _on_add_return_button_pressed() -> void: diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 0bfe37b..9a26e7e 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -6,7 +6,7 @@ extends Container ## Represents an argument in a CallNode's method. Used to store the argument's data, as well as ## react to user editing based on said data. -var arg_name: String = "" +var arg_name: String = '' var type: Variant.Type = Variant.Type.TYPE_NIL var default_arg = null @@ -25,32 +25,46 @@ func set_call_node(call_node: GraphNode) -> void: _call_node = call_node +func get_arg() -> String: + return _input.text + + +func set_arg(new_arg: String) -> void: + _input.text = new_arg + _button.visible = new_arg != (str(default_arg) if default_arg != null else '') + + func get_data() -> Dictionary: - return { "name": arg_name, "type": type, "default": default_arg } + return { 'name': arg_name, 'type': type, 'argument': _input.text, 'default': default_arg } -func set_data(new_name: String, new_type: Variant.Type, new_default) -> void: +func set_data(new_name: String, new_type: Variant.Type, argument: String, new_default) -> void: # Set Name _label.text = new_name arg_name = new_name - + # Set Type _input.placeholder_text = ( - type_string(new_type) if new_type != Variant.Type.TYPE_NIL else "" + type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' ) type = new_type - + # Set Default Value - _input.text = str(new_default if new_default != null else "") default_arg = new_default + + # Set Argument + if !argument.is_empty(): + set_arg(argument) + else: + set_arg(str(new_default) if new_default != null else '') func _on_argument_input_text_changed(new_text: String) -> void: - _button.visible = new_text != (str(default_arg) if default_arg != null else "") + _button.visible = new_text != (str(default_arg) if default_arg != null else '') func _on_reset_button_pressed() -> void: - _input.text = str(default_arg) if default_arg != null else "" + _input.text = str(default_arg) if default_arg != null else '' _on_argument_input_text_changed(_input.text) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index f1cf3c1..ded62d2 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -18,14 +18,22 @@ var _call_node: GraphNode = null func set_call_node(call_node: GraphNode) -> void: if call_node != get_parent_control(): - push_error("A CallNodeReturn MUST be a direct child of the CallNode is bound to!") + push_error('A CallNodeReturn MUST be a direct child of the CallNode is bound to!') return _call_node = call_node +func get_ret() -> String: + return _input.text + + +func set_ret(new_ret: String) -> void: + _input.text = new_ret + + func set_type(new_type: Variant.Type) -> void: _input.placeholder_text = ( - type_string(new_type) if new_type != Variant.Type.TYPE_NIL else "" + type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' ) type = new_type diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index ec44e21..7e5ec14 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -125,7 +125,8 @@ func _proceed(node_name: String): _process_set, _process_condition, _process_nest, - _process_fork + _process_fork, + _process_call ] var id := int(node_name.split('_')[0]) @@ -237,6 +238,23 @@ func _process_fork(dict : Dictionary): _proceed(result) +func _process_call(dict: Dictionary): + if dict.method.is_empty(): + _proceed(dict.default) + return + + var args: Array = [] + for idx: int in dict.args.size(): + args.push_back(type_convert(dict.args[idx], dict.method.args[idx].type)) + var ret = (load(dict.library) as Script).callv(dict.method.name, args) + + for idx: int in dict.rets: + if type_convert(dict.rets[idx].value, dict.method.return.type) == ret: + _proceed(dict.rets[idx].link) + return + _proceed(dict.default) + + # Checks the condition based on dict.value1, dict.value2 and dict.operator func _check_condition(conditions: Array): var result := true diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres index 133b1c2..638d64d 100644 --- a/examples/ExampleCall.tres +++ b/examples/ExampleCall.tres @@ -115,10 +115,7 @@ with a default output if no return case matches.", "size": Vector2(380, 200) }, "8_1": { -"args": { -0: "", -1: "6" -}, +"args": ["Regular", "6"], "default": &"1_7", "library": "res://addons/dialogue_nodes/editor/calls.gd", "method": { @@ -138,7 +135,7 @@ with a default output if no return case matches.", "usage": 0 }]), "default_args": [6], -"flags": 1, +"flags": 33, "id": 0, "name": "roll_dice", "return": { From f7df213b67312737d547993bcd918ca642d14d61 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Thu, 10 Oct 2024 14:55:55 +0200 Subject: [PATCH 08/52] Added Connection Shifting to CallNode --- addons/dialogue_nodes/editor/graph.gd | 4 ++-- addons/dialogue_nodes/nodes/callNode.gd | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 8b7c44c..1275435 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -152,7 +152,7 @@ func connect_node_signals(node : GraphNode): characters_updated.connect(node._on_characters_updated) node.disconnection_from_request.connect(_on_disconnection_from_request) node.connection_shift_request.connect(_on_connection_shift_request) - 7: # fork node + 7, 8: # fork node, call node node.disconnection_from_request.connect(_on_disconnection_from_request) node.connection_shift_request.connect(_on_connection_shift_request) @@ -170,7 +170,7 @@ func disconnect_node_signals(node : GraphNode): characters_updated.disconnect(node._on_characters_updated) node.disconnection_from_request.disconnect(_on_disconnection_from_request) node.connection_shift_request.disconnect(_on_connection_shift_request) - 7: # fork node + 7, 8: # fork node, call node node.disconnection_from_request.disconnect(_on_disconnection_from_request) node.connection_shift_request.disconnect(_on_connection_shift_request) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 7f960ab..512a491 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -10,6 +10,8 @@ extends GraphNode const DEFAULT_CALLS: Script = preload("res://addons/dialogue_nodes/editor/calls.gd") signal modified +signal disconnection_from_request(from_node: String, from_port: int) +signal connection_shift_request(from_node: String, old_port: int, new_port: int) var undo_redo: EditorUndoRedoManager @@ -279,11 +281,28 @@ func _add_return() -> Control: new_ret.requested_removal.connect(_on_return_requested_removal) new_ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) + # Shift Return Connections + for i: int in range(_num_rets - 1, new_ret.get_index() - _ret_idx_start, -1): + connection_shift_request.emit(name, i - 1, i) + + # Shift Default Return Connection + set_slot(_ret_idx_start + _num_rets + 1, false, 0, base_color, true, 0, base_color) + connection_shift_request.emit(name, _num_rets - 1, _num_rets) + _update_slots() return new_ret func _remove_return(ret: Control) -> Control: + # Shift Return Connections + var ret_idx: int = ret.get_index() - _ret_idx_start + disconnection_from_request.emit(name, ret_idx) + for i: int in range(ret_idx, _num_rets - 1): + connection_shift_request.emit(name, i + 1, i) + + # Shift Default Return Connection + connection_shift_request.emit(name, _num_rets, _num_rets - 1) + if ret.requested_removal.is_connected(_on_return_requested_removal): ret.requested_removal.disconnect(_on_return_requested_removal) From 0eb30cb338b87cc3315901da1ad818507db8815f Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Thu, 10 Oct 2024 18:04:49 +0200 Subject: [PATCH 09/52] Bugfixing + Expanding system to include Arrays and Dicts --- addons/dialogue_nodes/editor/calls.gd | 32 ++++++++++++-- addons/dialogue_nodes/nodes/CallNode.tscn | 4 -- addons/dialogue_nodes/nodes/callNode.gd | 34 ++++++++++----- .../dialogue_nodes/objects/DialogueParser.gd | 15 +++++-- examples/ExampleCall.tres | 43 ++++++++----------- 5 files changed, 83 insertions(+), 45 deletions(-) diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index a4578cf..8f3958b 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -7,10 +7,36 @@ extends Script ## This script is expected to be expanded by the user, customized to their needs. ## Alternativelly, the user may create new scripts to call methods from. ## [br][br] -## Note: Cannot return void nor a container (must always return something that is not Array/Dict). +## [color=Yellow]Warning[/color]: [Array] and [Dictionary] arguments [b]cannot[/b] have default values. +## It is recommended they Static Typing is not used to specify their contents either. -static func roll_dice(die_name: String, faces: int = 6) -> int: - var result: int = randi_range(1, faces) +static func roll_a_die(die_name: String, ignore: Array, faces: int = 6) -> int: + var result: int = -1 + for i: int in 100: + result = randi_range(1, faces) + if !ignore.has(result): + break + if result == -1: + push_error( + "Die %s rolled (<%d> faces). Set to ignore <%s>. Rolling a valid result was imposible!" + % [die_name, faces, ignore] + ) + return -1 + print("Die %s rolled (<%d> faces). Result is <%d>." % [die_name, faces, result]) return result + + +static func roll_dice(dice: Dictionary) -> Array: + var results: Array[int] = [] + for die_name: String in dice: + var die: Dictionary = dice[die_name] + if !die.has("faces"): + push_error("Invalid dice <%s>. Cannot roll, it has no faces key!" % die_name) + continue + + var result: int = roll_a_die(die_name, die.ignore if die.has("ignore") else [], die.faces) + if result != -1: + results.push_back(result) + return results diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index 8b2a264..9039e7f 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -76,11 +76,7 @@ icon = ExtResource("2_w7ejb") [node name="MethodSelector" type="OptionButton" parent="MethodSetup"] unique_name_in_owner = true layout_mode = 2 -selected = 0 allow_reselect = true -item_count = 2 -popup/item_1/text = "roll_dice" -popup/item_1/id = 1 [node name="ArgumentsSectionContainer" type="HBoxContainer" parent="MethodSetup"] unique_name_in_owner = true diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 512a491..483df8f 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -6,6 +6,9 @@ extends GraphNode ## This Node calls a function from an expandable library of callables to determine which Node ## output to take based on what the method returned. ## The library, a static object called [DialogueCalls], is expected to be expanded by the user. +## [br][br] +## [color=Yellow]Warning[/color]: All [i]Arguments[/i] and [i]Returns[/i] must be formatted +## so they can be converted to their appropriate types via [method @GlobalScope.str_to_var]. const DEFAULT_CALLS: Script = preload("res://addons/dialogue_nodes/editor/calls.gd") @@ -99,16 +102,25 @@ func _from_dict(dict: Dictionary) -> Array[String]: if dict['method'].is_empty(): _clear_method() else: - _set_method(dict['method'].name) - for i: int in _method_button.item_count: - if _method_button.get_item_text(i) == dict['method'].name: - _method_button.select(i) - break + if _set_method(dict['method'].name) == true: + for i: int in _method_button.item_count: + if _method_button.get_item_text(i) == dict['method'].name: + _method_button.select(i) + break + else: + _method_button.selected = -1 # Import Arguments - var args: Array[Node] = _args_container.get_children() - for idx: int in dict['args'].size(): - args[idx].set_arg(dict['args'][idx]) + if !_active_method.is_empty(): + var args: Array[Node] = _args_container.get_children() + if args.size() != dict['args'].size(): + push_error( + "Number of Args of CallNode's <%s> Save and loaded Method <%s> do not match <%d != %d>!" + % [title, _active_method.name, args.size(), dict['args'].size()] + ) + else: + for idx: int in dict['args'].size(): + args[idx].set_arg(dict['args'][idx]) # Import Returns var next_nodes: Array[String] = [] @@ -172,13 +184,14 @@ func _reload_method_ui() -> void: idx += 1 -func _set_method(method_name: String) -> void: +func _set_method(method_name: String) -> bool: if !_calls.has(method_name): push_error("CallNode's selected method <%s> is not in calls library!" % method_name) - return + return false _active_method = _calls[method_name] _reload_args_ui() _reload_rets_ui() + return true func _clear_method() -> void: @@ -279,6 +292,7 @@ func _add_return() -> Control: _num_rets += 1 new_ret.requested_removal.connect(_on_return_requested_removal) + new_ret.set_call_node(self) new_ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) # Shift Return Connections diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index 7e5ec14..ba44b97 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -245,11 +245,20 @@ func _process_call(dict: Dictionary): var args: Array = [] for idx: int in dict.args.size(): - args.push_back(type_convert(dict.args[idx], dict.method.args[idx].type)) - var ret = (load(dict.library) as Script).callv(dict.method.name, args) + args.push_back( + dict.args[idx] + if dict.method.args[idx].type == Variant.Type.TYPE_STRING + else str_to_var(dict.args[idx]) + ) + var ret = (load(dict.library) as Script).callv(dict.method.name, args) for idx: int in dict.rets: - if type_convert(dict.rets[idx].value, dict.method.return.type) == ret: + var ret_option = ( + dict.rets[idx].value + if dict.method.return.type == Variant.Type.TYPE_STRING + else str_to_var(dict.rets[idx].value) + ) + if typeof(ret_option) == typeof(ret) and ret_option == ret: _proceed(dict.rets[idx].link) return _proceed(dict.default) diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres index 638d64d..67d1529 100644 --- a/examples/ExampleCall.tres +++ b/examples/ExampleCall.tres @@ -10,11 +10,11 @@ starts = { nodes = { "0_1": { "link": &"8_1", -"offset": Vector2(-220, -140), +"offset": Vector2(-300, -220), "start_id": "START" }, "1_1": { -"dialogue": "I rolled 1!", +"dialogue": "I will never roll a 1, as we ignore odd numbers.", "offset": Vector2(360, -300), "options": { 0: { @@ -40,7 +40,7 @@ nodes = { "speaker": "Announcer" }, "1_3": { -"dialogue": "I rolled 3!", +"dialogue": "I will never roll a 3, as we ignore odd numbers.", "offset": Vector2(1020, -300), "options": { 0: { @@ -66,7 +66,7 @@ nodes = { "speaker": "Announcer" }, "1_5": { -"dialogue": "I rolled 5!", +"dialogue": "I will never roll a 5, as we ignore odd numbers.", "offset": Vector2(700, 0), "options": { 0: { @@ -92,7 +92,7 @@ nodes = { "speaker": "Announcer" }, "1_7": { -"dialogue": "I rolled above 6! This wasn't a regular dice!", +"dialogue": "I rolled something beyond 1-6. This is not your typical dice!", "offset": Vector2(360, 300), "options": { 0: { @@ -111,11 +111,11 @@ arguments, listing any possible outputs to determine where the node will connect to depending on what the method returns, with a default output if no return case matches.", -"offset": Vector2(-440, 40), +"offset": Vector2(-500, -60), "size": Vector2(380, 200) }, "8_1": { -"args": ["Regular", "6"], +"args": ["{ \"d6\": {\"faces\": 7, \"ignore\": [1,3,5]} }"], "default": &"1_7", "library": "res://addons/dialogue_nodes/editor/calls.gd", "method": { @@ -123,18 +123,11 @@ with a default output if no return case matches.", "class_name": &"", "hint": 0, "hint_string": "", -"name": "die_name", -"type": 4, -"usage": 0 -}, { -"class_name": &"", -"hint": 0, -"hint_string": "", -"name": "faces", -"type": 2, +"name": "dice", +"type": 27, "usage": 0 }]), -"default_args": [6], +"default_args": [], "flags": 33, "id": 0, "name": "roll_dice", @@ -143,35 +136,35 @@ with a default output if no return case matches.", "hint": 0, "hint_string": "", "name": "", -"type": 2, +"type": 28, "usage": 0 } }, -"offset": Vector2(80, -240), +"offset": Vector2(-60, -220), "rets": { 0: { "link": &"1_1", -"value": "1" +"value": "[1]" }, 1: { "link": &"1_2", -"value": "2" +"value": "[2]" }, 2: { "link": &"1_3", -"value": "3" +"value": "[3]" }, 3: { "link": &"1_4", -"value": "4" +"value": "[4]" }, 4: { "link": &"1_5", -"value": "5" +"value": "[5]" }, 5: { "link": &"1_6", -"value": "6" +"value": "[6]" } } } From 38cd2da7058431df58ee86c1fef5db4bc0fe49c5 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Thu, 10 Oct 2024 18:05:02 +0200 Subject: [PATCH 10/52] QoL: Resize when TextEdits are changed --- .../dialogue_nodes/nodes/sub_nodes/call_node_argument.gd | 2 +- addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 9a26e7e..6bd5199 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -69,4 +69,4 @@ func _on_reset_button_pressed() -> void: func _on_argument_input_focus_exited() -> void: - pass + _call_node.reset_size() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index ded62d2..131fb8e 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -38,9 +38,9 @@ func set_type(new_type: Variant.Type) -> void: type = new_type -func _on_return_input_focus_exited() -> void: - pass - - func _on_remove_button_pressed() -> void: requested_removal.emit(self) + + +func _on_return_input_focus_exited() -> void: + _call_node.reset_size() From c59570dd33a18dcc1fafecd28e5af286928874c7 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Thu, 10 Oct 2024 18:30:20 +0200 Subject: [PATCH 11/52] Added type checker when Arguments and Returns are edited. --- addons/dialogue_nodes/editor/calls.gd | 4 ++++ addons/dialogue_nodes/nodes/callNode.gd | 12 +++++++----- .../nodes/sub_nodes/call_node_argument.gd | 13 +++++++++++++ .../nodes/sub_nodes/call_node_return.gd | 13 +++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index 8f3958b..e2a9f87 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -11,6 +11,10 @@ extends Script ## It is recommended they Static Typing is not used to specify their contents either. +static func print_text(text: String) -> void: + print(text) + + static func roll_a_die(die_name: String, ignore: Array, faces: int = 6) -> int: var result: int = -1 for i: int in 100: diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 483df8f..afa5d69 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -10,7 +10,7 @@ extends GraphNode ## [color=Yellow]Warning[/color]: All [i]Arguments[/i] and [i]Returns[/i] must be formatted ## so they can be converted to their appropriate types via [method @GlobalScope.str_to_var]. -const DEFAULT_CALLS: Script = preload("res://addons/dialogue_nodes/editor/calls.gd") +const DEFAULT_CALLS: Script = preload('res://addons/dialogue_nodes/editor/calls.gd') signal modified signal disconnection_from_request(from_node: String, from_port: int) @@ -25,8 +25,8 @@ var _num_rets: int = 0 var base_color: Color = Color.WHITE -var _arg_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn") -var _ret_scene: PackedScene = preload("res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn") +var _arg_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn') +var _ret_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn') @onready var _method_button: OptionButton = %MethodSelector @onready var _args_section_container: Container = %ArgumentsSectionContainer @@ -191,6 +191,7 @@ func _set_method(method_name: String) -> bool: _active_method = _calls[method_name] _reload_args_ui() _reload_rets_ui() + reset_size() return true @@ -198,6 +199,7 @@ func _clear_method() -> void: _active_method = {} _reload_args_ui() _reload_rets_ui() + reset_size() # ------------------------------------------------------------------------------------------------- @@ -232,7 +234,7 @@ func _reload_args_ui() -> void: continue # If new argument, instantiate a UI for it and set it up. - _add_argument(arg, "", null if i < non_def_args else _active_method.default_args[i - non_def_args]) + _add_argument(arg, '', null if i < non_def_args else _active_method.default_args[i - non_def_args]) # Remove old arguments that do not exist in new method. for outdated_arg: Node in _old_args.values(): @@ -242,7 +244,7 @@ func _reload_args_ui() -> void: _args_section_container.visible = _args_container.get_child_count() > 0 -func _add_argument(arg_data: Dictionary, arg: String = "", default_arg = null) -> Control: +func _add_argument(arg_data: Dictionary, arg: String = '', default_arg = null) -> Control: var new_arg_ui: Control = _arg_scene.instantiate() _args_container.add_child(new_arg_ui) new_arg_ui.set_call_node(self) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 6bd5199..d188b6f 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -59,6 +59,14 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de set_arg(str(new_default) if new_default != null else '') +func _validate_input_type() -> bool: + return ( + type == Variant.Type.TYPE_NIL + or type == Variant.Type.TYPE_STRING + or typeof(str_to_var(_input.text)) == type + ) + + func _on_argument_input_text_changed(new_text: String) -> void: _button.visible = new_text != (str(default_arg) if default_arg != null else '') @@ -69,4 +77,9 @@ func _on_reset_button_pressed() -> void: func _on_argument_input_focus_exited() -> void: + if !_validate_input_type(): + push_error( + 'Argument <%s> with value <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' + % [arg_name, _input.text, _call_node.title, type_string(type)] + ) _call_node.reset_size() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 131fb8e..f782e38 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -38,9 +38,22 @@ func set_type(new_type: Variant.Type) -> void: type = new_type +func _validate_input_type() -> bool: + return ( + type == Variant.Type.TYPE_NIL + or type == Variant.Type.TYPE_STRING + or typeof(str_to_var(_input.text)) == type + ) + + func _on_remove_button_pressed() -> void: requested_removal.emit(self) func _on_return_input_focus_exited() -> void: + if !_validate_input_type(): + push_error( + 'Return <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' + % [_input.text, _call_node.title, type_string(type)] + ) _call_node.reset_size() From 85c4344f764a983c8cf4de563a19404af4426a02 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Thu, 10 Oct 2024 22:07:56 +0200 Subject: [PATCH 12/52] Removed Bool auto-setup comment --- addons/dialogue_nodes/nodes/callNode.gd | 7 ------- 1 file changed, 7 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index afa5d69..39b154d 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -273,13 +273,6 @@ func _clear_arguments() -> void: # Returns # ------------------------------------------------------------------------------------------------- func _reload_rets_ui() -> void: - # @Choms-TODO: Add "automatic" bool returns, disallow adding more or less. - #if !_active_method.is_empty() and _active_method.return.type == Variant.Type.TYPE_BOOL: - #clear_returns() - #var true_ret: Control = _add_return() - #var false_ret: Control = _add_return() - #return - # Reset the type hint on all existing returns. for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): var ret: Node = get_child(idx) From 0e2b11089d426d0e8d531aa09c313c678b99e170 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Fri, 11 Oct 2024 12:47:57 +0200 Subject: [PATCH 13/52] Added TextEdit option to Arguments --- addons/dialogue_nodes/icons/LineEdit.svg | 1 + .../dialogue_nodes/icons/LineEdit.svg.import | 37 ++++++++++ addons/dialogue_nodes/icons/TextEdit.svg | 1 + .../dialogue_nodes/icons/TextEdit.svg.import | 37 ++++++++++ .../nodes/sub_nodes/CallNodeArgument.tscn | 39 ++++++++-- .../nodes/sub_nodes/call_node_argument.gd | 72 ++++++++++++++----- 6 files changed, 164 insertions(+), 23 deletions(-) create mode 100644 addons/dialogue_nodes/icons/LineEdit.svg create mode 100644 addons/dialogue_nodes/icons/LineEdit.svg.import create mode 100644 addons/dialogue_nodes/icons/TextEdit.svg create mode 100644 addons/dialogue_nodes/icons/TextEdit.svg.import diff --git a/addons/dialogue_nodes/icons/LineEdit.svg b/addons/dialogue_nodes/icons/LineEdit.svg new file mode 100644 index 0000000..a0e3fae --- /dev/null +++ b/addons/dialogue_nodes/icons/LineEdit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/LineEdit.svg.import b/addons/dialogue_nodes/icons/LineEdit.svg.import new file mode 100644 index 0000000..f4ef853 --- /dev/null +++ b/addons/dialogue_nodes/icons/LineEdit.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1quecpfgc2t0" +path="res://.godot/imported/LineEdit.svg-1efa104133ff410e7a84270b63cde1a1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_nodes/icons/LineEdit.svg" +dest_files=["res://.godot/imported/LineEdit.svg-1efa104133ff410e7a84270b63cde1a1.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/addons/dialogue_nodes/icons/TextEdit.svg b/addons/dialogue_nodes/icons/TextEdit.svg new file mode 100644 index 0000000..93a9266 --- /dev/null +++ b/addons/dialogue_nodes/icons/TextEdit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/TextEdit.svg.import b/addons/dialogue_nodes/icons/TextEdit.svg.import new file mode 100644 index 0000000..c70fd03 --- /dev/null +++ b/addons/dialogue_nodes/icons/TextEdit.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1infs2kx18p8" +path="res://.godot/imported/TextEdit.svg-ce951a4a570a9bd61fe362f3e8f2c77a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_nodes/icons/TextEdit.svg" +dest_files=["res://.godot/imported/TextEdit.svg-ce951a4a570a9bd61fe362f3e8f2c77a.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/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index f4aa632..ac351a0 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=3 format=3 uid="uid://bskh1vm7i45nw"] +[gd_scene load_steps=4 format=3 uid="uid://bskh1vm7i45nw"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd" id="1_mqq8d"] [ext_resource type="Texture2D" uid="uid://cf5ef41yo6jl6" path="res://addons/dialogue_nodes/icons/Reload.svg" id="2_h0t8n"] +[ext_resource type="Texture2D" uid="uid://c1quecpfgc2t0" path="res://addons/dialogue_nodes/icons/LineEdit.svg" id="3_eqbi1"] [node name="CallNodeArgument" type="HBoxContainer"] size_flags_horizontal = 3 @@ -14,7 +15,7 @@ layout_mode = 2 size_flags_horizontal = 0 text = "ArgName" -[node name="ArgumentInput" type="LineEdit" parent="."] +[node name="ArgumentLineEdit" type="LineEdit" parent="."] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -23,15 +24,41 @@ text = "Default" placeholder_text = "Type" expand_to_text_length = true -[node name="ResetButton" type="Button" parent="."] +[node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true visible = false +custom_minimum_size = Vector2(140, 0) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Default" +placeholder_text = "Type" +scroll_fit_content_height = true + +[node name="VFlowContainer" type="VFlowContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 4 +alignment = 1 + +[node name="ResetButton" type="Button" parent="VFlowContainer"] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 icon = ExtResource("2_h0t8n") flat = true -[connection signal="focus_exited" from="ArgumentInput" to="." method="_on_argument_input_focus_exited"] -[connection signal="text_changed" from="ArgumentInput" to="." method="_on_argument_input_text_changed"] -[connection signal="pressed" from="ResetButton" to="." method="_on_reset_button_pressed"] +[node name="SwapEditButton" type="Button" parent="VFlowContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +icon = ExtResource("3_eqbi1") +flat = true + +[connection signal="focus_exited" from="ArgumentLineEdit" to="." method="_on_any_argument_edit_focus_exited"] +[connection signal="text_changed" from="ArgumentLineEdit" to="." method="_on_argument_line_edit_text_changed"] +[connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_any_argument_edit_focus_exited"] +[connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] +[connection signal="pressed" from="VFlowContainer/ResetButton" to="." method="_on_reset_button_pressed"] +[connection signal="pressed" from="VFlowContainer/SwapEditButton" to="." method="_on_swap_edit_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index d188b6f..7e8b10b 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -6,19 +6,27 @@ extends Container ## Represents an argument in a CallNode's method. Used to store the argument's data, as well as ## react to user editing based on said data. +@export var _line_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/LineEdit.svg') +@export var _text_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/TextEdit.svg') + var arg_name: String = '' var type: Variant.Type = Variant.Type.TYPE_NIL var default_arg = null var _call_node: GraphNode = null +var _arg: String = "" @onready var _label: Label = %ArgumentLabel -@onready var _input: LineEdit = %ArgumentInput -@onready var _button: Button = %ResetButton +@onready var _line_edit: LineEdit = %ArgumentLineEdit +@onready var _text_edit: TextEdit = %ArgumentTextEdit + +@onready var _reset_button: Button = %ResetButton +@onready var _swap_edit_button: Button = %SwapEditButton func _ready() -> void: - _button.visible = false + _text_edit.visible = false + _reset_button.visible = false func set_call_node(call_node: GraphNode) -> void: @@ -26,16 +34,18 @@ func set_call_node(call_node: GraphNode) -> void: func get_arg() -> String: - return _input.text + return _arg func set_arg(new_arg: String) -> void: - _input.text = new_arg - _button.visible = new_arg != (str(default_arg) if default_arg != null else '') + _arg = new_arg + _line_edit.text = new_arg + _text_edit.text = new_arg + _set_reset_button_visibility() func get_data() -> Dictionary: - return { 'name': arg_name, 'type': type, 'argument': _input.text, 'default': default_arg } + return { 'name': arg_name, 'type': type, 'argument': _arg, 'default': default_arg } func set_data(new_name: String, new_type: Variant.Type, argument: String, new_default) -> void: @@ -44,9 +54,9 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de arg_name = new_name # Set Type - _input.placeholder_text = ( - type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' - ) + var placeholder: String = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' + _line_edit.placeholder_text = placeholder + _text_edit.placeholder_text = placeholder type = new_type # Set Default Value @@ -59,27 +69,55 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de set_arg(str(new_default) if new_default != null else '') +func _set_reset_button_visibility() -> void: + print("I was here") + _reset_button.visible = _arg != (str(default_arg) if default_arg != null else '') + + func _validate_input_type() -> bool: return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING - or typeof(str_to_var(_input.text)) == type + or typeof(str_to_var(_arg)) == type ) -func _on_argument_input_text_changed(new_text: String) -> void: - _button.visible = new_text != (str(default_arg) if default_arg != null else '') +func _on_argument_line_edit_text_changed(new_text: String) -> void: + _arg = new_text + if !_line_edit.visible: + return + _text_edit.text = _arg + _set_reset_button_visibility() + + +func _on_argument_text_edit_text_changed() -> void: + _arg = _text_edit.text + if !_text_edit.visible: + return + _line_edit.text = _arg + _set_reset_button_visibility() func _on_reset_button_pressed() -> void: - _input.text = str(default_arg) if default_arg != null else '' - _on_argument_input_text_changed(_input.text) + _arg = str(default_arg) if default_arg != null else '' + _line_edit.text = _arg + _text_edit.text = _arg + _set_reset_button_visibility() + _call_node.reset_size.call_deferred() -func _on_argument_input_focus_exited() -> void: +func _on_any_argument_edit_focus_exited() -> void: if !_validate_input_type(): push_error( 'Argument <%s> with value <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' - % [arg_name, _input.text, _call_node.title, type_string(type)] + % [arg_name, _arg, _call_node.title, type_string(type)] ) _call_node.reset_size() + + +func _on_swap_edit_button_pressed() -> void: + _line_edit.visible = !_line_edit.visible + _text_edit.visible = !_text_edit.visible + + _swap_edit_button.icon = _line_edit_icon if _line_edit.visible else _text_edit_icon + _call_node.reset_size() From 6690f3b37488f9452abfb96f004a43ec92b53efe Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Fri, 11 Oct 2024 13:02:30 +0200 Subject: [PATCH 14/52] Added "Line/Text" functionality to CallNodeReturn --- .../nodes/sub_nodes/CallNodeArgument.tscn | 13 ++--- .../nodes/sub_nodes/CallNodeReturn.tscn | 38 +++++++++++++-- .../nodes/sub_nodes/call_node_return.gd | 48 +++++++++++++++---- 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index ac351a0..f19c732 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -27,7 +27,7 @@ expand_to_text_length = true [node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true visible = false -custom_minimum_size = Vector2(140, 0) +custom_minimum_size = Vector2(100, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 @@ -40,25 +40,26 @@ layout_mode = 2 size_flags_horizontal = 4 alignment = 1 -[node name="ResetButton" type="Button" parent="VFlowContainer"] +[node name="SwapEditButton" type="Button" parent="VFlowContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 -icon = ExtResource("2_h0t8n") +icon = ExtResource("3_eqbi1") flat = true -[node name="SwapEditButton" type="Button" parent="VFlowContainer"] +[node name="ResetButton" type="Button" parent="VFlowContainer"] unique_name_in_owner = true +visible = false layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 -icon = ExtResource("3_eqbi1") +icon = ExtResource("2_h0t8n") flat = true [connection signal="focus_exited" from="ArgumentLineEdit" to="." method="_on_any_argument_edit_focus_exited"] [connection signal="text_changed" from="ArgumentLineEdit" to="." method="_on_argument_line_edit_text_changed"] [connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_any_argument_edit_focus_exited"] [connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] -[connection signal="pressed" from="VFlowContainer/ResetButton" to="." method="_on_reset_button_pressed"] [connection signal="pressed" from="VFlowContainer/SwapEditButton" to="." method="_on_swap_edit_button_pressed"] +[connection signal="pressed" from="VFlowContainer/ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index a4d3d13..211eae6 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -1,14 +1,15 @@ -[gd_scene load_steps=3 format=3 uid="uid://djc2ggs14vbxc"] +[gd_scene load_steps=4 format=3 uid="uid://djc2ggs14vbxc"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd" id="1_lcs7a"] [ext_resource type="Texture2D" uid="uid://b8o81rn8q8a43" path="res://addons/dialogue_nodes/icons/Remove.svg" id="1_t7mvg"] +[ext_resource type="Texture2D" uid="uid://c1quecpfgc2t0" path="res://addons/dialogue_nodes/icons/LineEdit.svg" id="2_0a408"] [node name="CallNodeReturn" type="HBoxContainer"] size_flags_horizontal = 3 size_flags_vertical = 4 script = ExtResource("1_lcs7a") -[node name="ReturnInput" type="LineEdit" parent="."] +[node name="ReturnLineEdit" type="LineEdit" parent="."] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -16,12 +17,39 @@ size_flags_vertical = 4 placeholder_text = "Type" expand_to_text_length = true -[node name="RemoveButton" type="Button" parent="."] +[node name="ReturnTextEdit" type="TextEdit" parent="."] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Default" +placeholder_text = "Type" +scroll_fit_content_height = true + +[node name="VFlowContainer" type="VFlowContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 4 +alignment = 1 + +[node name="SwapEditButton" type="Button" parent="VFlowContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +icon = ExtResource("2_0a408") +flat = true + +[node name="RemoveButton" type="Button" parent="VFlowContainer"] layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 icon = ExtResource("1_t7mvg") flat = true -[connection signal="focus_exited" from="ReturnInput" to="." method="_on_return_input_focus_exited"] -[connection signal="pressed" from="RemoveButton" to="." method="_on_remove_button_pressed"] +[connection signal="focus_exited" from="ReturnLineEdit" to="." method="_on_return_input_focus_exited"] +[connection signal="text_changed" from="ReturnLineEdit" to="." method="_on_return_line_edit_text_changed"] +[connection signal="text_changed" from="ReturnTextEdit" to="." method="_on_return_text_edit_text_changed"] +[connection signal="pressed" from="VFlowContainer/SwapEditButton" to="." method="_on_swap_edit_button_pressed"] +[connection signal="pressed" from="VFlowContainer/RemoveButton" to="." method="_on_remove_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index f782e38..f0a180e 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -6,14 +6,21 @@ extends Container ## Represents a possible return of a CallNode's method. Used mainly to manage itself and to ## pass interactions to the CallNode that owns it. +@export var _line_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/LineEdit.svg') +@export var _text_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/TextEdit.svg') + signal text_changed(ret: Control, new_text: String) signal requested_removal(ret: Control) var type: Variant.Type = Variant.Type.TYPE_NIL var _call_node: GraphNode = null +var _ret: String = "" + +@onready var _line_edit: LineEdit = %ReturnLineEdit +@onready var _text_edit: TextEdit = %ReturnTextEdit -@onready var _input: LineEdit = %ReturnInput +@onready var _swap_edit_button: Button = %SwapEditButton func set_call_node(call_node: GraphNode) -> void: @@ -24,17 +31,20 @@ func set_call_node(call_node: GraphNode) -> void: func get_ret() -> String: - return _input.text + return _ret func set_ret(new_ret: String) -> void: - _input.text = new_ret + _ret = new_ret + _line_edit.text = new_ret + _text_edit.text = new_ret func set_type(new_type: Variant.Type) -> void: - _input.placeholder_text = ( - type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' - ) + var placeholder: String = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' + _line_edit.placeholder_text = placeholder + _text_edit.placeholder_text = placeholder + type = new_type @@ -42,10 +52,32 @@ func _validate_input_type() -> bool: return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING - or typeof(str_to_var(_input.text)) == type + or typeof(str_to_var(_ret)) == type ) +func _on_return_line_edit_text_changed(new_text: String) -> void: + _ret = new_text + if !_line_edit.visible: + return + _text_edit.text = _ret + + +func _on_return_text_edit_text_changed() -> void: + _ret = _text_edit.text + if !_text_edit.visible: + return + _line_edit.text = _ret + + +func _on_swap_edit_button_pressed() -> void: + _line_edit.visible = !_line_edit.visible + _text_edit.visible = !_text_edit.visible + + _swap_edit_button.icon = _line_edit_icon if _line_edit.visible else _text_edit_icon + _call_node.reset_size() + + func _on_remove_button_pressed() -> void: requested_removal.emit(self) @@ -54,6 +86,6 @@ func _on_return_input_focus_exited() -> void: if !_validate_input_type(): push_error( 'Return <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' - % [_input.text, _call_node.title, type_string(type)] + % [_ret, _call_node.title, type_string(type)] ) _call_node.reset_size() From 7c006bf1f9aba708b79c4787cd26e853f8d2e489 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Fri, 11 Oct 2024 22:20:19 +0200 Subject: [PATCH 15/52] Removed old print --- addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 7e8b10b..a891273 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -70,7 +70,6 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de func _set_reset_button_visibility() -> void: - print("I was here") _reset_button.visible = _arg != (str(default_arg) if default_arg != null else '') From 9d82159350b3e99944abf431ead601eeaf06b766 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 02:18:33 +0200 Subject: [PATCH 16/52] Included Undo/Redo for MethodSelection --- addons/dialogue_nodes/nodes/callNode.gd | 62 +++++++++++++------------ examples/ExampleCall.tres | 1 + 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 39b154d..911645d 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -18,13 +18,13 @@ signal connection_shift_request(from_node: String, old_port: int, new_port: int) var undo_redo: EditorUndoRedoManager +var base_color: Color = Color.WHITE + var _calls_script: Script = null var _calls: Dictionary = {} var _active_method: Dictionary = {} var _num_rets: int = 0 -var base_color: Color = Color.WHITE - var _arg_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn') var _ret_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn') @@ -100,7 +100,7 @@ func _from_dict(dict: Dictionary) -> Array[String]: # Import Method _reload_method_ui() if dict['method'].is_empty(): - _clear_method() + _set_method("") else: if _set_method(dict['method'].name) == true: for i: int in _method_button.item_count: @@ -108,7 +108,7 @@ func _from_dict(dict: Dictionary) -> Array[String]: _method_button.select(i) break else: - _method_button.selected = -1 + _method_button.select(-1) # Import Arguments if !_active_method.is_empty(): @@ -150,11 +150,6 @@ func _reload_library(script: Script) -> bool: push_error("Cannot reload CallNode's library with a NULL script!") return false - # Swap signal connections - if _calls_script != null and _calls_script.script_changed.is_connected(_on_calls_script_changed): - _calls_script.script_changed.disconnect(_on_calls_script_changed) - script.script_changed.connect(_on_calls_script_changed, ConnectFlags.CONNECT_DEFERRED) - # Re-write Calls Library with new methods _calls.clear() for method: Dictionary in script.get_script_method_list(): @@ -173,35 +168,33 @@ func _reload_method_ui() -> void: curr_method_name = _method_button.get_item_text(_method_button.selected) _method_button.clear() - _method_button.selected = -1 _method_button.add_item('', 0) - + _method_button.select(0) + var idx: int = 1 for method: Dictionary in _calls.values(): + method.index = idx _method_button.add_item(method.name, idx) if method.name == curr_method_name: - _method_button.selected = idx + _method_button.select(idx) idx += 1 func _set_method(method_name: String) -> bool: - if !_calls.has(method_name): - push_error("CallNode's selected method <%s> is not in calls library!" % method_name) - return false - _active_method = _calls[method_name] + if method_name.is_empty(): + _active_method = {} + else: + if !_calls.has(method_name): + push_error("CallNode's selected method <%s> is not in calls library!" % method_name) + return false + _active_method = _calls[method_name] + _reload_args_ui() _reload_rets_ui() reset_size() return true -func _clear_method() -> void: - _active_method = {} - _reload_args_ui() - _reload_rets_ui() - reset_size() - - # ------------------------------------------------------------------------------------------------- # Arguments # ------------------------------------------------------------------------------------------------- @@ -337,24 +330,33 @@ func clear_returns() -> void: # ------------------------------------------------------------------------------------------------- # Signals # ------------------------------------------------------------------------------------------------- +# TODO: Add method to "bind" a "changed file" signal from script we are loading methods from. func _on_calls_script_changed() -> void: pass - #_reload_library() + #_reload_library(_calls_script) #_reload_method_ui() #_reload_args_ui() func _on_method_selector_item_selected(index: int) -> void: if index == -1: - _clear_method() return - + + # If active method was reselected, do nothing. var method_name: String = _method_button.get_item_text(index) - if method_name.is_empty(): - _clear_method() + if _active_method.is_empty(): + if method_name.is_empty(): + return + elif _active_method.name == _method_button.get_item_text(index): return - - _set_method(method_name) + + # Else, Select new Method + undo_redo.create_action('Selected Method <%s>' % method_name if !method_name.is_empty() else 'Cleared Method') + undo_redo.add_do_method(_method_button, 'select', index) + undo_redo.add_do_method(self, '_set_method', method_name) + undo_redo.add_undo_method(_method_button, 'select', _active_method.index if !_active_method.is_empty() else 0) + undo_redo.add_undo_method(self, '_set_method', _active_method.name if !_active_method.is_empty() else '') + undo_redo.commit_action() func _on_add_return_button_pressed() -> void: diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres index 67d1529..5e4377e 100644 --- a/examples/ExampleCall.tres +++ b/examples/ExampleCall.tres @@ -130,6 +130,7 @@ with a default output if no return case matches.", "default_args": [], "flags": 33, "id": 0, +"index": 3, "name": "roll_dice", "return": { "class_name": &"", From 94f343fc5ddd58a5f43c72bf133a27719960b2dc Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 13:44:21 +0200 Subject: [PATCH 17/52] Resizable TextEdit for CallNode! --- .../nodes/sub_nodes/CallNodeArgument.tscn | 30 ++------- .../nodes/sub_nodes/call_node_argument.gd | 61 ++++++++----------- 2 files changed, 33 insertions(+), 58 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index f19c732..b847da6 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=4 format=3 uid="uid://bskh1vm7i45nw"] +[gd_scene load_steps=3 format=3 uid="uid://bskh1vm7i45nw"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd" id="1_mqq8d"] [ext_resource type="Texture2D" uid="uid://cf5ef41yo6jl6" path="res://addons/dialogue_nodes/icons/Reload.svg" id="2_h0t8n"] -[ext_resource type="Texture2D" uid="uid://c1quecpfgc2t0" path="res://addons/dialogue_nodes/icons/LineEdit.svg" id="3_eqbi1"] [node name="CallNodeArgument" type="HBoxContainer"] size_flags_horizontal = 3 @@ -15,19 +14,9 @@ layout_mode = 2 size_flags_horizontal = 0 text = "ArgName" -[node name="ArgumentLineEdit" type="LineEdit" parent="."] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -text = "Default" -placeholder_text = "Type" -expand_to_text_length = true - [node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -visible = false -custom_minimum_size = Vector2(100, 0) +custom_minimum_size = Vector2(60, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 @@ -40,14 +29,6 @@ layout_mode = 2 size_flags_horizontal = 4 alignment = 1 -[node name="SwapEditButton" type="Button" parent="VFlowContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 -icon = ExtResource("3_eqbi1") -flat = true - [node name="ResetButton" type="Button" parent="VFlowContainer"] unique_name_in_owner = true visible = false @@ -57,9 +38,10 @@ size_flags_vertical = 4 icon = ExtResource("2_h0t8n") flat = true -[connection signal="focus_exited" from="ArgumentLineEdit" to="." method="_on_any_argument_edit_focus_exited"] -[connection signal="text_changed" from="ArgumentLineEdit" to="." method="_on_argument_line_edit_text_changed"] +[node name="TextTimer" type="Timer" parent="."] +wait_time = 0.5 +one_shot = true + [connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_any_argument_edit_focus_exited"] [connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] -[connection signal="pressed" from="VFlowContainer/SwapEditButton" to="." method="_on_swap_edit_button_pressed"] [connection signal="pressed" from="VFlowContainer/ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index a891273..635c08c 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -6,8 +6,7 @@ extends Container ## Represents an argument in a CallNode's method. Used to store the argument's data, as well as ## react to user editing based on said data. -@export var _line_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/LineEdit.svg') -@export var _text_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/TextEdit.svg') +@export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 var arg_name: String = '' var type: Variant.Type = Variant.Type.TYPE_NIL @@ -17,15 +16,13 @@ var _call_node: GraphNode = null var _arg: String = "" @onready var _label: Label = %ArgumentLabel -@onready var _line_edit: LineEdit = %ArgumentLineEdit -@onready var _text_edit: TextEdit = %ArgumentTextEdit +@onready var _input: TextEdit = %ArgumentTextEdit @onready var _reset_button: Button = %ResetButton -@onready var _swap_edit_button: Button = %SwapEditButton func _ready() -> void: - _text_edit.visible = false + _resize_input_to_arg() _reset_button.visible = false @@ -39,8 +36,7 @@ func get_arg() -> String: func set_arg(new_arg: String) -> void: _arg = new_arg - _line_edit.text = new_arg - _text_edit.text = new_arg + _input.text = new_arg _set_reset_button_visibility() @@ -54,9 +50,7 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de arg_name = new_name # Set Type - var placeholder: String = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' - _line_edit.placeholder_text = placeholder - _text_edit.placeholder_text = placeholder + _input.placeholder_text = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' type = new_type # Set Default Value @@ -73,6 +67,25 @@ func _set_reset_button_visibility() -> void: _reset_button.visible = _arg != (str(default_arg) if default_arg != null else '') +func _resize_input_to_arg() -> void: + var font: Font = get_theme_default_font() + + var lines: PackedStringArray = [] + if _input.text.is_empty(): + lines = _input.placeholder_text.split("\n") + else: + for line_idx: int in _input.get_line_count(): + lines.push_back(_input.get_line(line_idx)) + + var max_width: int = -1 + for line: String in lines: + var str_size: int = font.get_string_size(line).x + if str_size > max_width: + max_width = str_size + + _input.custom_minimum_size.x = max_width + _font_size_margin + + func _validate_input_type() -> bool: return ( type == Variant.Type.TYPE_NIL @@ -81,26 +94,14 @@ func _validate_input_type() -> bool: ) -func _on_argument_line_edit_text_changed(new_text: String) -> void: - _arg = new_text - if !_line_edit.visible: - return - _text_edit.text = _arg - _set_reset_button_visibility() - - func _on_argument_text_edit_text_changed() -> void: - _arg = _text_edit.text - if !_text_edit.visible: - return - _line_edit.text = _arg + _arg = _input.text + _resize_input_to_arg() _set_reset_button_visibility() func _on_reset_button_pressed() -> void: - _arg = str(default_arg) if default_arg != null else '' - _line_edit.text = _arg - _text_edit.text = _arg + _input.text = _arg _set_reset_button_visibility() _call_node.reset_size.call_deferred() @@ -112,11 +113,3 @@ func _on_any_argument_edit_focus_exited() -> void: % [arg_name, _arg, _call_node.title, type_string(type)] ) _call_node.reset_size() - - -func _on_swap_edit_button_pressed() -> void: - _line_edit.visible = !_line_edit.visible - _text_edit.visible = !_text_edit.visible - - _swap_edit_button.icon = _line_edit_icon if _line_edit.visible else _text_edit_icon - _call_node.reset_size() From 923e477c1ba22847e6ef05e847f91e0961918db2 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 13:44:44 +0200 Subject: [PATCH 18/52] Tiny CallNode Argument tweak --- addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index b847da6..1017b46 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -16,7 +16,7 @@ text = "ArgName" [node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -custom_minimum_size = Vector2(60, 0) +custom_minimum_size = Vector2(100, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 From faadbcb7d9872e5955edabe33249a81075f89a7d Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 15:09:49 +0200 Subject: [PATCH 19/52] Re-orged Return vars --- addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index f0a180e..d0f33c0 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -6,12 +6,12 @@ extends Container ## Represents a possible return of a CallNode's method. Used mainly to manage itself and to ## pass interactions to the CallNode that owns it. -@export var _line_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/LineEdit.svg') -@export var _text_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/TextEdit.svg') - signal text_changed(ret: Control, new_text: String) signal requested_removal(ret: Control) +@export var _line_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/LineEdit.svg') +@export var _text_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/TextEdit.svg') + var type: Variant.Type = Variant.Type.TYPE_NIL var _call_node: GraphNode = null From 3dad61e0a84f6ffd52844c0353fb9c8227f0963a Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 15:13:38 +0200 Subject: [PATCH 20/52] Removed LineEdit from Argument/Return CallNode --- .../nodes/sub_nodes/CallNodeArgument.tscn | 4 +- .../nodes/sub_nodes/CallNodeReturn.tscn | 24 +----------- .../nodes/sub_nodes/call_node_argument.gd | 2 +- .../nodes/sub_nodes/call_node_return.gd | 39 +++---------------- 4 files changed, 11 insertions(+), 58 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index 1017b46..176fa9b 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -16,7 +16,7 @@ text = "ArgName" [node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -custom_minimum_size = Vector2(100, 0) +custom_minimum_size = Vector2(72, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 @@ -42,6 +42,6 @@ flat = true wait_time = 0.5 one_shot = true -[connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_any_argument_edit_focus_exited"] +[connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_focus_exited"] [connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] [connection signal="pressed" from="VFlowContainer/ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index 211eae6..93b5428 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -1,25 +1,15 @@ -[gd_scene load_steps=4 format=3 uid="uid://djc2ggs14vbxc"] +[gd_scene load_steps=3 format=3 uid="uid://djc2ggs14vbxc"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd" id="1_lcs7a"] [ext_resource type="Texture2D" uid="uid://b8o81rn8q8a43" path="res://addons/dialogue_nodes/icons/Remove.svg" id="1_t7mvg"] -[ext_resource type="Texture2D" uid="uid://c1quecpfgc2t0" path="res://addons/dialogue_nodes/icons/LineEdit.svg" id="2_0a408"] [node name="CallNodeReturn" type="HBoxContainer"] size_flags_horizontal = 3 size_flags_vertical = 4 script = ExtResource("1_lcs7a") -[node name="ReturnLineEdit" type="LineEdit" parent="."] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -placeholder_text = "Type" -expand_to_text_length = true - [node name="ReturnTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -visible = false custom_minimum_size = Vector2(100, 0) layout_mode = 2 size_flags_horizontal = 3 @@ -33,14 +23,6 @@ layout_mode = 2 size_flags_horizontal = 4 alignment = 1 -[node name="SwapEditButton" type="Button" parent="VFlowContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 -icon = ExtResource("2_0a408") -flat = true - [node name="RemoveButton" type="Button" parent="VFlowContainer"] layout_mode = 2 size_flags_horizontal = 4 @@ -48,8 +30,6 @@ size_flags_vertical = 4 icon = ExtResource("1_t7mvg") flat = true -[connection signal="focus_exited" from="ReturnLineEdit" to="." method="_on_return_input_focus_exited"] -[connection signal="text_changed" from="ReturnLineEdit" to="." method="_on_return_line_edit_text_changed"] +[connection signal="focus_exited" from="ReturnTextEdit" to="." method="_on_return_text_edit_focus_exited"] [connection signal="text_changed" from="ReturnTextEdit" to="." method="_on_return_text_edit_text_changed"] -[connection signal="pressed" from="VFlowContainer/SwapEditButton" to="." method="_on_swap_edit_button_pressed"] [connection signal="pressed" from="VFlowContainer/RemoveButton" to="." method="_on_remove_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 635c08c..aabd5ca 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -106,7 +106,7 @@ func _on_reset_button_pressed() -> void: _call_node.reset_size.call_deferred() -func _on_any_argument_edit_focus_exited() -> void: +func _on_argument_text_edit_focus_exited() -> void: if !_validate_input_type(): push_error( 'Argument <%s> with value <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index d0f33c0..2618aef 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -9,18 +9,12 @@ extends Container signal text_changed(ret: Control, new_text: String) signal requested_removal(ret: Control) -@export var _line_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/LineEdit.svg') -@export var _text_edit_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/TextEdit.svg') - var type: Variant.Type = Variant.Type.TYPE_NIL var _call_node: GraphNode = null var _ret: String = "" -@onready var _line_edit: LineEdit = %ReturnLineEdit -@onready var _text_edit: TextEdit = %ReturnTextEdit - -@onready var _swap_edit_button: Button = %SwapEditButton +@onready var _input: TextEdit = %ReturnTextEdit func set_call_node(call_node: GraphNode) -> void: @@ -36,15 +30,11 @@ func get_ret() -> String: func set_ret(new_ret: String) -> void: _ret = new_ret - _line_edit.text = new_ret - _text_edit.text = new_ret + _input.text = new_ret func set_type(new_type: Variant.Type) -> void: - var placeholder: String = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' - _line_edit.placeholder_text = placeholder - _text_edit.placeholder_text = placeholder - + _input.placeholder_text = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' type = new_type @@ -56,33 +46,16 @@ func _validate_input_type() -> bool: ) -func _on_return_line_edit_text_changed(new_text: String) -> void: - _ret = new_text - if !_line_edit.visible: - return - _text_edit.text = _ret - - func _on_return_text_edit_text_changed() -> void: - _ret = _text_edit.text - if !_text_edit.visible: - return - _line_edit.text = _ret - - -func _on_swap_edit_button_pressed() -> void: - _line_edit.visible = !_line_edit.visible - _text_edit.visible = !_text_edit.visible - - _swap_edit_button.icon = _line_edit_icon if _line_edit.visible else _text_edit_icon - _call_node.reset_size() + _ret = _input.text + _input.text = _ret func _on_remove_button_pressed() -> void: requested_removal.emit(self) -func _on_return_input_focus_exited() -> void: +func _on_return_text_edit_focus_exited() -> void: if !_validate_input_type(): push_error( 'Return <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' From 54af88be1484417d0a4610c890e5998d5d93d638 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 15:13:47 +0200 Subject: [PATCH 21/52] Removed LineEdit and TextEdit icons --- addons/dialogue_nodes/icons/LineEdit.svg | 1 - .../dialogue_nodes/icons/LineEdit.svg.import | 37 ------------------- addons/dialogue_nodes/icons/TextEdit.svg | 1 - .../dialogue_nodes/icons/TextEdit.svg.import | 37 ------------------- 4 files changed, 76 deletions(-) delete mode 100644 addons/dialogue_nodes/icons/LineEdit.svg delete mode 100644 addons/dialogue_nodes/icons/LineEdit.svg.import delete mode 100644 addons/dialogue_nodes/icons/TextEdit.svg delete mode 100644 addons/dialogue_nodes/icons/TextEdit.svg.import diff --git a/addons/dialogue_nodes/icons/LineEdit.svg b/addons/dialogue_nodes/icons/LineEdit.svg deleted file mode 100644 index a0e3fae..0000000 --- a/addons/dialogue_nodes/icons/LineEdit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/LineEdit.svg.import b/addons/dialogue_nodes/icons/LineEdit.svg.import deleted file mode 100644 index f4ef853..0000000 --- a/addons/dialogue_nodes/icons/LineEdit.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c1quecpfgc2t0" -path="res://.godot/imported/LineEdit.svg-1efa104133ff410e7a84270b63cde1a1.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/dialogue_nodes/icons/LineEdit.svg" -dest_files=["res://.godot/imported/LineEdit.svg-1efa104133ff410e7a84270b63cde1a1.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/addons/dialogue_nodes/icons/TextEdit.svg b/addons/dialogue_nodes/icons/TextEdit.svg deleted file mode 100644 index 93a9266..0000000 --- a/addons/dialogue_nodes/icons/TextEdit.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/TextEdit.svg.import b/addons/dialogue_nodes/icons/TextEdit.svg.import deleted file mode 100644 index c70fd03..0000000 --- a/addons/dialogue_nodes/icons/TextEdit.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c1infs2kx18p8" -path="res://.godot/imported/TextEdit.svg-ce951a4a570a9bd61fe362f3e8f2c77a.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/dialogue_nodes/icons/TextEdit.svg" -dest_files=["res://.godot/imported/TextEdit.svg-ce951a4a570a9bd61fe362f3e8f2c77a.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 From a901c2e0e1d1b4c6ebdf8a08ebf3f08326593284 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 15:23:35 +0200 Subject: [PATCH 22/52] Added TextEdit resizing to CallNode return --- .../nodes/sub_nodes/call_node_argument.gd | 16 +++++++------- .../nodes/sub_nodes/call_node_return.gd | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index aabd5ca..b44d055 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -67,6 +67,14 @@ func _set_reset_button_visibility() -> void: _reset_button.visible = _arg != (str(default_arg) if default_arg != null else '') +func _validate_input_type() -> bool: + return ( + type == Variant.Type.TYPE_NIL + or type == Variant.Type.TYPE_STRING + or typeof(str_to_var(_arg)) == type + ) + + func _resize_input_to_arg() -> void: var font: Font = get_theme_default_font() @@ -86,14 +94,6 @@ func _resize_input_to_arg() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin -func _validate_input_type() -> bool: - return ( - type == Variant.Type.TYPE_NIL - or type == Variant.Type.TYPE_STRING - or typeof(str_to_var(_arg)) == type - ) - - func _on_argument_text_edit_text_changed() -> void: _arg = _input.text _resize_input_to_arg() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 2618aef..85bcf89 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -6,6 +6,8 @@ extends Container ## Represents a possible return of a CallNode's method. Used mainly to manage itself and to ## pass interactions to the CallNode that owns it. +@export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 + signal text_changed(ret: Control, new_text: String) signal requested_removal(ret: Control) @@ -46,8 +48,28 @@ func _validate_input_type() -> bool: ) +func _resize_input_to_arg() -> void: + var font: Font = get_theme_default_font() + + var lines: PackedStringArray = [] + if _input.text.is_empty(): + lines = _input.placeholder_text.split("\n") + else: + for line_idx: int in _input.get_line_count(): + lines.push_back(_input.get_line(line_idx)) + + var max_width: int = -1 + for line: String in lines: + var str_size: int = font.get_string_size(line).x + if str_size > max_width: + max_width = str_size + + _input.custom_minimum_size.x = max_width + _font_size_margin + + func _on_return_text_edit_text_changed() -> void: _ret = _input.text + _resize_input_to_arg() _input.text = _ret From c6d2a47981371927117a5887609e3ebc98757e6d Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 15:31:08 +0200 Subject: [PATCH 23/52] Removed FlowContainer from Argument and Return (CallNode) --- .../dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn | 9 ++------- .../dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index 176fa9b..95339a8 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -24,12 +24,7 @@ text = "Default" placeholder_text = "Type" scroll_fit_content_height = true -[node name="VFlowContainer" type="VFlowContainer" parent="."] -layout_mode = 2 -size_flags_horizontal = 4 -alignment = 1 - -[node name="ResetButton" type="Button" parent="VFlowContainer"] +[node name="ResetButton" type="Button" parent="."] unique_name_in_owner = true visible = false layout_mode = 2 @@ -44,4 +39,4 @@ one_shot = true [connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_focus_exited"] [connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] -[connection signal="pressed" from="VFlowContainer/ResetButton" to="." method="_on_reset_button_pressed"] +[connection signal="pressed" from="ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index 93b5428..91e97f6 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -18,12 +18,7 @@ text = "Default" placeholder_text = "Type" scroll_fit_content_height = true -[node name="VFlowContainer" type="VFlowContainer" parent="."] -layout_mode = 2 -size_flags_horizontal = 4 -alignment = 1 - -[node name="RemoveButton" type="Button" parent="VFlowContainer"] +[node name="RemoveButton" type="Button" parent="."] layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 @@ -32,4 +27,4 @@ flat = true [connection signal="focus_exited" from="ReturnTextEdit" to="." method="_on_return_text_edit_focus_exited"] [connection signal="text_changed" from="ReturnTextEdit" to="." method="_on_return_text_edit_text_changed"] -[connection signal="pressed" from="VFlowContainer/RemoveButton" to="." method="_on_remove_button_pressed"] +[connection signal="pressed" from="RemoveButton" to="." method="_on_remove_button_pressed"] From f7f80c14889ec24e83789fb53179aea9ce2ebe0f Mon Sep 17 00:00:00 2001 From: nagidev Date: Sat, 12 Oct 2024 20:22:27 +0530 Subject: [PATCH 24/52] added GraphFrame --- addons/dialogue_nodes/editor/Graph.tscn | 11 +- addons/dialogue_nodes/editor/graph.gd | 103 +++++++++++++++--- addons/dialogue_nodes/icons/ExternalLink.svg | 1 + .../icons/ExternalLink.svg.import | 37 +++++++ addons/dialogue_nodes/nodes/GraphFrame.tscn | 15 +++ addons/dialogue_nodes/nodes/graphFrame.gd | 38 +++++++ addons/dialogue_nodes/nodes/startNode.gd | 2 +- project.godot | 5 + 8 files changed, 194 insertions(+), 18 deletions(-) create mode 100644 addons/dialogue_nodes/icons/ExternalLink.svg create mode 100644 addons/dialogue_nodes/icons/ExternalLink.svg.import create mode 100644 addons/dialogue_nodes/nodes/GraphFrame.tscn create mode 100644 addons/dialogue_nodes/nodes/graphFrame.gd diff --git a/addons/dialogue_nodes/editor/Graph.tscn b/addons/dialogue_nodes/editor/Graph.tscn index 1fa8310..dd0eba9 100644 --- a/addons/dialogue_nodes/editor/Graph.tscn +++ b/addons/dialogue_nodes/editor/Graph.tscn @@ -11,15 +11,13 @@ grow_horizontal = 2 grow_vertical = 2 size_flags_vertical = 3 right_disconnects = true -connection_lines_thickness = 4.0 zoom_min = 0.5 zoom_max = 2.0 script = ExtResource("1_7isyk") [node name="PopupMenu" type="PopupMenu" parent="."] -item_count = 6 +item_count = 9 item_0/text = "StartNode" -item_0/id = 0 item_1/text = "DialogueNode" item_1/id = 1 item_2/text = "CommentNode" @@ -30,12 +28,19 @@ item_4/text = "SetNode" item_4/id = 4 item_5/text = "ConditionNode" item_5/id = 5 +item_6/text = "NestNode" +item_6/id = 6 +item_7/text = "ForkNode" +item_7/id = 7 +item_8/text = "GraphFrame" +item_8/id = 8 [connection signal="connection_request" from="." to="." method="_on_connection_request"] [connection signal="connection_to_empty" from="." to="." method="_on_connection_to_empty"] [connection signal="delete_nodes_request" from="." to="." method="_on_delete_nodes_request"] [connection signal="disconnection_request" from="." to="." method="_on_disconnection_request"] [connection signal="duplicate_nodes_request" from="." to="." method="_on_duplicate_nodes_request"] +[connection signal="graph_elements_linked_to_frame_request" from="." to="." method="_on_graph_elements_linked_to_frame_request"] [connection signal="node_deselected" from="." to="." method="_on_node_deselected"] [connection signal="node_selected" from="." to="." method="_on_node_selected"] [connection signal="popup_request" from="." to="." method="show_add_menu"] diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 6206e82..d4510bf 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -14,8 +14,10 @@ signal run_requested(start_node_idx: int) preload('res://addons/dialogue_nodes/nodes/SetNode.tscn'), preload('res://addons/dialogue_nodes/nodes/ConditionNode.tscn'), preload('res://addons/dialogue_nodes/nodes/NestNode.tscn'), - preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn') + preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn'), + preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn') ] +@export var detach_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/ExternalLink.svg') @onready var popup_menu := $PopupMenu @@ -23,6 +25,7 @@ const _duplicate_offset := Vector2(20, 20) var undo_redo: EditorUndoRedoManager var starts: Array[String] = [] +var frames: Array[StringName] = [] var cursor_pos := Vector2.ZERO var selected_nodes := [] var request_node := '' @@ -57,7 +60,7 @@ func get_data() -> DialogueData: # get stray nodes data.strays.clear() for node in get_children(): - if node is GraphNode and not data.nodes.has(node.name): + if node is GraphElement and not data.nodes.has(node.name): data.strays.append(node.name) data.nodes[node.name] = node._to_dict(self) data.nodes[node.name]['offset'] = node.position_offset @@ -69,7 +72,7 @@ func load_data(data: DialogueData) -> void: # clear graph clear_connections() for node in get_children(): - if node is GraphNode: + if node is GraphElement: node.queue_free() request_node = '' request_port = -1 @@ -90,6 +93,11 @@ func load_data(data: DialogueData) -> void: request_port = -1 update_slots_color() + + # call after loading hooks for nodes + for node in get_children(): + if node.has_method('_after_loaded'): + node._after_loaded(self) func init_add_menu(add_menu: PopupMenu) -> void: @@ -104,7 +112,7 @@ func init_add_menu(add_menu: PopupMenu) -> void: add_menu.add_item(scene_name, i) -func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphNode: +func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphElement: deselect_all_nodes() # create new node @@ -135,11 +143,13 @@ func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphNode: new_node.set_ID('START' + new_node.name.split('_')[1]) 1: # dialogue node new_node._on_characters_updated(last_character_list) + 8: # graph frame + add_to_frames(new_node.name) return new_node -func connect_node_signals(node: GraphNode) -> void: +func connect_node_signals(node: GraphElement) -> void: var id := int(node.name.split('_')[0]) node.dragged.connect(_on_node_dragged.bind(node)) @@ -157,7 +167,7 @@ func connect_node_signals(node: GraphNode) -> void: node.connection_shift_request.connect(_on_connection_shift_request) -func disconnect_node_signals(node: GraphNode) -> void: +func disconnect_node_signals(node: GraphElement) -> void: var id := int(node.name.split('_')[0]) node.dragged.disconnect(_on_node_dragged.bind(node)) @@ -205,8 +215,16 @@ func add_to_starts(node_name: String) -> void: func remove_from_starts(node_name: String) -> void: - if starts.has(node_name): - starts.erase(node_name) + starts.erase(node_name) + + +func add_to_frames(node_name: StringName) -> void: + if not frames.has(node_name): + frames.append(node_name) + + +func remove_from_frames(node_name: StringName) -> void: + frames.erase(node_name) func update_slots_color(nodes: Array = get_children()) -> void: @@ -232,6 +250,35 @@ func update_slots_color(nodes: Array = get_children()) -> void: if 'base_color' in node: node.base_color = base_color +func attach_node_to_frame(element: StringName, frame: StringName) -> void: + attach_graph_element_to_frame(element, frame) + + var node: GraphNode = get_node(NodePath(element)) + var detach_button := Button.new() + detach_button.icon = detach_icon + detach_button.name = 'DetachButton' + detach_button.flat = true + node.get_titlebar_hbox().add_child(detach_button, true) + detach_button.pressed.connect( + _on_graph_elements_unlinked_to_frame_request.bind(element, frame) + ) + + var frame_node: GraphFrame = get_node(NodePath(frame)) + frame_node.attach_node(element) + + +func detach_node_from_frame(element: StringName, frame: StringName) -> void: + detach_graph_element_from_frame(element) + + var node: GraphNode = get_node(NodePath(element)) + var detach_button: Button = node.get_titlebar_hbox().get_node('DetachButton') + detach_button.pressed.disconnect(_on_graph_elements_unlinked_to_frame_request) + detach_button.queue_free() + + var frame_node: GraphFrame = get_node(NodePath(frame)) + frame_node.detach_node(element) + + func _on_add_menu_pressed(id: int) -> void: if not undo_redo: add_node(id) @@ -240,7 +287,7 @@ func _on_add_menu_pressed(id: int) -> void: _on_modified() var prev_connection := get_connections(request_node, request_port) - var new_node: GraphNode = add_node(id) + var new_node: GraphElement = add_node(id) undo_redo.create_action('Add graph node') undo_redo.add_do_method(self, 'add_child', new_node) @@ -270,17 +317,17 @@ func _on_add_menu_pressed(id: int) -> void: update_slots_color([new_node]) -func _on_node_selected(node: GraphNode) -> void: +func _on_node_selected(node: GraphElement) -> void: if not selected_nodes.has(node): selected_nodes.append(node) -func _on_node_deselected(node: GraphNode) -> void: +func _on_node_deselected(node: GraphElement) -> void: if selected_nodes.has(node): selected_nodes.erase(node) -func _on_node_dragged(from: Vector2, to: Vector2, node: GraphNode) -> void: +func _on_node_dragged(from: Vector2, to: Vector2, node: GraphElement) -> void: if not undo_redo: cursor_pos = to return @@ -305,7 +352,7 @@ func _on_duplicate_nodes_request() -> void: for node in nodes_to_duplicate: var clone_id := int(node.name.split('_')[0]) - var clone_node: GraphNode = add_node(clone_id) + var clone_node: GraphElement = add_node(clone_id) clone_node._from_dict(node._to_dict(self)) clone_node.position_offset = node.position_offset + _duplicate_offset if clone_id == 1: @@ -451,7 +498,7 @@ func _on_characters_updated(character_list: Array[Character]) -> void: characters_updated.emit(character_list) -func _on_run_requested(node: GraphNode) -> void: +func _on_run_requested(node: GraphElement) -> void: var idx := starts.find(node.name) if idx == -1: return @@ -460,3 +507,31 @@ func _on_run_requested(node: GraphNode) -> void: func _on_modified() -> void: modified.emit() + + +func _on_graph_elements_linked_to_frame_request(elements: Array, frame: StringName) -> void: + if not undo_redo: + for element_name: StringName in elements: + attach_node_to_frame(element_name, frame) + return + + undo_redo.create_action('Attach to frame') + for element_name: StringName in elements: + undo_redo.add_do_method(self, 'attach_node_to_frame', element_name, frame) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') + undo_redo.add_undo_method(self, 'detach_node_from_frame', element_name, frame) + undo_redo.commit_action() + + +func _on_graph_elements_unlinked_to_frame_request(element: StringName, frame: StringName) -> void: + if not undo_redo: + detach_node_from_frame(element, frame) + return + + undo_redo.create_action('Attach to frame') + undo_redo.add_do_method(self, 'detach_node_from_frame', element, frame) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') + undo_redo.add_undo_method(self, 'attach_node_to_frame', element, frame) + undo_redo.commit_action() diff --git a/addons/dialogue_nodes/icons/ExternalLink.svg b/addons/dialogue_nodes/icons/ExternalLink.svg new file mode 100644 index 0000000..4a2b57f --- /dev/null +++ b/addons/dialogue_nodes/icons/ExternalLink.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/ExternalLink.svg.import b/addons/dialogue_nodes/icons/ExternalLink.svg.import new file mode 100644 index 0000000..8de387f --- /dev/null +++ b/addons/dialogue_nodes/icons/ExternalLink.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wi6brbfwuyyn" +path="res://.godot/imported/ExternalLink.svg-8ded53d3be4b860412554fa94f351a62.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_nodes/icons/ExternalLink.svg" +dest_files=["res://.godot/imported/ExternalLink.svg-8ded53d3be4b860412554fa94f351a62.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/addons/dialogue_nodes/nodes/GraphFrame.tscn b/addons/dialogue_nodes/nodes/GraphFrame.tscn new file mode 100644 index 0000000..4beaed1 --- /dev/null +++ b/addons/dialogue_nodes/nodes/GraphFrame.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://cpwycjf6645y8"] + +[ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/graphFrame.gd" id="1_coo11"] + +[node name="GraphFrame" type="GraphFrame"] +offset_right = 400.0 +offset_bottom = 200.0 +title = "Frame" +script = ExtResource("1_coo11") + +[node name="InstructionLabel" type="Label" parent="."] +layout_mode = 2 +theme_override_colors/font_color = Color(0.533333, 0.533333, 0.533333, 1) +text = "Drag and drop nodes here to attach them." +horizontal_alignment = 1 diff --git a/addons/dialogue_nodes/nodes/graphFrame.gd b/addons/dialogue_nodes/nodes/graphFrame.gd new file mode 100644 index 0000000..c5a32c8 --- /dev/null +++ b/addons/dialogue_nodes/nodes/graphFrame.gd @@ -0,0 +1,38 @@ +@tool +extends GraphFrame + + +signal modified + +@onready var instruction_label: Label = $InstructionLabel + +var undo_redo: EditorUndoRedoManager +var attached_nodes: Array[StringName] = [] + + +func _to_dict(graph: GraphEdit) -> Dictionary: + var dict := {} + dict['attached_nodes'] = attached_nodes + return dict + + +func _from_dict(dict: Dictionary) -> Array[String]: + attached_nodes = dict['attached_nodes'] + return [] + + +func _after_loaded(graph: GraphEdit) -> void: + for node in attached_nodes: + graph.attach_node_to_frame(node, name) + instruction_label.visible = attached_nodes.size() == 0 + + +func attach_node(element: StringName) -> void: + if attached_nodes.has(element): return + attached_nodes.append(element) + instruction_label.visible = attached_nodes.size() == 0 + + +func detach_node(element: StringName) -> void: + attached_nodes.erase(element) + instruction_label.visible = attached_nodes.size() == 0 diff --git a/addons/dialogue_nodes/nodes/startNode.gd b/addons/dialogue_nodes/nodes/startNode.gd index fcd5c0a..8389b68 100644 --- a/addons/dialogue_nodes/nodes/startNode.gd +++ b/addons/dialogue_nodes/nodes/startNode.gd @@ -75,7 +75,7 @@ func data_to_tree(graph: GraphEdit, data: DialogueData, node_name := name) -> vo elif not graph.has_node(NodePath(node_name)): var type := int(node_name.split('_')[0]) var offset: Vector2 = data.nodes[node_name]['offset'] - var node: GraphNode = graph.add_node(type, node_name, offset) + var node: GraphElement = graph.add_node(type, node_name, offset) next_nodes = node._from_dict(data.nodes[node_name]) elif graph.has_node(NodePath(node_name)) and graph.request_port > -1: graph.connect_node(graph.request_node, graph.request_port, node_name, 0) diff --git a/project.godot b/project.godot index b0e4712..51c420a 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,11 @@ run/main_scene="res://examples/Demo1.tscn" config/features=PackedStringArray("4.3") config/icon="res://icon.svg" +[editor] + +naming/scene_name_casing=1 +naming/script_name_casing=1 + [filesystem] import/blender/enabled=false From ed1108efb699cd6eaac8afc085106091831c3de0 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 18:23:44 +0200 Subject: [PATCH 25/52] Added UndoRedo for Arguments/Returns --- addons/dialogue_nodes/nodes/callNode.gd | 54 ++++++++++++++++++- .../nodes/sub_nodes/CallNodeArgument.tscn | 5 -- .../nodes/sub_nodes/CallNodeReturn.tscn | 1 - .../nodes/sub_nodes/call_node_argument.gd | 34 ++++++------ .../nodes/sub_nodes/call_node_return.gd | 26 ++++----- 5 files changed, 78 insertions(+), 42 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 911645d..b409d09 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -198,6 +198,20 @@ func _set_method(method_name: String) -> bool: # ------------------------------------------------------------------------------------------------- # Arguments # ------------------------------------------------------------------------------------------------- +func get_argument(arg_name: String) -> Control: + for arg: Node in _args_container.get_children(): + if arg.arg_name == arg_name: + return arg as Control + return null + + +func set_argument(arg_name: String, arg_value: String) -> bool: + var arg: Node = get_argument(arg_name) + if arg != null: + arg.set_arg(arg_value) + return arg != null + + func _reload_args_ui() -> void: # If no active method, clear all arguments if _active_method.is_empty(): @@ -240,6 +254,7 @@ func _reload_args_ui() -> void: func _add_argument(arg_data: Dictionary, arg: String = '', default_arg = null) -> Control: var new_arg_ui: Control = _arg_scene.instantiate() _args_container.add_child(new_arg_ui) + new_arg_ui.changed_value.connect(_on_changed_argument) new_arg_ui.set_call_node(self) new_arg_ui.set_data( arg_data.name, @@ -265,6 +280,19 @@ func _clear_arguments() -> void: # ------------------------------------------------------------------------------------------------- # Returns # ------------------------------------------------------------------------------------------------- +func get_return(idx: int) -> Control: + if idx < 0 or idx >= _num_rets: + return null + return get_child(_ret_idx_start + idx) as Control + + +func set_return(idx: int, ret_value: String) -> bool: + var ret: Control = get_return(idx) + if ret != null: + ret.set_ret(ret_value) + return ret != null + + func _reload_rets_ui() -> void: # Reset the type hint on all existing returns. for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): @@ -279,6 +307,7 @@ func _add_return() -> Control: move_child(new_ret, _ret_button.get_index()) _num_rets += 1 + new_ret.changed_value.connect(_on_changed_return) new_ret.requested_removal.connect(_on_return_requested_removal) new_ret.set_call_node(self) new_ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) @@ -328,7 +357,7 @@ func clear_returns() -> void: # ------------------------------------------------------------------------------------------------- -# Signals +# Signals: CallNode # ------------------------------------------------------------------------------------------------- # TODO: Add method to "bind" a "changed file" signal from script we are loading methods from. func _on_calls_script_changed() -> void: @@ -350,7 +379,7 @@ func _on_method_selector_item_selected(index: int) -> void: elif _active_method.name == _method_button.get_item_text(index): return - # Else, Select new Method + # Else, Select new Method. undo_redo.create_action('Selected Method <%s>' % method_name if !method_name.is_empty() else 'Cleared Method') undo_redo.add_do_method(_method_button, 'select', index) undo_redo.add_do_method(self, '_set_method', method_name) @@ -365,3 +394,24 @@ func _on_add_return_button_pressed() -> void: func _on_return_requested_removal(ret: Control) -> void: _remove_return(ret) + + +# ------------------------------------------------------------------------------------------------- +# Signals: Arguments +# ------------------------------------------------------------------------------------------------- +func _on_changed_argument(arg: Control, old: String, new: String) -> void: + undo_redo.create_action('Edited Argument <%s> in <%s>' % [arg.arg_name, title]) + undo_redo.add_do_method(self, 'set_argument', arg.arg_name, new) + undo_redo.add_undo_method(self, 'set_argument', arg.arg_name, old) + undo_redo.commit_action() + + +# ------------------------------------------------------------------------------------------------- +# Signals: Returns +# ------------------------------------------------------------------------------------------------- +func _on_changed_return(ret: Control, old: String, new: String) -> void: + var ret_idx: int = ret.get_index() - _ret_idx_start + undo_redo.create_action('Edited Return <%s> in <%s>' % [ret_idx, title]) + undo_redo.add_do_method(self, 'set_return', ret_idx, new) + undo_redo.add_undo_method(self, 'set_return', ret_idx, old) + undo_redo.commit_action() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index 95339a8..7faf9c5 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -33,10 +33,5 @@ size_flags_vertical = 4 icon = ExtResource("2_h0t8n") flat = true -[node name="TextTimer" type="Timer" parent="."] -wait_time = 0.5 -one_shot = true - [connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_focus_exited"] -[connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] [connection signal="pressed" from="ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index 91e97f6..95141d4 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -26,5 +26,4 @@ icon = ExtResource("1_t7mvg") flat = true [connection signal="focus_exited" from="ReturnTextEdit" to="." method="_on_return_text_edit_focus_exited"] -[connection signal="text_changed" from="ReturnTextEdit" to="." method="_on_return_text_edit_text_changed"] [connection signal="pressed" from="RemoveButton" to="." method="_on_remove_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index b44d055..9c27a4d 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -6,6 +6,8 @@ extends Container ## Represents an argument in a CallNode's method. Used to store the argument's data, as well as ## react to user editing based on said data. +signal changed_value(arg: Control, old: String, new: String) + @export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 var arg_name: String = '' @@ -13,7 +15,7 @@ var type: Variant.Type = Variant.Type.TYPE_NIL var default_arg = null var _call_node: GraphNode = null -var _arg: String = "" +var _arg: String = '' @onready var _label: Label = %ArgumentLabel @onready var _input: TextEdit = %ArgumentTextEdit @@ -37,7 +39,9 @@ func get_arg() -> String: func set_arg(new_arg: String) -> void: _arg = new_arg _input.text = new_arg + _resize_input_to_arg() _set_reset_button_visibility() + _call_node.reset_size.call_deferred() func get_data() -> Dictionary: @@ -63,18 +67,18 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de set_arg(str(new_default) if new_default != null else '') -func _set_reset_button_visibility() -> void: - _reset_button.visible = _arg != (str(default_arg) if default_arg != null else '') - - -func _validate_input_type() -> bool: +func _is_string_valid_type(str: String) -> bool: return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING - or typeof(str_to_var(_arg)) == type + or typeof(str_to_var(str)) == type ) +func _set_reset_button_visibility() -> void: + _reset_button.visible = _arg != (str(default_arg) if default_arg != null else '') + + func _resize_input_to_arg() -> void: var font: Font = get_theme_default_font() @@ -94,22 +98,14 @@ func _resize_input_to_arg() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin -func _on_argument_text_edit_text_changed() -> void: - _arg = _input.text - _resize_input_to_arg() - _set_reset_button_visibility() - - func _on_reset_button_pressed() -> void: - _input.text = _arg - _set_reset_button_visibility() - _call_node.reset_size.call_deferred() + changed_value.emit(self, _arg, var_to_str(default_arg) if default_arg != null else '') func _on_argument_text_edit_focus_exited() -> void: - if !_validate_input_type(): + if !_is_string_valid_type(_input.text): push_error( 'Argument <%s> with value <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' - % [arg_name, _arg, _call_node.title, type_string(type)] + % [arg_name, _input.text, _call_node.title, type_string(type)] ) - _call_node.reset_size() + changed_value.emit(self, _arg, _input.text) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 85bcf89..db640ab 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -6,15 +6,15 @@ extends Container ## Represents a possible return of a CallNode's method. Used mainly to manage itself and to ## pass interactions to the CallNode that owns it. -@export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 - -signal text_changed(ret: Control, new_text: String) +signal changed_value(arg: Control, old: String, new: String) signal requested_removal(ret: Control) +@export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 + var type: Variant.Type = Variant.Type.TYPE_NIL var _call_node: GraphNode = null -var _ret: String = "" +var _ret: String = '' @onready var _input: TextEdit = %ReturnTextEdit @@ -33,6 +33,8 @@ func get_ret() -> String: func set_ret(new_ret: String) -> void: _ret = new_ret _input.text = new_ret + _resize_input_to_arg() + _call_node.reset_size.call_deferred() func set_type(new_type: Variant.Type) -> void: @@ -40,11 +42,11 @@ func set_type(new_type: Variant.Type) -> void: type = new_type -func _validate_input_type() -> bool: +func _is_string_valid_type(str: String) -> bool: return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING - or typeof(str_to_var(_ret)) == type + or typeof(str_to_var(str)) == type ) @@ -67,20 +69,14 @@ func _resize_input_to_arg() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin -func _on_return_text_edit_text_changed() -> void: - _ret = _input.text - _resize_input_to_arg() - _input.text = _ret - - func _on_remove_button_pressed() -> void: requested_removal.emit(self) func _on_return_text_edit_focus_exited() -> void: - if !_validate_input_type(): + if !_is_string_valid_type(_input.text): push_error( 'Return <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' - % [_ret, _call_node.title, type_string(type)] + % [_input.text, _call_node.title, type_string(type)] ) - _call_node.reset_size() + changed_value.emit(self, _ret, _input.text) From bc9d0c3d0a11306660516990fd4370d5eedb0cd0 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 18:26:21 +0200 Subject: [PATCH 26/52] Arg/Ret start as empty text by default --- addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn | 1 - addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn | 1 - addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd | 1 + addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd | 4 ++++ 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index 7faf9c5..8d94d61 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -20,7 +20,6 @@ custom_minimum_size = Vector2(72, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 -text = "Default" placeholder_text = "Type" scroll_fit_content_height = true diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index 95141d4..d18e5d6 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -14,7 +14,6 @@ custom_minimum_size = Vector2(100, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 -text = "Default" placeholder_text = "Type" scroll_fit_content_height = true diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 9c27a4d..a6f102d 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -25,6 +25,7 @@ var _arg: String = '' func _ready() -> void: _resize_input_to_arg() + _input.text = '' _reset_button.visible = false diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index db640ab..347f2e4 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -19,6 +19,10 @@ var _ret: String = '' @onready var _input: TextEdit = %ReturnTextEdit +func _ready() -> void: + _input.text = '' + + func set_call_node(call_node: GraphNode) -> void: if call_node != get_parent_control(): push_error('A CallNodeReturn MUST be a direct child of the CallNode is bound to!') From 767994c9807f85cfef0669c6c0c111db06d948ea Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 19:17:46 +0200 Subject: [PATCH 27/52] Added Undo/Redo for Method Changing --- addons/dialogue_nodes/nodes/callNode.gd | 5 +++++ addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index b409d09..e0975ba 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -385,6 +385,11 @@ func _on_method_selector_item_selected(index: int) -> void: undo_redo.add_do_method(self, '_set_method', method_name) undo_redo.add_undo_method(_method_button, 'select', _active_method.index if !_active_method.is_empty() else 0) undo_redo.add_undo_method(self, '_set_method', _active_method.name if !_active_method.is_empty() else '') + + if !_active_method.is_empty(): + for arg_data: Dictionary in _active_method.args: + undo_redo.add_undo_method(self, 'set_argument', arg_data.name, get_argument(arg_data.name).get_arg()) + undo_redo.commit_action() diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index a6f102d..5a98f10 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -24,9 +24,9 @@ var _arg: String = '' func _ready() -> void: - _resize_input_to_arg() _input.text = '' _reset_button.visible = false + _resize_input_to_arg() func set_call_node(call_node: GraphNode) -> void: @@ -40,8 +40,8 @@ func get_arg() -> String: func set_arg(new_arg: String) -> void: _arg = new_arg _input.text = new_arg - _resize_input_to_arg() _set_reset_button_visibility() + _resize_input_to_arg() _call_node.reset_size.call_deferred() From 3cbe03aad6ff3ae98b8a227a2c7f8e77ec25ac81 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 19:27:55 +0200 Subject: [PATCH 28/52] Empty arguments are valid now. They will be translated to the default type of the data Type that is required for said argument. --- .../nodes/sub_nodes/call_node_argument.gd | 1 + .../nodes/sub_nodes/call_node_return.gd | 1 + addons/dialogue_nodes/objects/DialogueParser.gd | 11 ++++++----- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 5a98f10..042b925 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -72,6 +72,7 @@ func _is_string_valid_type(str: String) -> bool: return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING + or str.is_empty() or typeof(str_to_var(str)) == type ) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 347f2e4..eafe412 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -50,6 +50,7 @@ func _is_string_valid_type(str: String) -> bool: return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING + or str.is_empty() or typeof(str_to_var(str)) == type ) diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index b9891e5..f038766 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -245,11 +245,12 @@ func _process_call(dict: Dictionary): var args: Array = [] for idx: int in dict.args.size(): - args.push_back( - dict.args[idx] - if dict.method.args[idx].type == Variant.Type.TYPE_STRING - else str_to_var(dict.args[idx]) - ) + if dict.method.args[idx].type == Variant.Type.TYPE_STRING: # If String, save it as is. + args.push_back(dict.args[idx]) + elif !dict.args[idx].is_empty(): # If not String, parse it to Var. + args.push_back(str_to_var(dict.args[idx])) + else: # If not String, but empty, parse the default value for argument Type. + args.push_back(type_convert('', dict.method.args[idx].type)) var ret = (load(dict.library) as Script).callv(dict.method.name, args) for idx: int in dict.rets: From 24086a80362a86d2c17ca8685d887d556fa8e960 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 20:52:24 +0200 Subject: [PATCH 29/52] CallNode Argument & Return do not update on focus lost if text has not changed --- addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn | 2 +- addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd | 2 ++ addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index 8d94d61..90b4a69 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -16,7 +16,7 @@ text = "ArgName" [node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -custom_minimum_size = Vector2(72, 0) +custom_minimum_size = Vector2(52, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 042b925..dd6a87d 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -105,6 +105,8 @@ func _on_reset_button_pressed() -> void: func _on_argument_text_edit_focus_exited() -> void: + if _input.text == _arg: + return if !_is_string_valid_type(_input.text): push_error( 'Argument <%s> with value <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index eafe412..c02b3ed 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -79,6 +79,8 @@ func _on_remove_button_pressed() -> void: func _on_return_text_edit_focus_exited() -> void: + if _input.text == _ret: + return if !_is_string_valid_type(_input.text): push_error( 'Return <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' From 717b223b903ac928c1388d0eea09bac58ca7985e Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sat, 12 Oct 2024 20:52:42 +0200 Subject: [PATCH 30/52] Added Undo/Redo for CallNode Returns --- addons/dialogue_nodes/nodes/callNode.gd | 72 +++++++++++++++---------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index e0975ba..dac0b41 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -198,15 +198,15 @@ func _set_method(method_name: String) -> bool: # ------------------------------------------------------------------------------------------------- # Arguments # ------------------------------------------------------------------------------------------------- -func get_argument(arg_name: String) -> Control: +func _get_argument(arg_name: String) -> Control: for arg: Node in _args_container.get_children(): if arg.arg_name == arg_name: return arg as Control return null -func set_argument(arg_name: String, arg_value: String) -> bool: - var arg: Node = get_argument(arg_name) +func _set_argument(arg_name: String, arg_value: String) -> bool: + var arg: Node = _get_argument(arg_name) if arg != null: arg.set_arg(arg_value) return arg != null @@ -280,31 +280,31 @@ func _clear_arguments() -> void: # ------------------------------------------------------------------------------------------------- # Returns # ------------------------------------------------------------------------------------------------- -func get_return(idx: int) -> Control: +func _reload_rets_ui() -> void: + # Reset the type hint on all existing returns. + for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): + var ret: Node = get_child(idx) + if ret != null: + ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) + + +func _get_return(idx: int) -> Control: if idx < 0 or idx >= _num_rets: return null return get_child(_ret_idx_start + idx) as Control -func set_return(idx: int, ret_value: String) -> bool: - var ret: Control = get_return(idx) +func _set_return(idx: int, ret_value: String) -> bool: + var ret: Control = _get_return(idx) if ret != null: ret.set_ret(ret_value) return ret != null -func _reload_rets_ui() -> void: - # Reset the type hint on all existing returns. - for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): - var ret: Node = get_child(idx) - if ret != null: - ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) - - -func _add_return() -> Control: +func _add_return(idx: int = _num_rets) -> Control: var new_ret: Control = _ret_scene.instantiate() add_child(new_ret) - move_child(new_ret, _ret_button.get_index()) + move_child(new_ret, _ret_idx_start + idx) _num_rets += 1 new_ret.changed_value.connect(_on_changed_return) @@ -349,6 +349,12 @@ func _remove_return(ret: Control) -> Control: return ret +func _remove_return_at(idx: int) -> void: + var ret: Node = _get_return(idx) + if ret != null: + _remove_return.call_deferred(ret) # Needed deferred or Undo/Redo goes crazy. + + func clear_returns() -> void: for idx: int in range(_ret_idx_start, _ret_idx_start + _num_rets): var ret: Node = get_child(idx) @@ -388,35 +394,43 @@ func _on_method_selector_item_selected(index: int) -> void: if !_active_method.is_empty(): for arg_data: Dictionary in _active_method.args: - undo_redo.add_undo_method(self, 'set_argument', arg_data.name, get_argument(arg_data.name).get_arg()) + undo_redo.add_undo_method(self, '_set_argument', arg_data.name, _get_argument(arg_data.name).get_arg()) undo_redo.commit_action() -func _on_add_return_button_pressed() -> void: - _add_return() - - -func _on_return_requested_removal(ret: Control) -> void: - _remove_return(ret) - - # ------------------------------------------------------------------------------------------------- # Signals: Arguments # ------------------------------------------------------------------------------------------------- func _on_changed_argument(arg: Control, old: String, new: String) -> void: undo_redo.create_action('Edited Argument <%s> in <%s>' % [arg.arg_name, title]) - undo_redo.add_do_method(self, 'set_argument', arg.arg_name, new) - undo_redo.add_undo_method(self, 'set_argument', arg.arg_name, old) + undo_redo.add_do_method(self, '_set_argument', arg.arg_name, new) + undo_redo.add_undo_method(self, '_set_argument', arg.arg_name, old) undo_redo.commit_action() # ------------------------------------------------------------------------------------------------- # Signals: Returns # ------------------------------------------------------------------------------------------------- +func _on_add_return_button_pressed() -> void: + undo_redo.create_action('Added Return on <%s>' % title) + undo_redo.add_do_method(self, '_add_return', _num_rets) + undo_redo.add_undo_method(self, '_remove_return_at', _num_rets) + undo_redo.commit_action() + + +func _on_return_requested_removal(ret: Control) -> void: + var relative_idx: int = ret.get_index() - _ret_idx_start + undo_redo.create_action('Remove Return on <%s>' % title) + undo_redo.add_do_method(self, '_remove_return_at', relative_idx) + undo_redo.add_undo_method(self, '_add_return', relative_idx) + undo_redo.add_undo_method(self, '_set_return', relative_idx, ret.get_ret()) + undo_redo.commit_action() + + func _on_changed_return(ret: Control, old: String, new: String) -> void: var ret_idx: int = ret.get_index() - _ret_idx_start undo_redo.create_action('Edited Return <%s> in <%s>' % [ret_idx, title]) - undo_redo.add_do_method(self, 'set_return', ret_idx, new) - undo_redo.add_undo_method(self, 'set_return', ret_idx, old) + undo_redo.add_do_method(self, '_set_return', ret_idx, new) + undo_redo.add_undo_method(self, '_set_return', ret_idx, old) undo_redo.commit_action() From 53727ae3f23d189559b79585b638b50569dc0c38 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 01:59:24 +0200 Subject: [PATCH 31/52] Fixed connection UndoRedo in CallNode --- addons/dialogue_nodes/nodes/callNode.gd | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index dac0b41..57980ba 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -312,22 +312,21 @@ func _add_return(idx: int = _num_rets) -> Control: new_ret.set_call_node(self) new_ret.set_type(_active_method.return.type if !_active_method.is_empty() else Variant.Type.TYPE_NIL) + # Shift Default Return Connection + connection_shift_request.emit(name, _num_rets - 1, _num_rets) + # Shift Return Connections for i: int in range(_num_rets - 1, new_ret.get_index() - _ret_idx_start, -1): connection_shift_request.emit(name, i - 1, i) - # Shift Default Return Connection - set_slot(_ret_idx_start + _num_rets + 1, false, 0, base_color, true, 0, base_color) - connection_shift_request.emit(name, _num_rets - 1, _num_rets) - _update_slots() + return new_ret func _remove_return(ret: Control) -> Control: # Shift Return Connections var ret_idx: int = ret.get_index() - _ret_idx_start - disconnection_from_request.emit(name, ret_idx) for i: int in range(ret_idx, _num_rets - 1): connection_shift_request.emit(name, i + 1, i) @@ -421,6 +420,9 @@ func _on_add_return_button_pressed() -> void: func _on_return_requested_removal(ret: Control) -> void: var relative_idx: int = ret.get_index() - _ret_idx_start + + disconnection_from_request.emit(name, relative_idx) + undo_redo.create_action('Remove Return on <%s>' % title) undo_redo.add_do_method(self, '_remove_return_at', relative_idx) undo_redo.add_undo_method(self, '_add_return', relative_idx) From 571ac74d975ad3f1b1121923b8247e9fd54c0dcc Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 02:08:18 +0200 Subject: [PATCH 32/52] Merged GraphFrame into CallNode branch --- addons/dialogue_nodes/editor/Graph.tscn | 11 +- addons/dialogue_nodes/editor/graph.gd | 105 +++++++++++++++--- addons/dialogue_nodes/icons/ExternalLink.svg | 1 + .../icons/ExternalLink.svg.import | 37 ++++++ addons/dialogue_nodes/nodes/GraphFrame.tscn | 15 +++ addons/dialogue_nodes/nodes/graphFrame.gd | 38 +++++++ addons/dialogue_nodes/nodes/startNode.gd | 2 +- 7 files changed, 190 insertions(+), 19 deletions(-) create mode 100644 addons/dialogue_nodes/icons/ExternalLink.svg create mode 100644 addons/dialogue_nodes/icons/ExternalLink.svg.import create mode 100644 addons/dialogue_nodes/nodes/GraphFrame.tscn create mode 100644 addons/dialogue_nodes/nodes/graphFrame.gd diff --git a/addons/dialogue_nodes/editor/Graph.tscn b/addons/dialogue_nodes/editor/Graph.tscn index 1fa8310..dd0eba9 100644 --- a/addons/dialogue_nodes/editor/Graph.tscn +++ b/addons/dialogue_nodes/editor/Graph.tscn @@ -11,15 +11,13 @@ grow_horizontal = 2 grow_vertical = 2 size_flags_vertical = 3 right_disconnects = true -connection_lines_thickness = 4.0 zoom_min = 0.5 zoom_max = 2.0 script = ExtResource("1_7isyk") [node name="PopupMenu" type="PopupMenu" parent="."] -item_count = 6 +item_count = 9 item_0/text = "StartNode" -item_0/id = 0 item_1/text = "DialogueNode" item_1/id = 1 item_2/text = "CommentNode" @@ -30,12 +28,19 @@ item_4/text = "SetNode" item_4/id = 4 item_5/text = "ConditionNode" item_5/id = 5 +item_6/text = "NestNode" +item_6/id = 6 +item_7/text = "ForkNode" +item_7/id = 7 +item_8/text = "GraphFrame" +item_8/id = 8 [connection signal="connection_request" from="." to="." method="_on_connection_request"] [connection signal="connection_to_empty" from="." to="." method="_on_connection_to_empty"] [connection signal="delete_nodes_request" from="." to="." method="_on_delete_nodes_request"] [connection signal="disconnection_request" from="." to="." method="_on_disconnection_request"] [connection signal="duplicate_nodes_request" from="." to="." method="_on_duplicate_nodes_request"] +[connection signal="graph_elements_linked_to_frame_request" from="." to="." method="_on_graph_elements_linked_to_frame_request"] [connection signal="node_deselected" from="." to="." method="_on_node_deselected"] [connection signal="node_selected" from="." to="." method="_on_node_selected"] [connection signal="popup_request" from="." to="." method="show_add_menu"] diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 1c256fc..55abb6b 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -15,8 +15,10 @@ signal run_requested(start_node_idx: int) preload('res://addons/dialogue_nodes/nodes/ConditionNode.tscn'), preload('res://addons/dialogue_nodes/nodes/NestNode.tscn'), preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn'), + preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn'), preload('res://addons/dialogue_nodes/nodes/CallNode.tscn') ] +@export var detach_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/ExternalLink.svg') @onready var popup_menu := $PopupMenu @@ -24,6 +26,7 @@ const _duplicate_offset := Vector2(20, 20) var undo_redo: EditorUndoRedoManager var starts: Array[String] = [] +var frames: Array[StringName] = [] var cursor_pos := Vector2.ZERO var selected_nodes := [] var request_node := '' @@ -58,7 +61,7 @@ func get_data() -> DialogueData: # get stray nodes data.strays.clear() for node in get_children(): - if node is GraphNode and not data.nodes.has(node.name): + if node is GraphElement and not data.nodes.has(node.name): data.strays.append(node.name) data.nodes[node.name] = node._to_dict(self) data.nodes[node.name]['offset'] = node.position_offset @@ -70,7 +73,7 @@ func load_data(data: DialogueData) -> void: # clear graph clear_connections() for node in get_children(): - if node is GraphNode: + if node is GraphElement: node.queue_free() request_node = '' request_port = -1 @@ -91,6 +94,11 @@ func load_data(data: DialogueData) -> void: request_port = -1 update_slots_color() + + # call after loading hooks for nodes + for node in get_children(): + if node.has_method('_after_loaded'): + node._after_loaded(self) func init_add_menu(add_menu: PopupMenu) -> void: @@ -105,7 +113,7 @@ func init_add_menu(add_menu: PopupMenu) -> void: add_menu.add_item(scene_name, i) -func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphNode: +func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphElement: deselect_all_nodes() # create new node @@ -136,11 +144,13 @@ func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphNode: new_node.set_ID('START' + new_node.name.split('_')[1]) 1: # dialogue node new_node._on_characters_updated(last_character_list) + 8: # graph frame + add_to_frames(new_node.name) return new_node -func connect_node_signals(node: GraphNode) -> void: +func connect_node_signals(node: GraphElement) -> void: var id := int(node.name.split('_')[0]) node.dragged.connect(_on_node_dragged.bind(node)) @@ -153,12 +163,12 @@ func connect_node_signals(node: GraphNode) -> void: characters_updated.connect(node._on_characters_updated) node.disconnection_from_request.connect(_on_disconnection_from_request) node.connection_shift_request.connect(_on_connection_shift_request) - 7, 8: # fork node, call node + 7, 9: # fork node, call node node.disconnection_from_request.connect(_on_disconnection_from_request) node.connection_shift_request.connect(_on_connection_shift_request) -func disconnect_node_signals(node: GraphNode) -> void: +func disconnect_node_signals(node: GraphElement) -> void: var id := int(node.name.split('_')[0]) node.dragged.disconnect(_on_node_dragged.bind(node)) @@ -171,7 +181,7 @@ func disconnect_node_signals(node: GraphNode) -> void: characters_updated.disconnect(node._on_characters_updated) node.disconnection_from_request.disconnect(_on_disconnection_from_request) node.connection_shift_request.disconnect(_on_connection_shift_request) - 7, 8: # fork node, call node + 7, 9: # fork node, call node node.disconnection_from_request.disconnect(_on_disconnection_from_request) node.connection_shift_request.disconnect(_on_connection_shift_request) @@ -206,8 +216,16 @@ func add_to_starts(node_name: String) -> void: func remove_from_starts(node_name: String) -> void: - if starts.has(node_name): - starts.erase(node_name) + starts.erase(node_name) + + +func add_to_frames(node_name: StringName) -> void: + if not frames.has(node_name): + frames.append(node_name) + + +func remove_from_frames(node_name: StringName) -> void: + frames.erase(node_name) func update_slots_color(nodes: Array = get_children()) -> void: @@ -233,6 +251,35 @@ func update_slots_color(nodes: Array = get_children()) -> void: if 'base_color' in node: node.base_color = base_color +func attach_node_to_frame(element: StringName, frame: StringName) -> void: + attach_graph_element_to_frame(element, frame) + + var node: GraphNode = get_node(NodePath(element)) + var detach_button := Button.new() + detach_button.icon = detach_icon + detach_button.name = 'DetachButton' + detach_button.flat = true + node.get_titlebar_hbox().add_child(detach_button, true) + detach_button.pressed.connect( + _on_graph_elements_unlinked_to_frame_request.bind(element, frame) + ) + + var frame_node: GraphFrame = get_node(NodePath(frame)) + frame_node.attach_node(element) + + +func detach_node_from_frame(element: StringName, frame: StringName) -> void: + detach_graph_element_from_frame(element) + + var node: GraphNode = get_node(NodePath(element)) + var detach_button: Button = node.get_titlebar_hbox().get_node('DetachButton') + detach_button.pressed.disconnect(_on_graph_elements_unlinked_to_frame_request) + detach_button.queue_free() + + var frame_node: GraphFrame = get_node(NodePath(frame)) + frame_node.detach_node(element) + + func _on_add_menu_pressed(id: int) -> void: if not undo_redo: add_node(id) @@ -241,7 +288,7 @@ func _on_add_menu_pressed(id: int) -> void: _on_modified() var prev_connection := get_connections(request_node, request_port) - var new_node: GraphNode = add_node(id) + var new_node: GraphElement = add_node(id) undo_redo.create_action('Add graph node') undo_redo.add_do_method(self, 'add_child', new_node) @@ -271,17 +318,17 @@ func _on_add_menu_pressed(id: int) -> void: update_slots_color([new_node]) -func _on_node_selected(node: GraphNode) -> void: +func _on_node_selected(node: GraphElement) -> void: if not selected_nodes.has(node): selected_nodes.append(node) -func _on_node_deselected(node: GraphNode) -> void: +func _on_node_deselected(node: GraphElement) -> void: if selected_nodes.has(node): selected_nodes.erase(node) -func _on_node_dragged(from: Vector2, to: Vector2, node: GraphNode) -> void: +func _on_node_dragged(from: Vector2, to: Vector2, node: GraphElement) -> void: if not undo_redo: cursor_pos = to return @@ -306,7 +353,7 @@ func _on_duplicate_nodes_request() -> void: for node in nodes_to_duplicate: var clone_id := int(node.name.split('_')[0]) - var clone_node: GraphNode = add_node(clone_id) + var clone_node: GraphElement = add_node(clone_id) clone_node._from_dict(node._to_dict(self)) clone_node.position_offset = node.position_offset + _duplicate_offset if clone_id == 1: @@ -452,7 +499,7 @@ func _on_characters_updated(character_list: Array[Character]) -> void: characters_updated.emit(character_list) -func _on_run_requested(node: GraphNode) -> void: +func _on_run_requested(node: GraphElement) -> void: var idx := starts.find(node.name) if idx == -1: return @@ -461,3 +508,31 @@ func _on_run_requested(node: GraphNode) -> void: func _on_modified() -> void: modified.emit() + + +func _on_graph_elements_linked_to_frame_request(elements: Array, frame: StringName) -> void: + if not undo_redo: + for element_name: StringName in elements: + attach_node_to_frame(element_name, frame) + return + + undo_redo.create_action('Attach to frame') + for element_name: StringName in elements: + undo_redo.add_do_method(self, 'attach_node_to_frame', element_name, frame) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') + undo_redo.add_undo_method(self, 'detach_node_from_frame', element_name, frame) + undo_redo.commit_action() + + +func _on_graph_elements_unlinked_to_frame_request(element: StringName, frame: StringName) -> void: + if not undo_redo: + detach_node_from_frame(element, frame) + return + + undo_redo.create_action('Attach to frame') + undo_redo.add_do_method(self, 'detach_node_from_frame', element, frame) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') + undo_redo.add_undo_method(self, 'attach_node_to_frame', element, frame) + undo_redo.commit_action() \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/ExternalLink.svg b/addons/dialogue_nodes/icons/ExternalLink.svg new file mode 100644 index 0000000..4a2b57f --- /dev/null +++ b/addons/dialogue_nodes/icons/ExternalLink.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/ExternalLink.svg.import b/addons/dialogue_nodes/icons/ExternalLink.svg.import new file mode 100644 index 0000000..8de387f --- /dev/null +++ b/addons/dialogue_nodes/icons/ExternalLink.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wi6brbfwuyyn" +path="res://.godot/imported/ExternalLink.svg-8ded53d3be4b860412554fa94f351a62.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_nodes/icons/ExternalLink.svg" +dest_files=["res://.godot/imported/ExternalLink.svg-8ded53d3be4b860412554fa94f351a62.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/addons/dialogue_nodes/nodes/GraphFrame.tscn b/addons/dialogue_nodes/nodes/GraphFrame.tscn new file mode 100644 index 0000000..4beaed1 --- /dev/null +++ b/addons/dialogue_nodes/nodes/GraphFrame.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://cpwycjf6645y8"] + +[ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/graphFrame.gd" id="1_coo11"] + +[node name="GraphFrame" type="GraphFrame"] +offset_right = 400.0 +offset_bottom = 200.0 +title = "Frame" +script = ExtResource("1_coo11") + +[node name="InstructionLabel" type="Label" parent="."] +layout_mode = 2 +theme_override_colors/font_color = Color(0.533333, 0.533333, 0.533333, 1) +text = "Drag and drop nodes here to attach them." +horizontal_alignment = 1 diff --git a/addons/dialogue_nodes/nodes/graphFrame.gd b/addons/dialogue_nodes/nodes/graphFrame.gd new file mode 100644 index 0000000..c5a32c8 --- /dev/null +++ b/addons/dialogue_nodes/nodes/graphFrame.gd @@ -0,0 +1,38 @@ +@tool +extends GraphFrame + + +signal modified + +@onready var instruction_label: Label = $InstructionLabel + +var undo_redo: EditorUndoRedoManager +var attached_nodes: Array[StringName] = [] + + +func _to_dict(graph: GraphEdit) -> Dictionary: + var dict := {} + dict['attached_nodes'] = attached_nodes + return dict + + +func _from_dict(dict: Dictionary) -> Array[String]: + attached_nodes = dict['attached_nodes'] + return [] + + +func _after_loaded(graph: GraphEdit) -> void: + for node in attached_nodes: + graph.attach_node_to_frame(node, name) + instruction_label.visible = attached_nodes.size() == 0 + + +func attach_node(element: StringName) -> void: + if attached_nodes.has(element): return + attached_nodes.append(element) + instruction_label.visible = attached_nodes.size() == 0 + + +func detach_node(element: StringName) -> void: + attached_nodes.erase(element) + instruction_label.visible = attached_nodes.size() == 0 diff --git a/addons/dialogue_nodes/nodes/startNode.gd b/addons/dialogue_nodes/nodes/startNode.gd index fcd5c0a..8389b68 100644 --- a/addons/dialogue_nodes/nodes/startNode.gd +++ b/addons/dialogue_nodes/nodes/startNode.gd @@ -75,7 +75,7 @@ func data_to_tree(graph: GraphEdit, data: DialogueData, node_name := name) -> vo elif not graph.has_node(NodePath(node_name)): var type := int(node_name.split('_')[0]) var offset: Vector2 = data.nodes[node_name]['offset'] - var node: GraphNode = graph.add_node(type, node_name, offset) + var node: GraphElement = graph.add_node(type, node_name, offset) next_nodes = node._from_dict(data.nodes[node_name]) elif graph.has_node(NodePath(node_name)) and graph.request_port > -1: graph.connect_node(graph.request_node, graph.request_port, node_name, 0) From bf7ebac81613d3b2d9a40af89748a8fdb7655347 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 02:09:13 +0200 Subject: [PATCH 33/52] Missing .project edit from last merge --- project.godot | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/project.godot b/project.godot index b0e4712..51c420a 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,11 @@ run/main_scene="res://examples/Demo1.tscn" config/features=PackedStringArray("4.3") config/icon="res://icon.svg" +[editor] + +naming/scene_name_casing=1 +naming/script_name_casing=1 + [filesystem] import/blender/enabled=false From 884b588e020c3527344e18dd85560981aeab4dfa Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 02:10:16 +0200 Subject: [PATCH 34/52] Retrying merge from Main to CallNode --- addons/dialogue_nodes/editor/graph.gd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 55abb6b..34072b6 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -17,6 +17,7 @@ signal run_requested(start_node_idx: int) preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn'), preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn'), preload('res://addons/dialogue_nodes/nodes/CallNode.tscn') + preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn') ] @export var detach_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/ExternalLink.svg') @@ -535,4 +536,5 @@ func _on_graph_elements_unlinked_to_frame_request(element: StringName, frame: St undo_redo.add_do_method(self, '_on_modified') undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, 'attach_node_to_frame', element, frame) + undo_redo.commit_action() undo_redo.commit_action() \ No newline at end of file From c30ffad812a31db309e1ea6a89d43b591fb7c5f5 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 02:11:53 +0200 Subject: [PATCH 35/52] Still trying with the merge... --- addons/dialogue_nodes/editor/graph.gd | 3 --- 1 file changed, 3 deletions(-) diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 34072b6..29bce03 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -15,8 +15,6 @@ signal run_requested(start_node_idx: int) preload('res://addons/dialogue_nodes/nodes/ConditionNode.tscn'), preload('res://addons/dialogue_nodes/nodes/NestNode.tscn'), preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn'), - preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn'), - preload('res://addons/dialogue_nodes/nodes/CallNode.tscn') preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn') ] @export var detach_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/ExternalLink.svg') @@ -536,5 +534,4 @@ func _on_graph_elements_unlinked_to_frame_request(element: StringName, frame: St undo_redo.add_do_method(self, '_on_modified') undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, 'attach_node_to_frame', element, frame) - undo_redo.commit_action() undo_redo.commit_action() \ No newline at end of file From 704f5630e9ef8433fc7e22ffa7c707b79ac1d31b Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 02:20:50 +0200 Subject: [PATCH 36/52] Readded CallNode to graph.gd --- addons/dialogue_nodes/editor/graph.gd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 29bce03..78284d9 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -15,7 +15,8 @@ signal run_requested(start_node_idx: int) preload('res://addons/dialogue_nodes/nodes/ConditionNode.tscn'), preload('res://addons/dialogue_nodes/nodes/NestNode.tscn'), preload('res://addons/dialogue_nodes/nodes/ForkNode.tscn'), - preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn') + preload('res://addons/dialogue_nodes/nodes/GraphFrame.tscn'), + preload('res://addons/dialogue_nodes/nodes/CallNode.tscn') ] @export var detach_icon: Texture2D = preload('res://addons/dialogue_nodes/icons/ExternalLink.svg') @@ -534,4 +535,4 @@ func _on_graph_elements_unlinked_to_frame_request(element: StringName, frame: St undo_redo.add_do_method(self, '_on_modified') undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, 'attach_node_to_frame', element, frame) - undo_redo.commit_action() \ No newline at end of file + undo_redo.commit_action() From 19efee9f6180a3d39296deb3fe65bc06c01d932f Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 02:38:26 +0200 Subject: [PATCH 37/52] Fixed ExampleCall save file --- examples/ExampleCall.tres | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres index 5e4377e..5ff79eb 100644 --- a/examples/ExampleCall.tres +++ b/examples/ExampleCall.tres @@ -9,7 +9,7 @@ starts = { } nodes = { "0_1": { -"link": &"8_1", +"link": &"9_1", "offset": Vector2(-300, -220), "start_id": "START" }, @@ -114,7 +114,7 @@ with a default output if no return case matches.", "offset": Vector2(-500, -60), "size": Vector2(380, 200) }, -"8_1": { +"9_1": { "args": ["{ \"d6\": {\"faces\": 7, \"ignore\": [1,3,5]} }"], "default": &"1_7", "library": "res://addons/dialogue_nodes/editor/calls.gd", From 5cc7bd208cb1451fcb1615c39860c6a96fc9d75d Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 15:51:05 +0200 Subject: [PATCH 38/52] CallNode Arguments and Returns update live --- .../dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn | 2 ++ addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn | 1 + .../dialogue_nodes/nodes/sub_nodes/call_node_argument.gd | 4 ++++ addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd | 8 ++++++-- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index 90b4a69..b77c600 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -7,6 +7,7 @@ size_flags_horizontal = 3 size_flags_vertical = 4 script = ExtResource("1_mqq8d") +_font_size_margin = 0.0 [node name="ArgumentLabel" type="Label" parent="."] unique_name_in_owner = true @@ -33,4 +34,5 @@ icon = ExtResource("2_h0t8n") flat = true [connection signal="focus_exited" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_focus_exited"] +[connection signal="text_changed" from="ArgumentTextEdit" to="." method="_on_argument_text_edit_text_changed"] [connection signal="pressed" from="ResetButton" to="." method="_on_reset_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index d18e5d6..f510323 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -25,4 +25,5 @@ icon = ExtResource("1_t7mvg") flat = true [connection signal="focus_exited" from="ReturnTextEdit" to="." method="_on_return_text_edit_focus_exited"] +[connection signal="text_changed" from="ReturnTextEdit" to="." method="_on_return_text_edit_text_changed"] [connection signal="pressed" from="RemoveButton" to="." method="_on_remove_button_pressed"] diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index dd6a87d..da19fb0 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -100,6 +100,10 @@ func _resize_input_to_arg() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin +func _on_argument_text_edit_text_changed() -> void: + _resize_input_to_arg() + + func _on_reset_button_pressed() -> void: changed_value.emit(self, _arg, var_to_str(default_arg) if default_arg != null else '') diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index c02b3ed..4440e77 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -37,7 +37,7 @@ func get_ret() -> String: func set_ret(new_ret: String) -> void: _ret = new_ret _input.text = new_ret - _resize_input_to_arg() + _resize_input_to_ret() _call_node.reset_size.call_deferred() @@ -55,7 +55,7 @@ func _is_string_valid_type(str: String) -> bool: ) -func _resize_input_to_arg() -> void: +func _resize_input_to_ret() -> void: var font: Font = get_theme_default_font() var lines: PackedStringArray = [] @@ -74,6 +74,10 @@ func _resize_input_to_arg() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin +func _on_return_text_edit_text_changed() -> void: + _resize_input_to_ret() + + func _on_remove_button_pressed() -> void: requested_removal.emit(self) From 16a9e0b0ca01b43d3025b2eda86ce6ee3925dee6 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Sun, 13 Oct 2024 15:51:32 +0200 Subject: [PATCH 39/52] Added variable checking to CallNode Parsing --- .../dialogue_nodes/objects/DialogueParser.gd | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index f038766..10f5f4c 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -126,6 +126,7 @@ func _proceed(node_name: String) -> void: _process_condition, _process_nest, _process_fork, + func(): pass, # frame _process_call ] @@ -242,23 +243,27 @@ func _process_call(dict: Dictionary): if dict.method.is_empty(): _proceed(dict.default) return - + var args: Array = [] for idx: int in dict.args.size(): + var arg: String = _parse_variables(dict.args[idx]) if dict.args[idx].count('{{') > 0 else dict.args[idx] if dict.method.args[idx].type == Variant.Type.TYPE_STRING: # If String, save it as is. - args.push_back(dict.args[idx]) - elif !dict.args[idx].is_empty(): # If not String, parse it to Var. - args.push_back(str_to_var(dict.args[idx])) + args.push_back(arg) + elif !arg.is_empty(): # If not String, parse it to Var. + args.push_back(str_to_var(arg)) else: # If not String, but empty, parse the default value for argument Type. args.push_back(type_convert('', dict.method.args[idx].type)) - + var ret = (load(dict.library) as Script).callv(dict.method.name, args) for idx: int in dict.rets: - var ret_option = ( + var ret_option = _parse_variables( dict.rets[idx].value - if dict.method.return.type == Variant.Type.TYPE_STRING - else str_to_var(dict.rets[idx].value) + if dict.rets[idx].value.count('{{') > 0 + else dict.rets[idx].value ) + if dict.method.return.type != Variant.Type.TYPE_STRING: + ret_option = str_to_var(dict.rets[idx].value) + if typeof(ret_option) == typeof(ret) and ret_option == ret: _proceed(dict.rets[idx].link) return From 6632364ee54533bab666afc8be9f5ae059307092 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 01:16:33 +0200 Subject: [PATCH 40/52] Added Variable checking placeholder code on CallNode --- .../nodes/sub_nodes/call_node_argument.gd | 22 ++++++++++++++----- .../nodes/sub_nodes/call_node_return.gd | 22 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index da19fb0..3c0f2a3 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -68,12 +68,17 @@ func set_data(new_name: String, new_type: Variant.Type, argument: String, new_de set_arg(str(new_default) if new_default != null else '') -func _is_string_valid_type(str: String) -> bool: +func _validate_text_type() -> bool: + var var_parsed_text: String = ( + _input.text + if _input.text.count("{{") <= 0 + else _input.text # TODO: Return Variable-Parsed text. + ) return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING - or str.is_empty() - or typeof(str_to_var(str)) == type + or var_parsed_text.is_empty() + or typeof(str_to_var(var_parsed_text)) == type ) @@ -111,9 +116,16 @@ func _on_reset_button_pressed() -> void: func _on_argument_text_edit_focus_exited() -> void: if _input.text == _arg: return - if !_is_string_valid_type(_input.text): + + var invalid_vars: Array[String] = [] # TODO: Call method that returns vars that could not be parsed (Array[String]). + if !invalid_vars.is_empty(): + push_error( + 'Argument <%s> with value <%s> in <%s> has invalid variables <%s>!' + % [arg_name, _input.text, _call_node.title, str(invalid_vars)] + ) + if !_validate_text_type(): push_error( - 'Argument <%s> with value <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' + 'Argument <%s> with value <%s> in <%s> cannot be converted to the needed type <%s>!' % [arg_name, _input.text, _call_node.title, type_string(type)] ) changed_value.emit(self, _arg, _input.text) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 4440e77..1e65307 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -46,12 +46,17 @@ func set_type(new_type: Variant.Type) -> void: type = new_type -func _is_string_valid_type(str: String) -> bool: +func _validate_text_type() -> bool: + var var_parsed_text: String = ( + _input.text + if _input.text.count("{{") <= 0 + else _input.text # TODO: Return Variable-Parsed text. + ) return ( type == Variant.Type.TYPE_NIL or type == Variant.Type.TYPE_STRING - or str.is_empty() - or typeof(str_to_var(str)) == type + or var_parsed_text.is_empty() + or typeof(str_to_var(var_parsed_text)) == type ) @@ -85,9 +90,16 @@ func _on_remove_button_pressed() -> void: func _on_return_text_edit_focus_exited() -> void: if _input.text == _ret: return - if !_is_string_valid_type(_input.text): + + var invalid_vars: Array[String] = [] # TODO: Call method that returns vars that could not be parsed (Array[String]). + if !invalid_vars.is_empty(): + push_error( + 'Return <%s> in <%s> has invalid variables <%s>!' + % [_input.text, _call_node.title, str(invalid_vars)] + ) + if !_validate_text_type(): push_error( - 'Return <%s> in CallNode <%s> cannot be converted to the needed type <%s>!' + 'Return <%s> in <%s> cannot be converted to the needed type <%s>!' % [_input.text, _call_node.title, type_string(type)] ) changed_value.emit(self, _ret, _input.text) From 9db7b0b96811fc60f40682b2e0293bd7e06ec94d Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 01:16:49 +0200 Subject: [PATCH 41/52] Fixed bug on DialogueParser where returns vars where not being parsed --- addons/dialogue_nodes/objects/DialogueParser.gd | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index 10f5f4c..a403d52 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -256,13 +256,9 @@ func _process_call(dict: Dictionary): var ret = (load(dict.library) as Script).callv(dict.method.name, args) for idx: int in dict.rets: - var ret_option = _parse_variables( - dict.rets[idx].value - if dict.rets[idx].value.count('{{') > 0 - else dict.rets[idx].value - ) + var ret_option = _parse_variables(dict.rets[idx].value) if dict.rets[idx].value.count('{{') > 0 else dict.rets[idx].value if dict.method.return.type != Variant.Type.TYPE_STRING: - ret_option = str_to_var(dict.rets[idx].value) + ret_option = str_to_var(ret_option) if typeof(ret_option) == typeof(ret) and ret_option == ret: _proceed(dict.rets[idx].link) From 03a8552fffbff69c4e3809be1344b6bc9c5654fc Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 01:33:41 +0200 Subject: [PATCH 42/52] Created good ExampleCalls sample CallNode file --- addons/dialogue_nodes/editor/calls.gd | 1 + examples/ExampleCall.tres | 117 +++++++++----------------- 2 files changed, 42 insertions(+), 76 deletions(-) diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index e2a9f87..bc293ec 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -43,4 +43,5 @@ static func roll_dice(dice: Dictionary) -> Array: var result: int = roll_a_die(die_name, die.ignore if die.has("ignore") else [], die.faces) if result != -1: results.push_back(result) + print("Several dice where rolled, results are <%s>." % str(results)) return results diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres index 5ff79eb..af8e71d 100644 --- a/examples/ExampleCall.tres +++ b/examples/ExampleCall.tres @@ -10,12 +10,12 @@ starts = { nodes = { "0_1": { "link": &"9_1", -"offset": Vector2(-300, -220), +"offset": Vector2(-640, -220), "start_id": "START" }, "1_1": { -"dialogue": "I will never roll a 1, as we ignore odd numbers.", -"offset": Vector2(360, -300), +"dialogue": "This will never happen, as the d6 dice ignore 1s, so it will never roll a 1.", +"offset": Vector2(320, -460), "options": { 0: { "condition": Array[Dictionary]([]), @@ -27,8 +27,8 @@ nodes = { "speaker": "Announcer" }, "1_2": { -"dialogue": "I rolled 2!", -"offset": Vector2(700, -300), +"dialogue": "You rolled a 6 AND the roll you where aiming for, a 6 and a {{target_face}}!", +"offset": Vector2(680, -180), "options": { 0: { "condition": Array[Dictionary]([]), @@ -39,61 +39,27 @@ nodes = { "size": Vector2(300, 240), "speaker": "Announcer" }, -"1_3": { -"dialogue": "I will never roll a 3, as we ignore odd numbers.", -"offset": Vector2(1020, -300), -"options": { -0: { -"condition": Array[Dictionary]([]), -"link": "END", -"text": "" -} -}, -"size": Vector2(300, 240), -"speaker": "Announcer" -}, -"1_4": { -"dialogue": "I rolled 4!", -"offset": Vector2(360, 0), -"options": { -0: { -"condition": Array[Dictionary]([]), -"link": "END", -"text": "" -} -}, -"size": Vector2(300, 240), -"speaker": "Announcer" -}, -"1_5": { -"dialogue": "I will never roll a 5, as we ignore odd numbers.", -"offset": Vector2(700, 0), +"1_7": { +"dialogue": "You failed to roll any significant value... Want to try again?", +"offset": Vector2(320, 180), "options": { 0: { "condition": Array[Dictionary]([]), -"link": "END", -"text": "" -} -}, -"size": Vector2(300, 240), -"speaker": "Announcer" +"link": &"9_1", +"text": "Yeah!" }, -"1_6": { -"dialogue": "I rolled 6!", -"offset": Vector2(1020, 0), -"options": { -0: { +1: { "condition": Array[Dictionary]([]), "link": "END", -"text": "" +"text": "Nah..." } }, -"size": Vector2(300, 240), +"size": Vector2(300, 289), "speaker": "Announcer" }, -"1_7": { -"dialogue": "I rolled something beyond 1-6. This is not your typical dice!", -"offset": Vector2(360, 300), +"1_8": { +"dialogue": "Lucky! You rolled the max value on both dice: 6 and {{dice_faces}}!", +"offset": Vector2(320, -120), "options": { 0: { "condition": Array[Dictionary]([]), @@ -111,11 +77,14 @@ arguments, listing any possible outputs to determine where the node will connect to depending on what the method returns, with a default output if no return case matches.", -"offset": Vector2(-500, -60), +"offset": Vector2(-840, -60), "size": Vector2(380, 200) }, "9_1": { -"args": ["{ \"d6\": {\"faces\": 7, \"ignore\": [1,3,5]} }"], +"args": ["{ +\"1d6\": {\"faces\": 6, \"ignore\": [1,3,5] }, +\"{{dice_name}}\": {\"faces\": {{dice_faces}}, \"ignore\": {{ignore_faces}} } +}"], "default": &"1_7", "library": "res://addons/dialogue_nodes/editor/calls.gd", "method": { @@ -141,43 +110,39 @@ with a default output if no return case matches.", "usage": 0 } }, -"offset": Vector2(-60, -220), +"offset": Vector2(-380, -220), "rets": { 0: { "link": &"1_1", -"value": "[1]" +"value": "[1,1]" }, 1: { -"link": &"1_2", -"value": "[2]" +"link": &"1_8", +"value": "[6, {{dice_faces}}]" }, 2: { -"link": &"1_3", -"value": "[3]" -}, -3: { -"link": &"1_4", -"value": "[4]" -}, -4: { -"link": &"1_5", -"value": "[5]" -}, -5: { -"link": &"1_6", -"value": "[6]" +"link": &"1_2", +"value": "[6, {{target_face}}]" } } } } variables = { -"blue_pill": { -"type": 1, -"value": false +"dice_faces": { +"type": 2, +"value": 8 +}, +"dice_name": { +"type": 4, +"value": "d8" +}, +"ignore_faces": { +"type": 4, +"value": "[1,2,3,4,5,6]" }, -"red_pill": { -"type": 1, -"value": false +"target_face": { +"type": 4, +"value": "7" } } strays = Array[String](["2_1"]) From 6db7efc8ba1a4e664133d37407c91d1af3637ee8 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 11:33:22 +0200 Subject: [PATCH 43/52] CallNode Return resizes on ready --- addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index 1e65307..cc89381 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -21,6 +21,7 @@ var _ret: String = '' func _ready() -> void: _input.text = '' + _resize_input_to_ret() func set_call_node(call_node: GraphNode) -> void: From f4e4e499cf9c8b9e70a08a89e10b15d28afc540f Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 11:33:48 +0200 Subject: [PATCH 44/52] Added Variable Highlighting to CallNode --- .../dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn | 7 +++++-- addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn | 6 ++++-- addons/dialogue_nodes/themes/node_code_highlighter.tres | 7 +++++++ examples/ExampleCall.tres | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 addons/dialogue_nodes/themes/node_code_highlighter.tres diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index b77c600..b774d3a 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=3 format=3 uid="uid://bskh1vm7i45nw"] +[gd_scene load_steps=4 format=3 uid="uid://bskh1vm7i45nw"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd" id="1_mqq8d"] +[ext_resource type="CodeHighlighter" uid="uid://dkf2orxmijq20" path="res://addons/dialogue_nodes/themes/node_code_highlighter.tres" id="2_bcsls"] [ext_resource type="Texture2D" uid="uid://cf5ef41yo6jl6" path="res://addons/dialogue_nodes/icons/Reload.svg" id="2_h0t8n"] [node name="CallNodeArgument" type="HBoxContainer"] @@ -17,12 +18,14 @@ text = "ArgName" [node name="ArgumentTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -custom_minimum_size = Vector2(52, 0) +custom_minimum_size = Vector2(37, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 placeholder_text = "Type" scroll_fit_content_height = true +syntax_highlighter = ExtResource("2_bcsls") +highlight_all_occurrences = true [node name="ResetButton" type="Button" parent="."] unique_name_in_owner = true diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index f510323..b9fe914 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=3 format=3 uid="uid://djc2ggs14vbxc"] +[gd_scene load_steps=4 format=3 uid="uid://djc2ggs14vbxc"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd" id="1_lcs7a"] [ext_resource type="Texture2D" uid="uid://b8o81rn8q8a43" path="res://addons/dialogue_nodes/icons/Remove.svg" id="1_t7mvg"] +[ext_resource type="CodeHighlighter" uid="uid://dkf2orxmijq20" path="res://addons/dialogue_nodes/themes/node_code_highlighter.tres" id="2_rq5ec"] [node name="CallNodeReturn" type="HBoxContainer"] size_flags_horizontal = 3 @@ -10,12 +11,13 @@ script = ExtResource("1_lcs7a") [node name="ReturnTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true -custom_minimum_size = Vector2(100, 0) +custom_minimum_size = Vector2(52, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 placeholder_text = "Type" scroll_fit_content_height = true +syntax_highlighter = ExtResource("2_rq5ec") [node name="RemoveButton" type="Button" parent="."] layout_mode = 2 diff --git a/addons/dialogue_nodes/themes/node_code_highlighter.tres b/addons/dialogue_nodes/themes/node_code_highlighter.tres new file mode 100644 index 0000000..7b42fd8 --- /dev/null +++ b/addons/dialogue_nodes/themes/node_code_highlighter.tres @@ -0,0 +1,7 @@ +[gd_resource type="CodeHighlighter" format=3 uid="uid://dkf2orxmijq20"] + +[resource] +number_color = Color(0.113725, 0.717647, 0.619608, 1) +color_regions = { +"{{ }}": Color(0.826247, 0.867188, 0.118561, 1) +} diff --git a/examples/ExampleCall.tres b/examples/ExampleCall.tres index af8e71d..d1ded16 100644 --- a/examples/ExampleCall.tres +++ b/examples/ExampleCall.tres @@ -82,7 +82,7 @@ with a default output if no return case matches.", }, "9_1": { "args": ["{ -\"1d6\": {\"faces\": 6, \"ignore\": [1,3,5] }, +\"d6\": {\"faces\": 6, \"ignore\": [1,3,5] }, \"{{dice_name}}\": {\"faces\": {{dice_faces}}, \"ignore\": {{ignore_faces}} } }"], "default": &"1_7", From 4949a94a201eb475b743d43faf7506952102125e Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 13:34:54 +0200 Subject: [PATCH 45/52] Added RedColor background for invalid CallNode elements --- .../nodes/sub_nodes/CallNodeArgument.tscn | 2 +- .../nodes/sub_nodes/CallNodeReturn.tscn | 1 + .../nodes/sub_nodes/call_node_argument.gd | 39 +++++++++++----- .../nodes/sub_nodes/call_node_return.gd | 44 ++++++++++++++----- .../themes/node_code_highlighter.tres | 3 ++ 5 files changed, 64 insertions(+), 25 deletions(-) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn index b774d3a..64e5a4c 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn @@ -8,7 +8,7 @@ size_flags_horizontal = 3 size_flags_vertical = 4 script = ExtResource("1_mqq8d") -_font_size_margin = 0.0 +invalid_color = Color(0.2, 0.08, 0.08, 1) [node name="ArgumentLabel" type="Label" parent="."] unique_name_in_owner = true diff --git a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn index b9fe914..89d4a0c 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn +++ b/addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn @@ -8,6 +8,7 @@ size_flags_horizontal = 3 size_flags_vertical = 4 script = ExtResource("1_lcs7a") +invalid_color = Color(0.2, 0.08, 0.08, 1) [node name="ReturnTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd index 3c0f2a3..e9301b1 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_argument.gd @@ -8,6 +8,10 @@ extends Container signal changed_value(arg: Control, old: String, new: String) +# NOTE: Not a fan of this being defined here. Systematic default colors (Error, Warning, etc.) +# should be customizable, but the default value should be "centralized" somewhere in the addon +# so that all Nodes and such can refer to it. I'd recommend a custom Resource instance. +@export var invalid_color = Color.DARK_RED @export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 var arg_name: String = '' @@ -42,6 +46,12 @@ func set_arg(new_arg: String) -> void: _input.text = new_arg _set_reset_button_visibility() _resize_input_to_arg() + + if _validate_contents(): + _input.remove_theme_color_override('background_color') + else: + _input.add_theme_color_override('background_color', invalid_color) + _call_node.reset_size.call_deferred() @@ -105,27 +115,32 @@ func _resize_input_to_arg() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin -func _on_argument_text_edit_text_changed() -> void: - _resize_input_to_arg() - - -func _on_reset_button_pressed() -> void: - changed_value.emit(self, _arg, var_to_str(default_arg) if default_arg != null else '') - - -func _on_argument_text_edit_focus_exited() -> void: - if _input.text == _arg: - return - +func _validate_contents() -> bool: var invalid_vars: Array[String] = [] # TODO: Call method that returns vars that could not be parsed (Array[String]). if !invalid_vars.is_empty(): push_error( 'Argument <%s> with value <%s> in <%s> has invalid variables <%s>!' % [arg_name, _input.text, _call_node.title, str(invalid_vars)] ) + return false if !_validate_text_type(): push_error( 'Argument <%s> with value <%s> in <%s> cannot be converted to the needed type <%s>!' % [arg_name, _input.text, _call_node.title, type_string(type)] ) + return false + return true + + +func _on_argument_text_edit_text_changed() -> void: + _resize_input_to_arg() + + +func _on_reset_button_pressed() -> void: + changed_value.emit(self, _arg, var_to_str(default_arg) if default_arg != null else '') + + +func _on_argument_text_edit_focus_exited() -> void: + if _input.text == _arg: + return changed_value.emit(self, _arg, _input.text) diff --git a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd index cc89381..fa449a0 100644 --- a/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd +++ b/addons/dialogue_nodes/nodes/sub_nodes/call_node_return.gd @@ -9,6 +9,10 @@ extends Container signal changed_value(arg: Control, old: String, new: String) signal requested_removal(ret: Control) +# NOTE: Not a fan of this being defined here. Systematic default colors (Error, Warning, etc.) +# should be customizable, but the default value should be "centralized" somewhere in the addon +# so that all Nodes and such can refer to it. I'd recommend a custom Resource instance. +@export var invalid_color = Color.DARK_RED @export_range(0.0, 30.0, 0.1) var _font_size_margin: float = 15.0 var type: Variant.Type = Variant.Type.TYPE_NIL @@ -39,12 +43,23 @@ func set_ret(new_ret: String) -> void: _ret = new_ret _input.text = new_ret _resize_input_to_ret() + + if _validate_contents(): + _input.remove_theme_color_override('background_color') + else: + _input.add_theme_color_override('background_color', invalid_color) + _call_node.reset_size.call_deferred() func set_type(new_type: Variant.Type) -> void: _input.placeholder_text = type_string(new_type) if new_type != Variant.Type.TYPE_NIL else '' type = new_type + + if _validate_contents(): + _input.remove_theme_color_override('background_color') + else: + _input.add_theme_color_override('background_color', invalid_color) func _validate_text_type() -> bool: @@ -80,27 +95,32 @@ func _resize_input_to_ret() -> void: _input.custom_minimum_size.x = max_width + _font_size_margin -func _on_return_text_edit_text_changed() -> void: - _resize_input_to_ret() - - -func _on_remove_button_pressed() -> void: - requested_removal.emit(self) - - -func _on_return_text_edit_focus_exited() -> void: - if _input.text == _ret: - return - +func _validate_contents() -> bool: var invalid_vars: Array[String] = [] # TODO: Call method that returns vars that could not be parsed (Array[String]). if !invalid_vars.is_empty(): push_error( 'Return <%s> in <%s> has invalid variables <%s>!' % [_input.text, _call_node.title, str(invalid_vars)] ) + return false if !_validate_text_type(): push_error( 'Return <%s> in <%s> cannot be converted to the needed type <%s>!' % [_input.text, _call_node.title, type_string(type)] ) + return false + return true + + +func _on_return_text_edit_text_changed() -> void: + _resize_input_to_ret() + + +func _on_remove_button_pressed() -> void: + requested_removal.emit(self) + + +func _on_return_text_edit_focus_exited() -> void: + if _input.text == _ret: + return changed_value.emit(self, _ret, _input.text) diff --git a/addons/dialogue_nodes/themes/node_code_highlighter.tres b/addons/dialogue_nodes/themes/node_code_highlighter.tres index 7b42fd8..39826f5 100644 --- a/addons/dialogue_nodes/themes/node_code_highlighter.tres +++ b/addons/dialogue_nodes/themes/node_code_highlighter.tres @@ -2,6 +2,9 @@ [resource] number_color = Color(0.113725, 0.717647, 0.619608, 1) +symbol_color = Color(1, 1, 1, 1) +function_color = Color(1, 1, 1, 1) +member_variable_color = Color(1, 1, 1, 1) color_regions = { "{{ }}": Color(0.826247, 0.867188, 0.118561, 1) } From f7495032e34ac8691bed1c4464e16d2b36e3d803 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 14:57:13 +0200 Subject: [PATCH 46/52] Added FileSelection on CallNode --- addons/dialogue_nodes/nodes/CallNode.tscn | 27 ++++-- addons/dialogue_nodes/nodes/callNode.gd | 110 +++++++++++++++++++--- 2 files changed, 116 insertions(+), 21 deletions(-) diff --git a/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index 9039e7f..95ad40d 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -59,16 +59,18 @@ script = ExtResource("1_hul2k") [node name="MethodSetup" type="VBoxContainer" parent="."] layout_mode = 2 -[node name="SourceContainer" type="HBoxContainer" parent="MethodSetup"] -visible = false +[node name="MethodSourceContainer" type="BoxContainer" parent="MethodSetup"] layout_mode = 2 -[node name="FilePath" type="LineEdit" parent="MethodSetup/SourceContainer"] +[node name="FilePath" type="LineEdit" parent="MethodSetup/MethodSourceContainer"] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -size_flags_vertical = 4 +theme_override_font_sizes/font_size = 12 +text = "res://addons/dialogue_nodes/editor/calls.gd" +placeholder_text = "GDScript Resource" -[node name="BrowseButton" type="Button" parent="MethodSetup/SourceContainer"] +[node name="BrowseButton" type="Button" parent="MethodSetup/MethodSourceContainer"] layout_mode = 2 size_flags_horizontal = 8 icon = ExtResource("2_w7ejb") @@ -113,7 +115,18 @@ text = "Default Exit" horizontal_alignment = 2 [node name="FileDialog" type="FileDialog" parent="."] -size = Vector2i(800, 400) - +unique_name_in_owner = true +auto_translate_mode = 1 +title = "Open a DialogueData Resource" +size = Vector2i(600, 400) +ok_button_text = "Open" +mode_overrides_title = false +file_mode = 0 +filters = PackedStringArray("*.gd; GDScript Files") + +[connection signal="focus_exited" from="MethodSetup/MethodSourceContainer/FilePath" to="." method="_on_file_path_focus_exited"] +[connection signal="text_submitted" from="MethodSetup/MethodSourceContainer/FilePath" to="." method="_on_file_path_text_submitted"] +[connection signal="pressed" from="MethodSetup/MethodSourceContainer/BrowseButton" to="." method="_on_browse_button_pressed"] [connection signal="item_selected" from="MethodSetup/MethodSelector" to="." method="_on_method_selector_item_selected"] [connection signal="pressed" from="AddReturnButton" to="." method="_on_add_return_button_pressed"] +[connection signal="file_selected" from="FileDialog" to="." method="_on_file_dialog_file_selected"] diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 57980ba..061f5d0 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -10,7 +10,7 @@ extends GraphNode ## [color=Yellow]Warning[/color]: All [i]Arguments[/i] and [i]Returns[/i] must be formatted ## so they can be converted to their appropriate types via [method @GlobalScope.str_to_var]. -const DEFAULT_CALLS: Script = preload('res://addons/dialogue_nodes/editor/calls.gd') +const DEFAULT_CALLS: String = 'res://addons/dialogue_nodes/editor/calls.gd' signal modified signal disconnection_from_request(from_node: String, from_port: int) @@ -20,7 +20,7 @@ var undo_redo: EditorUndoRedoManager var base_color: Color = Color.WHITE -var _calls_script: Script = null +var _loaded_file: String = DEFAULT_CALLS var _calls: Dictionary = {} var _active_method: Dictionary = {} var _num_rets: int = 0 @@ -28,6 +28,9 @@ var _num_rets: int = 0 var _arg_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn') var _ret_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn') +@onready var _file_path: LineEdit = %FilePath +@onready var _file_dialog: FileDialog = %FileDialog + @onready var _method_button: OptionButton = %MethodSelector @onready var _args_section_container: Container = %ArgumentsSectionContainer @onready var _args_container: Container = %ArgumentsContainer @@ -42,8 +45,8 @@ var _ret_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nod # Core # ------------------------------------------------------------------------------------------------- func _ready() -> void: - _reload_library(DEFAULT_CALLS) - _reload_method_ui() + _set_library(DEFAULT_CALLS) + _reload_args_ui() _reload_rets_ui() _update_slots() @@ -53,7 +56,7 @@ func _to_dict(graph: GraphEdit) -> Dictionary: var dict := {} # Export Library - dict['library'] = _calls_script.resource_path + dict['library'] = _loaded_file # Export Method dict['method'] = _active_method @@ -95,7 +98,7 @@ func _to_dict(graph: GraphEdit) -> Dictionary: func _from_dict(dict: Dictionary) -> Array[String]: # Import Library - _reload_library(load(dict['library'])) + _set_library(dict['library']) # Import Method _reload_method_ui() @@ -145,20 +148,52 @@ func _update_slots() -> void: # ------------------------------------------------------------------------------------------------- # Library # ------------------------------------------------------------------------------------------------- -func _reload_library(script: Script) -> bool: - if script == null: - push_error("Cannot reload CallNode's library with a NULL script!") +func _set_library(path: String) -> bool: + _file_path.text = path + _calls.clear() + + if not ResourceLoader.exists(path, "Script"): + push_error("Cannot set <%s> as Method Library to <%s>. File path is invalid!" % [path, title]) + _loaded_file = '' + _set_method('') + _reload_method_ui() + _reload_args_ui() return false + _loaded_file = path + var script: Script = ResourceLoader.load(path, "Script") + # Re-write Calls Library with new methods - _calls.clear() for method: Dictionary in script.get_script_method_list(): _calls[method.name] = method - _calls_script = script + _reload_method_ui() + _reload_args_ui() return true +func _request_library_change(new_path: String) -> void: + if _loaded_file == new_path: + return + + if not undo_redo: + _set_library(new_path) + return + + undo_redo.create_action('CallNode Method Script Changed') + undo_redo.add_do_method(self, '_set_library', new_path) + undo_redo.add_do_method(self, '_on_modified') + + undo_redo.add_undo_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_set_library', _loaded_file) + undo_redo.add_undo_method(_method_button, 'select', _active_method.index if !_active_method.is_empty() else 0) + undo_redo.add_undo_method(self, '_set_method', _active_method.name if !_active_method.is_empty() else '') + for arg: Node in _args_container.get_children(): + undo_redo.add_undo_method(self, '_set_argument', arg.arg_name, arg.get_arg()) + + undo_redo.commit_action() + + # ------------------------------------------------------------------------------------------------- # Method # ------------------------------------------------------------------------------------------------- @@ -246,7 +281,7 @@ func _reload_args_ui() -> void: # Remove old arguments that do not exist in new method. for outdated_arg: Node in _old_args.values(): outdated_arg.queue_free() - + # Show/Hide Arguments section based on prevailing arguments _args_section_container.visible = _args_container.get_child_count() > 0 @@ -362,8 +397,24 @@ func clear_returns() -> void: # ------------------------------------------------------------------------------------------------- -# Signals: CallNode +# Signals: Method Library # ------------------------------------------------------------------------------------------------- +func _on_browse_button_pressed() -> void: + _file_dialog.popup_centered() + + +func _on_file_dialog_file_selected(path: String) -> void: + _request_library_change(path) + + +func _on_file_path_text_submitted(new_text: String): + _request_library_change(new_text) + + +func _on_file_path_focus_exited() -> void: + _request_library_change(_file_path.text) + + # TODO: Add method to "bind" a "changed file" signal from script we are loading methods from. func _on_calls_script_changed() -> void: pass @@ -372,6 +423,9 @@ func _on_calls_script_changed() -> void: #_reload_args_ui() +# ------------------------------------------------------------------------------------------------- +# Signals: Method +# ------------------------------------------------------------------------------------------------- func _on_method_selector_item_selected(index: int) -> void: if index == -1: return @@ -402,8 +456,14 @@ func _on_method_selector_item_selected(index: int) -> void: # Signals: Arguments # ------------------------------------------------------------------------------------------------- func _on_changed_argument(arg: Control, old: String, new: String) -> void: + if not undo_redo: + _set_argument(arg.arg_name, new) + return + undo_redo.create_action('Edited Argument <%s> in <%s>' % [arg.arg_name, title]) undo_redo.add_do_method(self, '_set_argument', arg.arg_name, new) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, '_set_argument', arg.arg_name, old) undo_redo.commit_action() @@ -412,19 +472,30 @@ func _on_changed_argument(arg: Control, old: String, new: String) -> void: # Signals: Returns # ------------------------------------------------------------------------------------------------- func _on_add_return_button_pressed() -> void: + if not undo_redo: + _add_return(_num_rets) + return + undo_redo.create_action('Added Return on <%s>' % title) undo_redo.add_do_method(self, '_add_return', _num_rets) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, '_remove_return_at', _num_rets) undo_redo.commit_action() func _on_return_requested_removal(ret: Control) -> void: var relative_idx: int = ret.get_index() - _ret_idx_start - disconnection_from_request.emit(name, relative_idx) + if not undo_redo: + _remove_return_at(relative_idx) + return + undo_redo.create_action('Remove Return on <%s>' % title) undo_redo.add_do_method(self, '_remove_return_at', relative_idx) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, '_add_return', relative_idx) undo_redo.add_undo_method(self, '_set_return', relative_idx, ret.get_ret()) undo_redo.commit_action() @@ -432,7 +503,18 @@ func _on_return_requested_removal(ret: Control) -> void: func _on_changed_return(ret: Control, old: String, new: String) -> void: var ret_idx: int = ret.get_index() - _ret_idx_start + + if not undo_redo: + _set_return(ret_idx, new) + return + undo_redo.create_action('Edited Return <%s> in <%s>' % [ret_idx, title]) undo_redo.add_do_method(self, '_set_return', ret_idx, new) + undo_redo.add_do_method(self, '_on_modified') + undo_redo.add_undo_method(self, '_on_modified') undo_redo.add_undo_method(self, '_set_return', ret_idx, old) undo_redo.commit_action() + + +func _on_modified() -> void: + modified.emit() From 083fcd1196009c166e253a6c5c789347dd59d43e Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 18:16:38 +0200 Subject: [PATCH 47/52] CallNode can now have its Library section hidden/shown for convenience --- .../icons/GuiVisibilityHidden.svg | 1 + .../icons/GuiVisibilityHidden.svg.import | 37 +++++++++++++ .../icons/GuiVisibilityVisible.svg | 1 + .../icons/GuiVisibilityVisible.svg.import | 37 +++++++++++++ addons/dialogue_nodes/nodes/CallNode.tscn | 50 ++++++++++++++---- addons/dialogue_nodes/nodes/callNode.gd | 52 ++++++++++++++++--- 6 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 addons/dialogue_nodes/icons/GuiVisibilityHidden.svg create mode 100644 addons/dialogue_nodes/icons/GuiVisibilityHidden.svg.import create mode 100644 addons/dialogue_nodes/icons/GuiVisibilityVisible.svg create mode 100644 addons/dialogue_nodes/icons/GuiVisibilityVisible.svg.import diff --git a/addons/dialogue_nodes/icons/GuiVisibilityHidden.svg b/addons/dialogue_nodes/icons/GuiVisibilityHidden.svg new file mode 100644 index 0000000..a9da2ef --- /dev/null +++ b/addons/dialogue_nodes/icons/GuiVisibilityHidden.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/GuiVisibilityHidden.svg.import b/addons/dialogue_nodes/icons/GuiVisibilityHidden.svg.import new file mode 100644 index 0000000..e4d5318 --- /dev/null +++ b/addons/dialogue_nodes/icons/GuiVisibilityHidden.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2mg38tjwdqxy" +path="res://.godot/imported/GuiVisibilityHidden.svg-738b8ca69e63c5fe2502627e2587ba16.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_nodes/icons/GuiVisibilityHidden.svg" +dest_files=["res://.godot/imported/GuiVisibilityHidden.svg-738b8ca69e63c5fe2502627e2587ba16.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/addons/dialogue_nodes/icons/GuiVisibilityVisible.svg b/addons/dialogue_nodes/icons/GuiVisibilityVisible.svg new file mode 100644 index 0000000..3f953ef --- /dev/null +++ b/addons/dialogue_nodes/icons/GuiVisibilityVisible.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogue_nodes/icons/GuiVisibilityVisible.svg.import b/addons/dialogue_nodes/icons/GuiVisibilityVisible.svg.import new file mode 100644 index 0000000..fe1fd6e --- /dev/null +++ b/addons/dialogue_nodes/icons/GuiVisibilityVisible.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bu86pp64kp8x1" +path="res://.godot/imported/GuiVisibilityVisible.svg-b483b14bb2090f8a0fc45209c4b12a10.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogue_nodes/icons/GuiVisibilityVisible.svg" +dest_files=["res://.godot/imported/GuiVisibilityVisible.svg-b483b14bb2090f8a0fc45209c4b12a10.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/addons/dialogue_nodes/nodes/CallNode.tscn b/addons/dialogue_nodes/nodes/CallNode.tscn index 95ad40d..5d77c1c 100644 --- a/addons/dialogue_nodes/nodes/CallNode.tscn +++ b/addons/dialogue_nodes/nodes/CallNode.tscn @@ -1,7 +1,9 @@ -[gd_scene load_steps=4 format=3 uid="uid://cu71fiowdkvmr"] +[gd_scene load_steps=6 format=3 uid="uid://cu71fiowdkvmr"] [ext_resource type="Script" path="res://addons/dialogue_nodes/nodes/callNode.gd" id="1_hul2k"] +[ext_resource type="Texture2D" uid="uid://cf5ef41yo6jl6" path="res://addons/dialogue_nodes/icons/Reload.svg" id="2_u8etb"] [ext_resource type="Texture2D" uid="uid://buayifvickq2l" path="res://addons/dialogue_nodes/icons/Folder.svg" id="2_w7ejb"] +[ext_resource type="Texture2D" uid="uid://2mg38tjwdqxy" path="res://addons/dialogue_nodes/icons/GuiVisibilityHidden.svg" id="4_golf1"] [ext_resource type="Texture2D" uid="uid://cj0m8q8hgm0ed" path="res://addons/dialogue_nodes/icons/Add.svg" id="4_hca56"] [node name="CallNode" type="GraphNode"] @@ -59,10 +61,15 @@ script = ExtResource("1_hul2k") [node name="MethodSetup" type="VBoxContainer" parent="."] layout_mode = 2 -[node name="MethodSourceContainer" type="BoxContainer" parent="MethodSetup"] +[node name="MethodLibraryContainer" type="VBoxContainer" parent="MethodSetup"] +unique_name_in_owner = true +visible = false layout_mode = 2 -[node name="FilePath" type="LineEdit" parent="MethodSetup/MethodSourceContainer"] +[node name="FileSelection" type="BoxContainer" parent="MethodSetup/MethodLibraryContainer"] +layout_mode = 2 + +[node name="FilePath" type="LineEdit" parent="MethodSetup/MethodLibraryContainer/FileSelection"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -70,14 +77,37 @@ theme_override_font_sizes/font_size = 12 text = "res://addons/dialogue_nodes/editor/calls.gd" placeholder_text = "GDScript Resource" -[node name="BrowseButton" type="Button" parent="MethodSetup/MethodSourceContainer"] +[node name="BrowseButton" type="Button" parent="MethodSetup/MethodLibraryContainer/FileSelection"] layout_mode = 2 size_flags_horizontal = 8 icon = ExtResource("2_w7ejb") -[node name="MethodSelector" type="OptionButton" parent="MethodSetup"] +[node name="ReimportButton" type="Button" parent="MethodSetup/MethodLibraryContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_font_sizes/font_size = 12 +text = "Reimport" +icon = ExtResource("2_u8etb") + +[node name="HSeparator" type="HSeparator" parent="MethodSetup/MethodLibraryContainer"] +layout_mode = 2 + +[node name="BoxContainer" type="BoxContainer" parent="MethodSetup"] +layout_mode = 2 + +[node name="MethodLibraryButton" type="Button" parent="MethodSetup/BoxContainer"] unique_name_in_owner = true layout_mode = 2 +toggle_mode = true +icon = ExtResource("4_golf1") +icon_alignment = 1 + +[node name="MethodSelector" type="OptionButton" parent="MethodSetup/BoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 allow_reselect = true [node name="ArgumentsSectionContainer" type="HBoxContainer" parent="MethodSetup"] @@ -124,9 +154,11 @@ mode_overrides_title = false file_mode = 0 filters = PackedStringArray("*.gd; GDScript Files") -[connection signal="focus_exited" from="MethodSetup/MethodSourceContainer/FilePath" to="." method="_on_file_path_focus_exited"] -[connection signal="text_submitted" from="MethodSetup/MethodSourceContainer/FilePath" to="." method="_on_file_path_text_submitted"] -[connection signal="pressed" from="MethodSetup/MethodSourceContainer/BrowseButton" to="." method="_on_browse_button_pressed"] -[connection signal="item_selected" from="MethodSetup/MethodSelector" to="." method="_on_method_selector_item_selected"] +[connection signal="focus_exited" from="MethodSetup/MethodLibraryContainer/FileSelection/FilePath" to="." method="_on_file_path_focus_exited"] +[connection signal="text_submitted" from="MethodSetup/MethodLibraryContainer/FileSelection/FilePath" to="." method="_on_file_path_text_submitted"] +[connection signal="pressed" from="MethodSetup/MethodLibraryContainer/FileSelection/BrowseButton" to="." method="_on_browse_button_pressed"] +[connection signal="pressed" from="MethodSetup/MethodLibraryContainer/ReimportButton" to="." method="_on_reimport_button_pressed"] +[connection signal="toggled" from="MethodSetup/BoxContainer/MethodLibraryButton" to="." method="_on_method_library_button_toggled"] +[connection signal="item_selected" from="MethodSetup/BoxContainer/MethodSelector" to="." method="_on_method_selector_item_selected"] [connection signal="pressed" from="AddReturnButton" to="." method="_on_add_return_button_pressed"] [connection signal="file_selected" from="FileDialog" to="." method="_on_file_dialog_file_selected"] diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 061f5d0..64f71c8 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -12,6 +12,9 @@ extends GraphNode const DEFAULT_CALLS: String = 'res://addons/dialogue_nodes/editor/calls.gd' +const HIDDEN_ICON: Texture2D = preload("res://addons/dialogue_nodes/icons/GuiVisibilityHidden.svg") +const VISIBLE_ICON: Texture2D = preload("res://addons/dialogue_nodes/icons/GuiVisibilityVisible.svg") + signal modified signal disconnection_from_request(from_node: String, from_port: int) signal connection_shift_request(from_node: String, old_port: int, new_port: int) @@ -28,8 +31,12 @@ var _num_rets: int = 0 var _arg_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeArgument.tscn') var _ret_scene: PackedScene = preload('res://addons/dialogue_nodes/nodes/sub_nodes/CallNodeReturn.tscn') +@onready var _method_library_container: Container = %MethodLibraryContainer +@onready var _method_library_button: Button = %MethodLibraryButton + @onready var _file_path: LineEdit = %FilePath @onready var _file_dialog: FileDialog = %FileDialog +@onready var _reimport_button: Button = %ReimportButton @onready var _method_button: OptionButton = %MethodSelector @onready var _args_section_container: Container = %ArgumentsSectionContainer @@ -155,6 +162,7 @@ func _set_library(path: String) -> bool: if not ResourceLoader.exists(path, "Script"): push_error("Cannot set <%s> as Method Library to <%s>. File path is invalid!" % [path, title]) _loaded_file = '' + _reimport_button.visible = false _set_method('') _reload_method_ui() _reload_args_ui() @@ -166,12 +174,27 @@ func _set_library(path: String) -> bool: # Re-write Calls Library with new methods for method: Dictionary in script.get_script_method_list(): _calls[method.name] = method + _reimport_button.visible = true _reload_method_ui() _reload_args_ui() return true +func _is_script_equal(script_file: String) -> bool: + var script: Script = ResourceLoader.load(_loaded_file, "Script") + for method: Dictionary in script.get_script_method_list(): + if ( + not _calls.has(method.name) + or _calls[method.name].args != method.args + or _calls[method.name].default_args != method.default_args + or _calls[method.name].default_args != method.default_args + or _calls[method.name].return != method.return + ): + return false + return true + + func _request_library_change(new_path: String) -> void: if _loaded_file == new_path: return @@ -230,6 +253,12 @@ func _set_method(method_name: String) -> bool: return true +func _toggle_library_ui(toggled_on: bool) -> void: + _method_library_container.visible = toggled_on + _method_library_button.icon = VISIBLE_ICON if toggled_on else HIDDEN_ICON + reset_size() + + # ------------------------------------------------------------------------------------------------- # Arguments # ------------------------------------------------------------------------------------------------- @@ -399,6 +428,17 @@ func clear_returns() -> void: # ------------------------------------------------------------------------------------------------- # Signals: Method Library # ------------------------------------------------------------------------------------------------- +func _on_method_library_button_toggled(toggled_on: bool) -> void: + if not undo_redo: + _toggle_library_ui(toggled_on) + return + + undo_redo.create_action('Set Method Visbility in <%s>' % title) + undo_redo.add_do_method(self, '_toggle_library_ui', toggled_on) + undo_redo.add_undo_method(self, '_toggle_library_ui', !toggled_on) + undo_redo.commit_action() + + func _on_browse_button_pressed() -> void: _file_dialog.popup_centered() @@ -415,12 +455,12 @@ func _on_file_path_focus_exited() -> void: _request_library_change(_file_path.text) -# TODO: Add method to "bind" a "changed file" signal from script we are loading methods from. -func _on_calls_script_changed() -> void: - pass - #_reload_library(_calls_script) - #_reload_method_ui() - #_reload_args_ui() +# NOTE: It would be ideal if this could be done automatically when the file is edited. +# Maybe an Editor-wide "save" signal or EditorPlugin signal could help. +func _on_reimport_button_pressed() -> void: + if not _loaded_file.is_empty() and not _is_script_equal(_loaded_file): + print_debug("Reimported Method Library <%s> in <%s>." % [_loaded_file, title]) + _set_library(_loaded_file) # ------------------------------------------------------------------------------------------------- From 6ceebbafd5de126573f7206bf52e1d051bfb1540 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Mon, 14 Oct 2024 18:30:04 +0200 Subject: [PATCH 48/52] Fixed unwanted changes on graph.gd --- addons/dialogue_nodes/editor/graph.gd | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/addons/dialogue_nodes/editor/graph.gd b/addons/dialogue_nodes/editor/graph.gd index 42f87e5..448fef2 100644 --- a/addons/dialogue_nodes/editor/graph.gd +++ b/addons/dialogue_nodes/editor/graph.gd @@ -26,7 +26,6 @@ const _duplicate_offset := Vector2(20, 20) var undo_redo: EditorUndoRedoManager var starts: Array[String] = [] -var frames: Array[StringName] = [] var cursor_pos := Vector2.ZERO var selected_nodes := [] var request_node := '' @@ -144,8 +143,6 @@ func add_node(id: int, node_name := '', offset := cursor_pos) -> GraphElement: new_node.set_ID('START' + new_node.name.split('_')[1]) 1: # dialogue node new_node._on_characters_updated(last_character_list) - 8: # graph frame - add_to_frames(new_node.name) return new_node @@ -275,35 +272,6 @@ func update_slots_color(nodes: Array = get_children()) -> void: if 'base_color' in node: node.base_color = base_color -func attach_node_to_frame(element: StringName, frame: StringName) -> void: - attach_graph_element_to_frame(element, frame) - - var node: GraphNode = get_node(NodePath(element)) - var detach_button := Button.new() - detach_button.icon = detach_icon - detach_button.name = 'DetachButton' - detach_button.flat = true - node.get_titlebar_hbox().add_child(detach_button, true) - detach_button.pressed.connect( - _on_graph_elements_unlinked_to_frame_request.bind(element, frame) - ) - - var frame_node: GraphFrame = get_node(NodePath(frame)) - frame_node.attach_node(element) - - -func detach_node_from_frame(element: StringName, frame: StringName) -> void: - detach_graph_element_from_frame(element) - - var node: GraphNode = get_node(NodePath(element)) - var detach_button: Button = node.get_titlebar_hbox().get_node('DetachButton') - detach_button.pressed.disconnect(_on_graph_elements_unlinked_to_frame_request) - detach_button.queue_free() - - var frame_node: GraphFrame = get_node(NodePath(frame)) - frame_node.detach_node(element) - - func _on_add_menu_pressed(id: int) -> void: if not undo_redo: add_node(id) From dab7d99866ce9c17e0f22e7d2ec786962211c239 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Tue, 15 Oct 2024 17:17:38 +0200 Subject: [PATCH 49/52] Added option to call Autoloads. --- addons/dialogue_nodes/nodes/callNode.gd | 21 ++++++++++++++++--- .../dialogue_nodes/objects/DialogueParser.gd | 14 ++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/addons/dialogue_nodes/nodes/callNode.gd b/addons/dialogue_nodes/nodes/callNode.gd index 64f71c8..48cfa97 100644 --- a/addons/dialogue_nodes/nodes/callNode.gd +++ b/addons/dialogue_nodes/nodes/callNode.gd @@ -3,12 +3,27 @@ extends GraphNode ## ## Call Node ## -## This Node calls a function from an expandable library of callables to determine which Node -## output to take based on what the method returned. -## The library, a static object called [DialogueCalls], is expected to be expanded by the user. +## This Node calls a method from a [Script] with the given parameters and matching the returned +## value to a list of given values, exiting the Node through the first matching value found. +## A default Script [code]calls.gd[/code] exists, +## but it is expected from the user to customize it or create more scripts to refer to. +## [br][br] +## While in-game, CallNode's will search [code]root[/code]'s children, +## looking for a [Node] (an Autoload, most likely) that holds an instance of its assigned [Script]. +## If none is found, a [Script] instance is loaded instead, +## although in the second case only [color=Indian_Red][i]static[/i][/color] methods can be used. ## [br][br] ## [color=Yellow]Warning[/color]: All [i]Arguments[/i] and [i]Returns[/i] must be formatted ## so they can be converted to their appropriate types via [method @GlobalScope.str_to_var]. +## [br][br] +## [color=Yellow]Warning[/color]: If more than one child of [code]root[/code] has an instance +## of the same [Script], only the first one will ever be used by CallNodes! +## I.e., if [i]Autoloads[/i] share scripts, only the first one loaded will be used by CallNodes. + +# NOTE: The current implementation does not allow for multiple Autoloads using the same Script. +# I considered it a very niche use-case, so I ignored it, specially since there's seemingly +# no easy way to read Godot's Autoload list to place on a [OptionButton] listing them. +# However, it could still be integrated, but there would be no easy way to warn of potential bugs. const DEFAULT_CALLS: String = 'res://addons/dialogue_nodes/editor/calls.gd' diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index 460c975..e956965 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -245,6 +245,7 @@ func _process_call(dict: Dictionary): _proceed(dict.default) return + # List all args in their correspondent types into an Array. var args: Array = [] for idx: int in dict.args.size(): var arg: String = _parse_variables(dict.args[idx]) if dict.args[idx].count('{{') > 0 else dict.args[idx] @@ -255,7 +256,18 @@ func _process_call(dict: Dictionary): else: # If not String, but empty, parse the default value for argument Type. args.push_back(type_convert('', dict.method.args[idx].type)) - var ret = (load(dict.library) as Script).callv(dict.method.name, args) + # Call method with given Args. Prioritize autoload with matching script, otherwise, call from Script. + var ret = null + if !Engine.is_editor_hint(): + for child: Node in get_tree().root.get_children(): + var script: Script = child.get_script() + if script != null and script.resource_path == dict.library: + ret = child.callv(dict.method.name, args) + break + if ret == null: + ret = (load(dict.library) as Script).callv(dict.method.name, args) + + # Take exit of first matching return, take default exit if none matches. for idx: int in dict.rets: var ret_option = _parse_variables(dict.rets[idx].value) if dict.rets[idx].value.count('{{') > 0 else dict.rets[idx].value if dict.method.return.type != Variant.Type.TYPE_STRING: From c35f53ffc998e41a9902309579139aae1bf6ab08 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Tue, 15 Oct 2024 17:48:03 +0200 Subject: [PATCH 50/52] Fixed a couple bugs related to DialogueParser regarding Autoload calls --- addons/dialogue_nodes/editor/calls.gd | 10 +++++----- addons/dialogue_nodes/objects/DialogueParser.gd | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/addons/dialogue_nodes/editor/calls.gd b/addons/dialogue_nodes/editor/calls.gd index bc293ec..b635e63 100644 --- a/addons/dialogue_nodes/editor/calls.gd +++ b/addons/dialogue_nodes/editor/calls.gd @@ -1,14 +1,14 @@ @tool -extends Script ## ## Default Method Database for CallNodes ## -## Method database for all function calls made from DialogueCalls. -## This script is expected to be expanded by the user, customized to their needs. -## Alternativelly, the user may create new scripts to call methods from. +## Default method library for all function calls made from CallNodes. +## This script is expected to be expanded by the user and customized to their needs. +## Alternativelly, the user may create new [Script] files to call methods from. ## [br][br] ## [color=Yellow]Warning[/color]: [Array] and [Dictionary] arguments [b]cannot[/b] have default values. -## It is recommended they Static Typing is not used to specify their contents either. +## It is recommended that Static Typing is not used to specify their contents either, +## e.g. [code]Array[int][/code]. static func print_text(text: String) -> void: diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index e956965..a717967 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -256,16 +256,24 @@ func _process_call(dict: Dictionary): else: # If not String, but empty, parse the default value for argument Type. args.push_back(type_convert('', dict.method.args[idx].type)) - # Call method with given Args. Prioritize autoload with matching script, otherwise, call from Script. + # Call method. Prioritize calling from matching Autoload, otherwise call from Script instance. + var called: bool = false # Methods that return void assign NULL, so this is needed. var ret = null - if !Engine.is_editor_hint(): + if not Engine.is_editor_hint(): for child: Node in get_tree().root.get_children(): var script: Script = child.get_script() if script != null and script.resource_path == dict.library: ret = child.callv(dict.method.name, args) + called = true break - if ret == null: - ret = (load(dict.library) as Script).callv(dict.method.name, args) + if not called: + if not (dict.method.flags & MethodFlags.METHOD_FLAG_STATIC): + push_warning( + "There's no root child (Autoload) to call non-static method <%s> from! Returning NULL..." + % dict.method.name + ) + else: + ret = load(dict.library).callv(dict.method.name, args) # Take exit of first matching return, take default exit if none matches. for idx: int in dict.rets: From bca7e59e8ab16ddee6c9a984766f7fbdc865ea35 Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Tue, 15 Oct 2024 17:48:33 +0200 Subject: [PATCH 51/52] Included Fork and Call Examples for Demo1 --- examples/Demo1.tscn | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/Demo1.tscn b/examples/Demo1.tscn index 4be745d..a5e068b 100644 --- a/examples/Demo1.tscn +++ b/examples/Demo1.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=19 format=3 uid="uid://bwk7tjlhnu23j"] +[gd_scene load_steps=21 format=3 uid="uid://bwk7tjlhnu23j"] [ext_resource type="Theme" uid="uid://dc6c1peo4pfyr" path="res://addons/dialogue_nodes/themes/bubbly.theme" id="1_qerq3"] [ext_resource type="Script" path="res://examples/demo.gd" id="2_5ddyy"] @@ -10,8 +10,10 @@ [ext_resource type="Resource" uid="uid://b4os1qum2hx0x" path="res://examples/ExampleSignals.tres" id="7_18syg"] [ext_resource type="Resource" uid="uid://cbu4k4hs8fk5s" path="res://examples/ExampleVariables.tres" id="8_4fu3f"] [ext_resource type="Script" path="res://addons/dialogue_nodes/objects/DialogueBox.gd" id="9_s4y1y"] +[ext_resource type="Resource" uid="uid://b6bki2h32l6i3" path="res://examples/ExampleFork.tres" id="10_op8gg"] [ext_resource type="Script" path="res://addons/dialogue_nodes/objects/bbcodeWait.gd" id="10_wwm1y"] [ext_resource type="Script" path="res://addons/dialogue_nodes/objects/bbcodeGhost.gd" id="11_iuh66"] +[ext_resource type="Resource" uid="uid://c2271nfd3mqia" path="res://examples/ExampleCall.tres" id="11_jbl7h"] [ext_resource type="Script" path="res://addons/dialogue_nodes/objects/bbcodeMatrix.gd" id="12_cnev0"] [sub_resource type="Curve" id="1"] @@ -41,7 +43,7 @@ grow_horizontal = 2 grow_vertical = 2 theme = ExtResource("1_qerq3") script = ExtResource("2_5ddyy") -demos = Array[ExtResource("3_vce48")]([ExtResource("3_yex7d"), ExtResource("4_ay03t"), ExtResource("5_ifbb7"), ExtResource("6_arg21"), ExtResource("7_18syg"), ExtResource("8_4fu3f")]) +demos = Array[ExtResource("3_vce48")]([ExtResource("3_yex7d"), ExtResource("4_ay03t"), ExtResource("5_ifbb7"), ExtResource("6_arg21"), ExtResource("7_18syg"), ExtResource("8_4fu3f"), ExtResource("10_op8gg"), ExtResource("11_jbl7h")]) [node name="DemoSelector" type="OptionButton" parent="."] layout_mode = 1 From 4fb76f673a8f2d8f005b67ca0b01ef3b3af3cb6e Mon Sep 17 00:00:00 2001 From: Carles Homs Date: Tue, 15 Oct 2024 17:51:29 +0200 Subject: [PATCH 52/52] Small fix from prev commit --- addons/dialogue_nodes/objects/DialogueParser.gd | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/dialogue_nodes/objects/DialogueParser.gd b/addons/dialogue_nodes/objects/DialogueParser.gd index a717967..197df8d 100644 --- a/addons/dialogue_nodes/objects/DialogueParser.gd +++ b/addons/dialogue_nodes/objects/DialogueParser.gd @@ -274,6 +274,7 @@ func _process_call(dict: Dictionary): ) else: ret = load(dict.library).callv(dict.method.name, args) + called = true # Take exit of first matching return, take default exit if none matches. for idx: int in dict.rets: