1 extends DialogicSubsystem
3 ## Subsystem that manages variables and allows to access them.
5 ## Emitted if a dialogic variable changes, gives a dictionary with the following keys:[br]
7 ## Key | Value Type | Value [br]
8 ## ----------- | ------------- | ----- [br]
9 ## `variable` | [type String] | The name of the variable that is getting changed. [br]
10 ## `new_value` | [type Variant]| The value that [variable] has after the change (the result). [br]
11 signal variable_changed(info:Dictionary)
13 ## Emitted on a set variable event, gives a dictionary with the following keys:[br]
15 ## Key | Value Type | Value [br]
16 ## ----------- | ------------- | ----- [br]
17 ## `variable` | [type String] | The name of the variable that is getting changed. [br]
18 ## `orig_value`| [type Variant]| The value that [variable] had before. [br]
19 ## `new_value` | [type Variant]| The value that [variable] has after the change (the result). [br]
20 ## `value` | [type Variant]| The value that the variable is changed by/to. [br]
21 ## `value_str` | [type String] | Whatever has been given as the value (not interpreted, so a variable is just a string).[br]
22 signal variable_was_set(info:Dictionary)
26 ####################################################################################################
28 func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR):
29 # loading default variables
30 if ! clear_flag & DialogicGameHandler.ClearFlags.KEEP_VARIABLES:
34 func load_game_state(load_flag:=LoadFlags.FULL_LOAD):
35 if load_flag == LoadFlags.ONLY_DNODES:
37 dialogic.current_state_info['variables'] = merge_folder(dialogic.current_state_info['variables'], ProjectSettings.get_setting('dialogic/variables', {}).duplicate(true))
43 ####################################################################################################
45 ## This function will try to get the value of variables provided inside curly brackets
46 ## and replace them with their values.
48 ## - look for the strings to replace
49 ## - search all autoloads
50 ## - try to get the value from context
52 ## So if you provide a string like `Hello, how are you doing {Game.player_name}
53 ## it will try to search for an autoload with the name `Game` and get the value
54 ## of `player_name` to replace it.
55 func parse_variables(text:String) -> String:
56 # First some dirty checks to avoid parsing
60 # Trying to extract the curly brackets from the text
61 var regex := RegEx.new()
62 regex.compile("(?<!\\\\)\\{(?<variable>([^{}]|\\{.*\\})*)\\}")
64 var parsed := text.replace('\\{', '{')
65 for result in regex.search_all(text):
66 var value: Variant = get_variable(result.get_string('variable'), "<NOT FOUND>")
67 parsed = parsed.replace("{"+result.get_string('variable')+"}", str(value))
72 func set_variable(variable_name: String, value: Variant) -> bool:
73 variable_name = variable_name.trim_prefix('{').trim_suffix('}')
75 # First assume this is a simple dialogic variable
76 if has(variable_name):
77 DialogicUtil._set_value_in_dictionary(variable_name, dialogic.current_state_info['variables'], value)
78 variable_changed.emit({'variable':variable_name, 'new_value':value})
81 # Second assume this is an autoload variable
82 elif '.' in variable_name:
83 var from := variable_name.get_slice('.', 0)
84 var variable := variable_name.trim_prefix(from+'.')
86 var autoloads := get_autoloads()
87 var object: Object = null
89 object = autoloads[from]
90 while variable.count("."):
91 from = variable.get_slice('.', 0)
92 if from in object and object.get(from) is Object:
93 object = object.get(from)
94 variable = variable.trim_prefix(from+'.')
99 sub_idx = variable.substr(variable.find('['))
100 variable = variable.trim_suffix(sub_idx)
101 sub_idx = sub_idx.trim_prefix('[').trim_suffix(']')
103 if variable in object:
104 match typeof(object.get(variable)):
107 if typeof(value) == TYPE_ARRAY:
108 object.set(variable, value)
110 elif sub_idx.is_valid_float():
111 object.get(variable).remove_at(int(sub_idx))
112 object.get(variable).insert(int(sub_idx), value)
116 if typeof(value) == TYPE_DICTIONARY:
117 object.set(variable, value)
120 object.get(variable).merge({str_to_var(sub_idx):value}, true)
123 object.set(variable, value)
126 printerr("[Dialogic] Tried setting non-existant variable '"+variable_name+"'.")
130 func get_variable(variable_path:String, default: Variant = null, no_warning := false) -> Variant:
131 if variable_path.begins_with('{') and variable_path.ends_with('}') and variable_path.count('{') == 1:
132 variable_path = variable_path.trim_prefix('{').trim_suffix('}')
134 # First assume this is just a single variable
135 var value: Variant = DialogicUtil._get_value_in_dictionary(variable_path, dialogic.current_state_info['variables'])
139 # Second assume this is an expression.
141 value = dialogic.Expressions.execute_string(variable_path, null, no_warning)
145 # If everything fails, tell the user and return the default
147 printerr("[Dialogic] Failed parsing variable/expression '"+variable_path+"'.")
151 ## Resets all variables or a specific variable to the value(s) defined in the variable editor
152 func reset(variable:="") -> void:
153 if variable.is_empty():
154 dialogic.current_state_info['variables'] = ProjectSettings.get_setting("dialogic/variables", {}).duplicate(true)
156 DialogicUtil._set_value_in_dictionary(variable, dialogic.current_state_info['variables'], DialogicUtil._get_value_in_dictionary(variable, ProjectSettings.get_setting('dialogic/variables', {})))
159 ## Returns true if a variable with the given path exists
160 func has(variable:="") -> bool:
161 return DialogicUtil._get_value_in_dictionary(variable, dialogic.current_state_info['variables']) != null
165 ## Allows to set dialogic built-in variables
166 func _set(property, value) -> bool:
167 property = str(property)
168 var vars: Dictionary = dialogic.current_state_info['variables']
169 if property in vars.keys():
170 if typeof(vars[property]) != TYPE_DICTIONARY:
171 vars[property] = value
173 if value is VariableFolder:
178 ## Allows to get dialogic built-in variables
180 property = str(property)
181 if property in dialogic.current_state_info['variables'].keys():
182 if typeof(dialogic.current_state_info['variables'][property]) == TYPE_DICTIONARY:
183 return VariableFolder.new(dialogic.current_state_info['variables'][property], property, self)
185 return DialogicUtil.logical_convert(dialogic.current_state_info['variables'][property])
188 func folders() -> Array:
190 for i in dialogic.current_state_info['variables'].keys():
191 if dialogic.current_state_info['variables'][i] is Dictionary:
192 result.append(VariableFolder.new(dialogic.current_state_info['variables'][i], i, self))
196 func variables(_absolute:=false) -> Array:
198 for i in dialogic.current_state_info['variables'].keys():
199 if not dialogic.current_state_info['variables'][i] is Dictionary:
205 ################################################################################
207 func get_autoloads() -> Dictionary:
209 for node: Node in get_tree().root.get_children():
210 autoloads[node.name] = node
214 func merge_folder(new:Dictionary, defs:Dictionary) -> Dictionary:
215 # also go through all groups in this folder
217 if x in defs and typeof(new[x]) == TYPE_DICTIONARY:
218 new[x] = merge_folder(new[x], defs[x])
219 # add all new variables
220 for x in defs.keys():
227 #region VARIABLE FOLDER
228 ################################################################################
229 class VariableFolder:
232 var outside: DialogicSubsystem
234 func _init(_data:Dictionary, _path:String, _outside:DialogicSubsystem):
240 func _get(property:StringName):
241 property = str(property)
243 if typeof(data[property]) == TYPE_DICTIONARY:
244 return VariableFolder.new(data[property], path+"."+property, outside)
246 return DialogicUtil.logical_convert(data[property])
249 func _set(property:StringName, value:Variant) -> bool:
250 property = str(property)
251 if not value is VariableFolder:
252 DialogicUtil._set_value_in_dictionary(path+"."+property, outside.dialogic.current_state_info['variables'], value)
256 func has(key:String) -> bool:
260 func folders() -> Array:
262 for i in data.keys():
263 if data[i] is Dictionary:
264 result.append(VariableFolder.new(data[i], path+"."+i, outside))
268 func variables(absolute:=false) -> Array:
270 for i in data.keys():
271 if not data[i] is Dictionary:
273 result.append(path+'.'+i)