]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Modules/Call/event_call.gd
Adding import files
[wolf-seeking-sheep.git] / addons / dialogic / Modules / Call / event_call.gd
1 @tool
2 class_name DialogicCallEvent
3 extends DialogicEvent
4
5 ## Event that allows calling a method in a node or autoload.
6
7 ### Settings
8
9 ## The name of the autoload to call the method on.
10 var autoload_name := ""
11 ## The name of the method to call on the given autoload.
12 var method := "":
13         set(value):
14                 method = value
15                 if Engine.is_editor_hint():
16                         update_argument_info()
17                         check_arguments_and_update_warning()
18 ## A list of arguments to give to the call.
19 var arguments := []:
20         set(value):
21                 arguments = value
22                 if Engine.is_editor_hint():
23                         check_arguments_and_update_warning()
24
25 var _current_method_arg_hints := {'a':null, 'm':null, 'info':{}}
26
27 ################################################################################
28 ##                                              EXECUTION
29 ################################################################################
30
31 func _execute() -> void:
32         var object: Object = null
33         var obj_path := autoload_name
34         var autoload: Node = dialogic.get_node('/root/'+obj_path.get_slice('.', 0))
35         obj_path = obj_path.trim_prefix(obj_path.get_slice('.', 0)+'.')
36         object = autoload
37         if object:
38                 while obj_path:
39                         if obj_path.get_slice(".", 0) in object and object.get(obj_path.get_slice(".", 0)) is Object:
40                                 object = object.get(obj_path.get_slice(".", 0))
41                         else:
42                                 break
43                         obj_path = obj_path.trim_prefix(obj_path.get_slice('.', 0)+'.')
44
45         if object == null:
46                 printerr("[Dialogic] Call event failed: Unable to find autoload '",autoload_name,"'")
47                 finish()
48                 return
49
50         if object.has_method(method):
51                 var args := []
52                 for arg in arguments:
53                         if arg is String and arg.begins_with('@'):
54                                 args.append(dialogic.Expressions.execute_string(arg.trim_prefix('@')))
55                         else:
56                                 args.append(arg)
57                 dialogic.current_state = dialogic.States.WAITING
58                 await object.callv(method, args)
59                 dialogic.current_state = dialogic.States.IDLE
60         else:
61                 printerr("[Dialogic] Call event failed: Autoload doesn't have the method '", method,"'.")
62
63         finish()
64
65
66 ################################################################################
67 ##                                              INITIALIZE
68 ################################################################################
69
70 func _init() -> void:
71         event_name = "Call"
72         set_default_color('Color6')
73         event_category = "Logic"
74         event_sorting_index = 10
75
76
77 ################################################################################
78 ##                                              SAVING/LOADING
79 ################################################################################
80
81 func to_text() -> String:
82         var result := "do "
83         if autoload_name:
84                 result += autoload_name
85                 if method:
86                         result += '.'+method
87                         if arguments.is_empty():
88                                 result += '()'
89                         else:
90                                 result += '('
91                                 for i in arguments:
92                                         if i is String and i.begins_with('@'):
93                                                 result += i.trim_prefix('@')
94                                         else:
95                                                 result += var_to_str(i)
96                                         result += ', '
97                                 result = result.trim_suffix(', ')+')'
98         return result
99
100
101 func from_text(string:String) -> void:
102         var result := RegEx.create_from_string(r"do (?<autoload>[^\(]*)\.((?<method>[^.(]*)(\((?<arguments>.*)\))?)?").search(string.strip_edges())
103         if result:
104                 autoload_name = result.get_string('autoload')
105                 method = result.get_string('method')
106                 if result.get_string('arguments').is_empty():
107                         arguments = []
108                 else:
109                         var arr := []
110                         for i in result.get_string('arguments').split(','):
111                                 i = i.strip_edges()
112                                 if str_to_var(i) != null:
113                                         arr.append(str_to_var(i))
114                                 else:
115                                         # Mark this as a complex expression
116                                         arr.append("@"+i)
117                         arguments = arr
118
119
120 func is_valid_event(string:String) -> bool:
121         if string.strip_edges().begins_with("do"):
122                 return true
123         return false
124
125
126 func get_shortcode_parameters() -> Dictionary:
127         return {
128                 #param_name : property_info
129                 "autoload"      : {"property": "autoload_name",         "default": ""},
130                 "method"        : {"property": "method",                        "default": ""},
131                 "args"          : {"property": "arguments",             "default": []},
132         }
133
134
135 ################################################################################
136 ##                                              EDITOR REPRESENTATION
137 ################################################################################
138
139 func build_event_editor() -> void:
140         add_header_edit('autoload_name', ValueType.DYNAMIC_OPTIONS, {'left_text':'On autoload',
141                 'empty_text':'Autoload',
142                 'suggestions_func':get_autoload_suggestions,
143                 'editor_icon':["Node", "EditorIcons"]})
144         add_header_edit('method', ValueType.DYNAMIC_OPTIONS, {'left_text':'call',
145                 'empty_text':'Method',
146                 'suggestions_func':get_method_suggestions,
147                 'editor_icon':["Callable", "EditorIcons"]}, 'autoload_name')
148         add_body_edit('arguments', ValueType.ARRAY, {'left_text':'Arguments:'}, 'not autoload_name.is_empty() and not method.is_empty()')
149
150
151
152 func get_autoload_suggestions(filter:String="") -> Dictionary:
153         var suggestions := {}
154
155         for prop in ProjectSettings.get_property_list():
156                 if prop.name.begins_with('autoload/'):
157                         var autoload: String = prop.name.trim_prefix('autoload/')
158                         suggestions[autoload] = {'value': autoload, 'tooltip':autoload, 'editor_icon': ["Node", "EditorIcons"]}
159                         if filter.begins_with(autoload):
160                                 suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]}
161         return suggestions
162
163
164 func get_method_suggestions(filter:String="", temp_autoload:String = "") -> Dictionary:
165         var suggestions := {}
166
167         var script: Script
168         if temp_autoload and ProjectSettings.has_setting('autoload/'+temp_autoload):
169                 script = load(ProjectSettings.get_setting('autoload/'+temp_autoload).trim_prefix('*'))
170
171         elif autoload_name and ProjectSettings.has_setting('autoload/'+autoload_name):
172                 var loaded_autoload := load(ProjectSettings.get_setting('autoload/'+autoload_name).trim_prefix('*'))
173
174                 if loaded_autoload is PackedScene:
175                         var packed_scene: PackedScene = loaded_autoload
176                         script = packed_scene.instantiate().get_script()
177
178                 else:
179                         script = loaded_autoload
180
181         if script:
182                 for script_method in script.get_script_method_list():
183                         if script_method.name.begins_with('@') or script_method.name.begins_with('_'):
184                                 continue
185                         suggestions[script_method.name] = {'value': script_method.name, 'tooltip':script_method.name, 'editor_icon': ["Callable", "EditorIcons"]}
186         if !filter.is_empty():
187                 suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]}
188         return suggestions
189
190
191 func update_argument_info() -> void:
192         if autoload_name and method and not _current_method_arg_hints.is_empty() and (_current_method_arg_hints.a == autoload_name and _current_method_arg_hints.m == method):
193                 if !ResourceLoader.exists(ProjectSettings.get_setting('autoload/'+autoload_name, '').trim_prefix('*')):
194                         _current_method_arg_hints = {}
195                         return
196                 var script: Script = load(ProjectSettings.get_setting('autoload/'+autoload_name, '').trim_prefix('*'))
197                 for m in script.get_script_method_list():
198                         if m.name == method:
199                                 _current_method_arg_hints = {'a':autoload_name, 'm':method, 'info':m}
200                                 break
201
202
203 func check_arguments_and_update_warning() -> void:
204         if not _current_method_arg_hints.has("info") or _current_method_arg_hints.info.is_empty():
205                 ui_update_warning.emit()
206                 return
207
208         var idx := -1
209         for arg in arguments:
210                 idx += 1
211                 if len(_current_method_arg_hints.info.args) <= idx:
212                         continue
213                 if _current_method_arg_hints.info.args[idx].type != 0:
214                         if _current_method_arg_hints.info.args[idx].type != typeof(arg):
215                                 if arg is String and arg.begins_with('@'):
216                                         continue
217                                 var expected_type: String = ""
218                                 match _current_method_arg_hints.info.args[idx].type:
219                                         TYPE_BOOL:              expected_type = "bool"
220                                         TYPE_STRING:    expected_type = "string"
221                                         TYPE_FLOAT:     expected_type = "float"
222                                         TYPE_INT:               expected_type = "int"
223                                         _:                              expected_type = "something else"
224
225                                 ui_update_warning.emit('Argument '+ str(idx+1)+ ' ('+_current_method_arg_hints.info.args[idx].name+') has the wrong type (method expects '+expected_type+')!')
226                                 return
227
228         if len(arguments) < len(_current_method_arg_hints.info.args)-len(_current_method_arg_hints.info.default_args):
229                 ui_update_warning.emit("The method is expecting at least "+str(len(_current_method_arg_hints.info.args)-len(_current_method_arg_hints.info.default_args))+ " arguments, but is given only "+str(len(arguments))+".")
230                 return
231         elif len(arguments) > len(_current_method_arg_hints.info.args):
232                 ui_update_warning.emit("The method is expecting at most "+str(len(_current_method_arg_hints.info.args))+ " arguments, but is given "+str(len(arguments))+".")
233                 return
234         ui_update_warning.emit()
235
236 ####################### CODE COMPLETION ########################################
237 ################################################################################
238
239 func _get_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void:
240         if line.count(' ') == 1 and not '.' in line:
241                 for i in get_autoload_suggestions():
242                         TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'.', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("Node", "EditorIcons"))
243         elif symbol == '.' and not '(' in line:
244                 for i in get_method_suggestions('', line.get_slice('.', 0).trim_prefix('do ')):
245                         TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'(', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("Callable", "EditorIcons"))
246
247
248 func _get_start_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit) -> void:
249         TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'do', 'do ', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), _get_icon())
250
251
252 #################### SYNTAX HIGHLIGHTING #######################################
253 ################################################################################
254
255 func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary:
256         dict[line.find('do')] = {"color":event_color.lerp(Highlighter.normal_color, 0.3)}
257         dict[line.find('do')+2] = {"color":event_color.lerp(Highlighter.normal_color, 0.5)}
258
259         Highlighter.color_region(dict, Highlighter.normal_color, line, '(', ')')
260         Highlighter.color_region(dict, Highlighter.string_color, line, '"', '"')
261         Highlighter.color_word(dict, Highlighter.boolean_operator_color, line, 'true')
262         Highlighter.color_word(dict, Highlighter.boolean_operator_color, line, 'false')
263         return dict