You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
13 KiB

6 months ago
@tool
class_name DialogicVariableEvent
extends DialogicEvent
## Event that allows changing a dialogic variable or a property of an autoload.
enum Operations {SET, ADD, SUBSTRACT, MULTIPLY, DIVIDE}
enum VarValueType {
STRING = 0,
NUMBER = 1,
VARIABLE = 2,
BOOL = 3,
EXPRESSION = 4,
RANDOM_NUMBER = 5,
}
## Settings
## Name/Path of the variable that should be changed.
var name: String = "":
set(_value):
name = _value
if Engine.is_editor_hint() and not value:
match DialogicUtil.get_variable_type(name):
DialogicUtil.VarTypes.ANY, DialogicUtil.VarTypes.STRING:
_value_type = VarValueType.STRING
DialogicUtil.VarTypes.FLOAT, DialogicUtil.VarTypes.INT:
_value_type = VarValueType.NUMBER
DialogicUtil.VarTypes.BOOL:
_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 == 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 :
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
################################################################################
func _execute() -> void:
if name:
var original_value: Variant = dialogic.VAR.get_variable(name, null, operation == Operations.SET and "[" in name)
if value != null and (original_value != null or (operation == Operations.SET and "[" in name)):
var interpreted_value: Variant
var result: Variant
match _value_type:
VarValueType.STRING:
interpreted_value = dialogic.VAR.get_variable('"' + value + '"')
VarValueType.VARIABLE:
interpreted_value = dialogic.VAR.get_variable('{' + value + '}')
VarValueType.NUMBER, VarValueType.BOOL, VarValueType.EXPRESSION, VarValueType.RANDOM_NUMBER:
interpreted_value = dialogic.VAR.get_variable(str(value))
if operation != Operations.SET and (not str(original_value).is_valid_float() or not str(interpreted_value).is_valid_float()):
printerr("[Dialogic] Set Variable event failed because one value wasn't a float! [", original_value, ", ",interpreted_value,"]")
finish()
return
if operation == Operations.SET:
result = interpreted_value
else:
original_value = float(original_value)
interpreted_value = float(interpreted_value)
match operation:
Operations.ADD:
result = original_value + interpreted_value
Operations.SUBSTRACT:
result = original_value - interpreted_value
Operations.MULTIPLY:
result = original_value * interpreted_value
Operations.DIVIDE:
result = original_value / interpreted_value
dialogic.VAR.set_variable(name, result)
dialogic.VAR.variable_was_set.emit(
{
'variable' : name,
'value' : interpreted_value,
'value_str' : value,
'orig_value' : original_value,
'new_value' : result,
})
else:
printerr("[Dialogic] Set Variable event failed because one value wasn't set!")
finish()
################################################################################
## INITIALIZE
################################################################################
func _init() -> void:
event_name = "Set Variable"
set_default_color('Color6')
event_category = "Logic"
event_sorting_index = 0
help_page_path = "https://docs.dialogic.pro/variables.html#23-set-variable-event"
################################################################################
## SAVING/LOADING
################################################################################
func to_text() -> String:
var string := "set "
if name:
string += "{" + name.trim_prefix('{').trim_suffix('}') + "}"
match operation:
Operations.SET:
string+= " = "
Operations.ADD:
string+= " += "
Operations.SUBSTRACT:
string+= " -= "
Operations.MULTIPLY:
string+= " *= "
Operations.DIVIDE:
string+= " /= "
value = str(value)
match _value_type:
VarValueType.STRING: # String
string += '"'+value.replace('"', '\\"')+'"'
VarValueType.NUMBER,VarValueType.BOOL,VarValueType.EXPRESSION: # Float Bool, or Expression
string += str(value)
VarValueType.VARIABLE: # Variable
string += '{'+value+'}'
VarValueType.RANDOM_NUMBER:
string += 'range('+str(random_min)+','+str(random_max)+').pick_random()'
return string
func from_text(string:String) -> void:
var reg := RegEx.new()
reg.compile("set(?<name>[^=+\\-*\\/]*)?(?<operation>=|\\+=|-=|\\*=|\\/=)?(?<value>.*)")
var result := reg.search(string)
if !result:
return
name = result.get_string('name').strip_edges().replace("{", "").replace("}", "")
match result.get_string('operation').strip_edges():
'=':
operation = Operations.SET
'-=':
operation = Operations.SUBSTRACT
'+=':
operation = Operations.ADD
'*=':
operation = Operations.MULTIPLY
'/=':
operation = Operations.DIVIDE
_suppress_default_value = true
value = result.get_string('value').strip_edges()
if not value.is_empty():
if value.begins_with('"') and value.ends_with('"') and value.count('"')-value.count('\\"') == 2:
value = result.get_string('value').strip_edges().replace('"', '')
_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 = VarValueType.VARIABLE
elif value in ["true", "false"]:
value = value == "true"
_value_type = VarValueType.BOOL
elif value.begins_with('range(') and value.ends_with(').pick_random()'):
_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 = VarValueType.NUMBER
else:
_value_type = VarValueType.EXPRESSION
else:
value = null
_suppress_default_value = false
func is_valid_event(string:String) -> bool:
return string.begins_with('set')
################################################################################
## EDITOR REPRESENTATION
################################################################################
func build_event_editor():
add_header_edit('name', ValueType.DYNAMIC_OPTIONS, {
'left_text' : 'Set',
'suggestions_func' : get_var_suggestions,
'icon' : load("res://addons/dialogic/Editor/Images/Pieces/variable.svg"),
'placeholder' :'Select Variable'}
)
add_header_edit('operation', ValueType.FIXED_OPTIONS, {
'options': [
{
'label': 'to be',
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/set.svg"),
'value': Operations.SET
},{
'label': 'to itself plus',
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/plus.svg"),
'value': Operations.ADD
},{
'label': 'to itself minus',
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/minus.svg"),
'value': Operations.SUBSTRACT
},{
'label': 'to itself multiplied by',
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/multiply.svg"),
'value': Operations.MULTIPLY
},{
'label': 'to itself divided by',
'icon': load("res://addons/dialogic/Editor/Images/Dropdown/divide.svg"),
'value': Operations.DIVIDE
}
]
}, '!name.is_empty()')
add_header_edit('_value_type', ValueType.FIXED_OPTIONS, {
'options': [
{
'label': 'String',
'icon': ["String", "EditorIcons"],
'value': VarValueType.STRING
},{
'label': 'Number',
'icon': ["float", "EditorIcons"],
'value': VarValueType.NUMBER
},{
'label': 'Variable',
'icon': load("res://addons/dialogic/Editor/Images/Pieces/variable.svg"),
'value': VarValueType.VARIABLE
},{
'label': 'Bool',
'icon': ["bool", "EditorIcons"],
'value': VarValueType.BOOL
},{
'label': 'Expression',
'icon': ["Variant", "EditorIcons"],
'value': VarValueType.EXPRESSION
},{
'label': 'Random Number',
'icon': ["RandomNumberGenerator", "EditorIcons"],
'value': VarValueType.RANDOM_NUMBER
}],
'symbol_only':true},
'!name.is_empty()')
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 == 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"])
func get_var_suggestions(filter:String) -> Dictionary:
var suggestions := {}
if filter:
suggestions[filter] = {'value':filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]}
for var_path in DialogicUtil.list_variables(DialogicUtil.get_default_variables()):
suggestions[var_path] = {'value':var_path, 'icon':load("res://addons/dialogic/Editor/Images/Pieces/variable.svg")}
return suggestions
func get_value_suggestions(filter:String) -> Dictionary:
var suggestions := {}
for var_path in DialogicUtil.list_variables(DialogicUtil.get_default_variables()):
suggestions[var_path] = {'value':var_path, 'icon':load("res://addons/dialogic/Editor/Images/Pieces/variable.svg")}
return suggestions
func _on_variable_editor_pressed():
var editor_manager := _editor_node.find_parent('EditorsManager')
if editor_manager:
editor_manager.open_editor(editor_manager.editors['VariablesEditor']['node'], true)
func update_editor_warning() -> void:
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)
if not type in [DialogicUtil.VarTypes.INT, DialogicUtil.VarTypes.FLOAT, DialogicUtil.VarTypes.ANY]:
ui_update_warning.emit('The selected variable is not a number!')
else:
ui_update_warning.emit('')
else:
ui_update_warning.emit('')
####################### CODE COMPLETION ########################################
################################################################################
func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void:
if CodeCompletionHelper.get_line_untill_caret(line) == 'set ':
TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, '{', '{', TextNode.syntax_highlighter.variable_color)
if symbol == '{':
CodeCompletionHelper.suggest_variables(TextNode)
func _get_start_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit) -> void:
TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'set', 'set ', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5))
#################### SYNTAX HIGHLIGHTING #######################################
################################################################################
func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary:
dict[line.find('set')] = {"color":event_color.lerp(Highlighter.normal_color, 0.5)}
dict[line.find('set')+3] = {"color":Highlighter.normal_color}
dict = Highlighter.color_region(dict, Highlighter.string_color, line, '"', '"', line.find('set'))
dict = Highlighter.color_region(dict, Highlighter.variable_color, line, '{', '}', line.find('set'))
return dict