Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to pass dialogic variables as args to dialogic event signals #2404

Open
Anomalous-Sentiment opened this issue Sep 17, 2024 · 2 comments
Labels
Enhance ⚡ Improve a feature's workflow. Events Feature✨

Comments

@Anomalous-Sentiment
Copy link

Anomalous-Sentiment commented Sep 17, 2024

Is your feature request related to a problem? Please describe.
The idea is to add the ability to use Dialogic variables as args for event signals instead of directly using values or "magic numbers" for better readability & maintenance.

Describe the solution you'd like
The ability to pass Dialogic variables as args to event signals, for example in the text editor:

[signal arg=foo.bar.id]

or for dictionaries, something like:

[signal arg_type="dict" arg="{"id":foo.bar.id}"]

where foo.bar.id is an int variable to be passed as an arg to the signal..

Describe alternatives you've considered
A working alternative solution that was suggested was to use:
[signal arg_type="dict" arg="{"id":"foo.bar.id"}"]

and receive the signal using:

func _on_dialogic_signal(arg:Variant) -> void:
     print(Dialogic.VAR.get_variable(arg.id))

which is one method to avoid using "magic numbers", although there is some overhead.

Additional context
In my use case, I was trying to create an RPG with a system where player can obtain items through dialogue and at certain points, I wanted signals to be emitted when an item is obtained, and to identify what item was obtained.

The idea was to pass an item ID to the signal, and let the receiver handle the rest, but that would require manually typing in the item ID into the Dialogic text editor, effectively making it a magic number.

Another issue was that in the scenario that the item ID(s) changed, then that could potentially cause issues with all the places where that ID was used. Using a variable would solve this as we could simply reference the variable, and if that value needs to be changed in the future, only the value of the single variable would need to be changed, making readability & maintenance significantly easier.

@Jowan-Spooner Jowan-Spooner added Feature✨ Events Enhance ⚡ Improve a feature's workflow. labels Sep 17, 2024
@Jowan-Spooner
Copy link
Collaborator

This sounds like a good addition, however most likely what it will mean is the syntax will be:
[signal arg_type="dict" arg="{"id":{foo.bar.id}}"]

In a very similar direction it would probably be really useful to allow variables/expressions as any paramter value in shortcode events, e.g.:

[wait time={SomeVariable}]
[text_input text={"Change your name (Current name is "+{Player.Name}+")"}]

@bazsupport
Copy link

Something like this?
Godot_v4 3-stable_win64_lQHZbrl0lD

Then the use code would look something like this:

func _ready():
    # Connect to the signal
    Dialogic.signal_event.connect(_on_dialogic_signal)

func _on_dialogic_signal(signal_name:String, args:Dictionary):
    # Handle the signal
    match signal_name:
        "player_died":
            print("Player died with args:", args)
            # args might be {"reason":"fell", "location":"cliff"}
        
        "item_collected":
            print("Collected item:", args)
            # args might be {"item_id":5, "amount":1}

Changes

addons/dialogic/Editor/Events/Fields/field_flex_value.gd

## Event block field part for a value that can change type.

signal value_changed

var value_field: Node
var value_type: int = -1

var current_value: Variant

func _ready() -> void:
    %ValueType.options = [{
            'label': 'String',
            'icon': ["String", "EditorIcons"],
            'value': TYPE_STRING
        },{
            'label': 'Number (int)',
            'icon': ["int", "EditorIcons"],
            'value': TYPE_INT
        },{
            'label': 'Number (float)',
            'icon': ["float", "EditorIcons"],
            'value': TYPE_FLOAT
        },{
            'label': 'Boolean',
            'icon': ["bool", "EditorIcons"],
            'value': TYPE_BOOL
        },
        ### ADDED NEW OPTION ###
        {
            'label': 'Variable',
            'icon': ["ClassList", "EditorIcons"],
            'value': TYPE_OBJECT  # Using TYPE_OBJECT to represent variables
        },
        ### END ADDED ###
        {
            'label': 'Expression',
            'icon': ["Variant", "EditorIcons"],
            'value': TYPE_MAX
        }
    ]
    %ValueType.symbol_only = true
    %ValueType.value_changed.connect(_on_type_changed.bind())
    %ValueType.tooltip_text = "Change type"

