diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.gd b/addons/dialogic/Editor/Events/EventBlock/event_block.gd index bb6c3d50e..b21f4c642 100644 --- a/addons/dialogic/Editor/Events/EventBlock/event_block.gd +++ b/addons/dialogic/Editor/Events/EventBlock/event_block.gd @@ -249,7 +249,16 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: # Some things need to be called AFTER the field is added to the tree if editor_node is DialogicVisualEditorField: - editor_node._set_value(resource.get(p.name)) + # Only set the value if the field is visible + # + # This prevents events with varied value types (event_setting, event_variable) + # from injecting incorrect types into hidden fields, which then throw errors + # in the console. + if p.has('condition') and not p.condition.is_empty(): + if _evaluate_visibility_condition(p): + editor_node._set_value(resource.get(p.name)) + else: + editor_node._set_value(resource.get(p.name)) editor_node.value_changed.connect(set_property) @@ -305,9 +314,7 @@ func recalculate_field_visibility() -> void: if p.location == 1: has_any_enabled_body_content = true else: - var expr := Expression.new() - expr.parse(p.condition) - if expr.execute([], resource): + if _evaluate_visibility_condition(p): if p.node != null: p.node.show() if p.location == 1: @@ -315,8 +322,6 @@ func recalculate_field_visibility() -> void: else: if p.node != null: p.node.hide() - if expr.has_execute_failed(): - printerr("[Dialogic] Failed executing visibility condition for '",p.get('property', 'unnamed'),"': " + expr.get_error_text()) %ExpandButton.visible = has_any_enabled_body_content @@ -327,10 +332,32 @@ func set_property(property_name:String, value:Variant) -> void: end_node.parent_node_changed() +func _evaluate_visibility_condition(p: Dictionary) -> bool: + var expr := Expression.new() + expr.parse(p.condition) + var result: bool + if expr.execute([], resource): + result = true + else: + result = false + if expr.has_execute_failed(): + printerr("[Dialogic] Failed executing visibility condition for '",p.get('property', 'unnamed'),"': " + expr.get_error_text()) + return result + + func _on_resource_ui_update_needed() -> void: for node_info in field_list: if node_info.node and node_info.node.has_method('set_value'): - node_info.node.set_value(resource.get(node_info.property)) + # Only set the value if the field is visible + # + # This prevents events with varied value types (event_setting, event_variable) + # from injecting incorrect types into hidden fields, which then throw errors + # in the console. + if node_info.has('condition') and not node_info.condition.is_empty(): + if _evaluate_visibility_condition(node_info): + node_info.node.set_value(resource.get(node_info.property)) + else: + node_info.node.set_value(resource.get(node_info.property)) recalculate_field_visibility() diff --git a/addons/dialogic/Modules/Settings/event_setting.gd b/addons/dialogic/Modules/Settings/event_setting.gd index 2005df734..7c808b678 100644 --- a/addons/dialogic/Modules/Settings/event_setting.gd +++ b/addons/dialogic/Modules/Settings/event_setting.gd @@ -8,14 +8,36 @@ extends DialogicEvent ### Settings enum Modes {SET, RESET, RESET_ALL} +enum SettingValueType { + STRING, + NUMBER, + VARIABLE, + EXPRESSION +} ## The name of the setting to save to. var name: String = "" -var _value_type := 0 +var _value_type := 0 : + get: + return _value_type + set(_value): + _value_type = _value + if not _suppress_default_value: + match _value_type: + SettingValueType.STRING, SettingValueType.VARIABLE, SettingValueType.EXPRESSION: + value = "" + SettingValueType.NUMBER: + value = 0 + ui_update_needed.emit() + var value: Variant = "" var mode := Modes.SET +## Used to suppress _value_type from overwriting value with a default value when the type changes +## This is only used when initializing the event_variable. +var _suppress_default_value: bool = false + ################################################################################ ## INITIALIZE ################################################################################ @@ -28,14 +50,14 @@ func _execute() -> void: dialogic.Settings.reset_all() else: match _value_type: - 0: + SettingValueType.STRING: dialogic.Settings.set(name, value) - 1: + SettingValueType.NUMBER: dialogic.Settings.set(name, float(value)) - 2: + SettingValueType.VARIABLE: if dialogic.has_subsystem('VAR'): dialogic.Settings.set(name, dialogic.VAR.get_variable('{'+value+'}')) - 3: + SettingValueType.EXPRESSION: if dialogic.has_subsystem('VAR'): dialogic.Settings.set(name, dialogic.VAR.get_variable(value)) finish() @@ -72,11 +94,11 @@ func to_text() -> String: string += " = " value = str(value) match _value_type: - 0: # String + SettingValueType.STRING: # String string += '"'+value.replace('"', '\\"')+'"' - 1,3: # Float or Expression + SettingValueType.NUMBER,SettingValueType.EXPRESSION: # Float or Expression string += str(value) - 2: # Variable + SettingValueType.VARIABLE: # Variable string += '{'+value+'}' return string @@ -98,19 +120,21 @@ func from_text(string:String) -> void: mode = Modes.RESET_ALL if result.get_string('value'): + _suppress_default_value = true value = result.get_string('value').strip_edges() if value.begins_with('"') and value.ends_with('"') and value.count('"')-value.count('\\"') == 2: value = result.get_string('value').strip_edges().replace('"', '') - _value_type = 0 + _value_type = SettingValueType.STRING elif value.begins_with('{') and value.ends_with('}') and value.count('{') == 1: value = result.get_string('value').strip_edges().trim_suffix('}').trim_prefix('{') - _value_type = 2 + _value_type = SettingValueType.VARIABLE else: value = result.get_string('value').strip_edges() if value.is_valid_float(): - _value_type = 1 + _value_type = SettingValueType.NUMBER else: - _value_type = 3 + _value_type = SettingValueType.EXPRESSION + _suppress_default_value = false func is_valid_event(string:String) -> bool: @@ -138,33 +162,33 @@ func build_event_editor(): }, ]}) - add_header_edit('name', ValueType.DYNAMIC_OPTIONS, {'placeholder':'Type setting', 'suggestions_func':get_settings_suggestions}, 'mode != 2') + add_header_edit('name', ValueType.DYNAMIC_OPTIONS, {'placeholder':'Type setting', 'suggestions_func':get_settings_suggestions}, 'mode != Modes.RESET_ALL') add_header_edit('_value_type', ValueType.FIXED_OPTIONS, {'left_text':'to', 'options': [ { 'label': 'String', 'icon': ["String", "EditorIcons"], - 'value': 0 + 'value': SettingValueType.STRING },{ 'label': 'Number', 'icon': ["float", "EditorIcons"], - 'value': 1 + 'value': SettingValueType.NUMBER },{ 'label': 'Variable', 'icon': ["ClassList", "EditorIcons"], - 'value': 2 + 'value': SettingValueType.VARIABLE },{ 'label': 'Expression', 'icon': ["Variant", "EditorIcons"], - 'value': 3 + 'value': SettingValueType.EXPRESSION }], 'symbol_only':true}, - '!name.is_empty() and mode == 0') - add_header_edit('value', ValueType.SINGLELINE_TEXT, {}, '!name.is_empty() and (_value_type == 0 or _value_type == 3) and mode == 0') - add_header_edit('value', ValueType.NUMBER, {}, '!name.is_empty() and _value_type == 1 and mode == 0') + '!name.is_empty() and mode == Modes.SET') + add_header_edit('value', ValueType.SINGLELINE_TEXT, {}, '!name.is_empty() and (_value_type == SettingValueType.STRING or _value_type == SettingValueType.EXPRESSION) and mode == Modes.SET') + add_header_edit('value', ValueType.NUMBER, {}, '!name.is_empty() and _value_type == SettingValueType.NUMBER and mode == Modes.SET') add_header_edit('value', ValueType.DYNAMIC_OPTIONS, {'suggestions_func' : get_value_suggestions, 'placeholder':'Select Variable'}, - '!name.is_empty() and _value_type == 2 and mode == 0') + '!name.is_empty() and _value_type == SettingValueType.VARIABLE and mode == Modes.SET') func get_settings_suggestions(filter:String) -> Dictionary: diff --git a/addons/dialogic/Modules/Variable/event_variable.gd b/addons/dialogic/Modules/Variable/event_variable.gd index 3f6a9610d..5e28b78ee 100644 --- a/addons/dialogic/Modules/Variable/event_variable.gd +++ b/addons/dialogic/Modules/Variable/event_variable.gd @@ -6,6 +6,14 @@ extends DialogicEvent enum Operations {SET, ADD, SUBSTRACT, MULTIPLY, DIVIDE} +enum VarValueType { + STRING = 0, + NUMBER = 1, + VARIABLE = 2, + BOOL = 3, + EXPRESSION = 4, + RANDOM_NUMBER = 5, +} ## Settings @@ -16,33 +24,48 @@ var name: String = "": if Engine.is_editor_hint() and not value: match DialogicUtil.get_variable_type(name): DialogicUtil.VarTypes.ANY, DialogicUtil.VarTypes.STRING: - _value_type = 0 + _value_type = VarValueType.STRING DialogicUtil.VarTypes.FLOAT, DialogicUtil.VarTypes.INT: - _value_type = 1 + _value_type = VarValueType.NUMBER DialogicUtil.VarTypes.BOOL: - _value_type = 3 + _value_type = VarValueType.BOOL ui_update_needed.emit() update_editor_warning() ## The operation to perform. var operation: int = Operations.SET: set(value): operation = value - if operation != Operations.SET and _value_type == 0: - _value_type = 1 + if operation != Operations.SET and _value_type == VarValueType.STRING: + _value_type = VarValueType.NUMBER ui_update_needed.emit() update_editor_warning() ## The value that is used. Can be a variable as well. var value: Variant = "" var _value_type := 0 :# helper for the ui 0 = string, 1= float, 2= variable 3=bool, 4= expression, 5= random int (a special expression) - set(value): - _value_type = value + set(_value): + _value_type = _value + if not _suppress_default_value: + match _value_type: + VarValueType.STRING, VarValueType.VARIABLE, VarValueType.EXPRESSION: + value = "" + VarValueType.NUMBER: + value = 0 + VarValueType.BOOL: + value = false + VarValueType.RANDOM_NUMBER: + value = null + ui_update_needed.emit() update_editor_warning() ## If true, a random number between [random_min] and [random_max] is used instead of [value]. var random_min: int = 0 var random_max: int = 100 +## Used to suppress _value_type from overwriting value with a default value when the type changes +## This is only used when initializing the event_variable. +var _suppress_default_value: bool = false + ################################################################################ ## EXECUTE @@ -54,9 +77,9 @@ func _execute() -> void: if value and orig != null: var the_value :Variant match _value_type: - 0: the_value = dialogic.VAR.get_variable('"'+value+'"') - 2: the_value = dialogic.VAR.get_variable('{'+value+'}') - 1,3,4: the_value = dialogic.VAR.get_variable(str(value)) + VarValueType.STRING: the_value = dialogic.VAR.get_variable('"'+value+'"') + VarValueType.VARIABLE: the_value = dialogic.VAR.get_variable('{'+value+'}') + VarValueType.NUMBER,VarValueType.BOOL,VarValueType.EXPRESSION,VarValueType.RANDOM_NUMBER: the_value = dialogic.VAR.get_variable(str(value)) if operation != Operations.SET and str(orig).is_valid_float() and str(the_value).is_valid_float(): orig = float(orig) @@ -115,13 +138,13 @@ func to_text() -> String: value = str(value) match _value_type: - 0: # String + VarValueType.STRING: # String string += '"'+value.replace('"', '\\"')+'"' - 1,3,4: # Float Bool, or Expression + VarValueType.NUMBER,VarValueType.BOOL,VarValueType.EXPRESSION: # Float Bool, or Expression string += str(value) - 2: # Variable + VarValueType.VARIABLE: # Variable string += '{'+value+'}' - 5: + VarValueType.RANDOM_NUMBER: string += 'range('+str(random_min)+','+str(random_max)+').pick_random()' return string @@ -147,27 +170,29 @@ func from_text(string:String) -> void: operation = Operations.DIVIDE if result.get_string('value'): + _suppress_default_value = true value = result.get_string('value').strip_edges() if value.begins_with('"') and value.ends_with('"') and value.count('"')-value.count('\\"') == 2: value = result.get_string('value').strip_edges().replace('"', '') - _value_type = 0 + _value_type = VarValueType.STRING elif value.begins_with('{') and value.ends_with('}') and value.count('{') == 1: value = result.get_string('value').strip_edges().trim_suffix('}').trim_prefix('{') - _value_type = 2 + _value_type = VarValueType.VARIABLE elif value in ["true", "false"]: value = value == "true" - _value_type = 3 + _value_type = VarValueType.BOOL elif value.begins_with('range(') and value.ends_with(').pick_random()'): - _value_type = 5 + _value_type = VarValueType.RANDOM_NUMBER var randinf := str(value).trim_prefix('range(').trim_suffix(').pick_random()').split(',') random_min = int(randinf[0]) random_max = int(randinf[1]) else: value = result.get_string('value').strip_edges() if value.is_valid_float(): - _value_type = 1 + _value_type = VarValueType.NUMBER else: - _value_type = 4 + _value_type = VarValueType.EXPRESSION + _suppress_default_value = false @@ -216,39 +241,39 @@ func build_event_editor(): { 'label': 'String', 'icon': ["String", "EditorIcons"], - 'value': 0 + 'value': VarValueType.STRING },{ 'label': 'Number', 'icon': ["float", "EditorIcons"], - 'value': 1 + 'value': VarValueType.NUMBER },{ 'label': 'Variable', 'icon': load("res://addons/dialogic/Editor/Images/Pieces/variable.svg"), - 'value': 2 + 'value': VarValueType.VARIABLE },{ 'label': 'Bool', 'icon': ["bool", "EditorIcons"], - 'value': 3 + 'value': VarValueType.BOOL },{ 'label': 'Expression', 'icon': ["Variant", "EditorIcons"], - 'value': 4 + 'value': VarValueType.EXPRESSION },{ 'label': 'Random Number', 'icon': ["RandomNumberGenerator", "EditorIcons"], - 'value': 5 + 'value': VarValueType.RANDOM_NUMBER }], 'symbol_only':true}, '!name.is_empty()') - add_header_edit('value', ValueType.SINGLELINE_TEXT, {}, '!name.is_empty() and (_value_type == 0 or _value_type == 4) ') - add_header_edit('value', ValueType.BOOL, {}, '!name.is_empty() and (_value_type == 3) ') - add_header_edit('value', ValueType.NUMBER, {}, '!name.is_empty() and _value_type == 1') + add_header_edit('value', ValueType.SINGLELINE_TEXT, {}, '!name.is_empty() and (_value_type == VarValueType.STRING or _value_type == VarValueType.EXPRESSION) ') + add_header_edit('value', ValueType.BOOL, {}, '!name.is_empty() and (_value_type == VarValueType.BOOL) ') + add_header_edit('value', ValueType.NUMBER, {}, '!name.is_empty() and _value_type == VarValueType.NUMBER') add_header_edit('value', ValueType.DYNAMIC_OPTIONS, {'suggestions_func' : get_value_suggestions, 'placeholder':'Select Variable'}, - '!name.is_empty() and _value_type == 2') - add_header_label('a number between', '_value_type == 5') - add_header_edit('random_min', ValueType.NUMBER, {'right_text':'and', 'mode':1}, '!name.is_empty() and _value_type == 5') - add_header_edit('random_max', ValueType.NUMBER, {'mode':1}, '!name.is_empty() and _value_type == 5') + '!name.is_empty() and _value_type == VarValueType.VARIABLE') + add_header_label('a number between', '_value_type == VarValueType.RANDOM_NUMBER') + add_header_edit('random_min', ValueType.NUMBER, {'right_text':'and', 'mode':1}, '!name.is_empty() and _value_type == VarValueType.RANDOM_NUMBER') + add_header_edit('random_max', ValueType.NUMBER, {'mode':1}, '!name.is_empty() and _value_type == VarValueType.RANDOM_NUMBER') add_header_button('', _on_variable_editor_pressed, 'Variable Editor', ["ExternalLink", "EditorIcons"]) @@ -276,7 +301,7 @@ func _on_variable_editor_pressed(): func update_editor_warning() -> void: - if _value_type == 0 and operation != Operations.SET: + if _value_type == VarValueType.STRING and operation != Operations.SET: ui_update_warning.emit('You cannot do this operation with a string!') elif operation != Operations.SET: var type := DialogicUtil.get_variable_type(name)