func set_value(value:Variant):
    change_field_type(deduce_type(value))
    %ValueType.set_value(deduce_type(value))
    current_value = value
    match value_type:
        TYPE_BOOL:
            value_field.button_pressed = value
        TYPE_STRING:
            value_field.text = value
        TYPE_FLOAT, TYPE_INT:
            value_field.set_value(value)
        ### ADDED NEW CASE ###
        TYPE_OBJECT:  # Variable type handling
            if value is String and value.begins_with('{') and value.ends_with('}'):
                value_field.current_value = value.trim_prefix('{').trim_suffix('}')
            else:
                value_field.current_value = value
        ### END ADDED ###
        TYPE_MAX, _:
            value_field.text = value.trim_prefix('@')

### MODIFIED FUNCTION ###
func deduce_type(value:Variant) -> int:
    if value is String:
        if value.begins_with('@'):
            return TYPE_MAX
        # Added variable detection
        elif value.begins_with('{') and value.ends_with('}'):
            return TYPE_OBJECT
    return typeof(value)
### END MODIFIED ###

func _on_type_changed(prop:String, type:Variant) -> void:
    if type == value_type:
        return

    match type:
        TYPE_BOOL:
            if typeof(current_value) == TYPE_STRING:
                current_value = DialogicUtil.str_to_bool(current_value)
            elif value_type == TYPE_FLOAT or value_type == TYPE_INT:
                current_value = bool(current_value)
            else:
                current_value = true if current_value else false
            set_value(current_value)
        TYPE_STRING:
            ### MODIFIED CASE ###
            if value_type == TYPE_OBJECT:  # Added variable to string conversion
                current_value = current_value.trim_prefix('{').trim_suffix('}')
            else:
                current_value = str(current_value).trim_prefix('@')
            ### END MODIFIED ###
            set_value(current_value)
        TYPE_FLOAT:
            current_value = float(current_value)
            set_value(current_value)
        TYPE_INT:
            current_value = int(current_value)
            set_value(current_value)
        ### ADDED NEW CASE ###
        TYPE_OBJECT:  # Variable type
            if value_type == TYPE_STRING:
                current_value = '{' + current_value + '}'
            else:
                current_value = '{' + str(current_value) + '}'
            set_value(current_value)
        ### END ADDED ###
        TYPE_MAX,_:
            current_value = var_to_str(current_value)
            set_value('@'+current_value)

    emit_signal.call_deferred('value_changed')

func change_field_type(type:int) -> void:
    if type == value_type:
        return

    value_type = type

    if value_field:
        value_field.queue_free()
    match type:
        TYPE_BOOL:
            value_field = CheckBox.new()
            value_field.toggled.connect(_on_bool_toggled)
        TYPE_STRING:
            value_field = LineEdit.new()
            value_field.theme_type_variation = "DialogicEventEdit"
            value_field.text_changed.connect(_on_str_text_changed)
            value_field.expand_to_text_length = true
        TYPE_FLOAT, TYPE_INT:
            value_field = load("res://addons/dialogic/Editor/Events/Fields/field_number.tscn").instantiate()
            if type == TYPE_FLOAT:
                value_field.use_float_mode()
            else:
                value_field.use_int_mode()
            value_field.value_changed.connect(_on_number_value_changed.bind(type == TYPE_INT))
        ### ADDED NEW CASE ###
        TYPE_OBJECT:  # Variable field type
            value_field = load("res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn").instantiate()
            value_field.get_suggestions_func = get_variable_suggestions
            value_field.value_changed.connect(_on_variable_value_changed)
        ### END ADDED ###
        TYPE_MAX, _:
            value_field = LineEdit.new()
            value_field.expand_to_text_length = true
            value_field.text_changed.connect(_on_expression_changed)
    add_child(value_field)
    move_child(value_field, 1)

### ADDED NEW FUNCTIONS ###
# Variable handling functions
func _on_variable_value_changed(prop:String, value:String) -> void:
    current_value = '{' + value + '}'
    value_changed.emit()

func get_variable_suggestions(filter:String="") -> Dictionary:
    var suggestions := {}
    if filter:
        suggestions[filter] = {'value':filter}
    
    # Add variable suggestions
    var vars: Dictionary = ProjectSettings.get_setting('dialogic/variables', {})
    for var_path in DialogicUtil.list_variables(vars):
        suggestions[var_path] = {
            'value': var_path,
            'icon': load("res://addons/dialogic/Editor/Images/Pieces/variable.svg")
        }
    return suggestions
### END ADDED ###

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhance ⚡ Improve a feature's workflow. Events Feature✨
Projects
None yet
Development

No branches or pull requests

3 participants