Updated dialogic

This commit is contained in:
MaddoScientisto 2026-01-05 16:00:41 +01:00
commit cbb82512ee
483 changed files with 5743 additions and 2177 deletions

View file

@ -1 +1 @@
uid://tesbnlcthnxh
uid://cj42eg6uts07a

View file

@ -1 +1 @@
uid://b4qa43srwtbnf
uid://umnf6yt0lb06

View file

@ -218,6 +218,7 @@ func set_default_button(enabled:bool) -> void:
func preview() -> void:
$Preview.load_overwrite(get_mood_info())
$Preview._on_started_revealing_text()
var preview_timer := Timer.new()
DialogicUtil.update_timer_process_callback(preview_timer)
add_child(preview_timer)

View file

@ -1 +1 @@
uid://x84nluahuf1e
uid://6qkd50m2lqnx

View file

@ -1,9 +1,9 @@
[gd_scene load_steps=9 format=3 uid="uid://8ad1pwbjuqpt"]
[ext_resource type="Script" uid="uid://x84nluahuf1e" path="res://addons/dialogic/Modules/Text/character_settings/character_moods_settings.gd" id="1_3px07"]
[ext_resource type="Script" uid="uid://6qkd50m2lqnx" path="res://addons/dialogic/Modules/Text/character_settings/character_moods_settings.gd" id="1_3px07"]
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="2_e1vyd"]
[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/field_number.tscn" id="3_yjcns"]
[ext_resource type="Script" uid="uid://bkfrnlul8c6cv" path="res://addons/dialogic/Modules/Text/node_type_sound.gd" id="5_yscws"]
[ext_resource type="Script" uid="uid://dpv2dfiv5dhmr" path="res://addons/dialogic/Modules/Text/node_type_sound.gd" id="5_yscws"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y7t05"]
content_margin_left = 10.0

View file

@ -7,7 +7,7 @@ func _get_title() -> String:
func _ready() -> void:
%PortraitMood.get_suggestions_func = mood_suggestions
%PortraitMood.suggestions_func = mood_suggestions
%PortraitMood.resource_icon = get_theme_icon("AudioStreamPlayer", "EditorIcons")

View file

@ -1 +1 @@
uid://b6fg02bv71snt
uid://vphe6s7nv242

View file

@ -1,6 +1,6 @@
[gd_scene load_steps=3 format=3 uid="uid://bvfiv5uhmkqq7"]
[ext_resource type="Script" uid="uid://b6fg02bv71snt" path="res://addons/dialogic/Modules/Text/character_settings/character_portrait_mood_settings.gd" id="1_5ni5u"]
[ext_resource type="Script" uid="uid://vphe6s7nv242" path="res://addons/dialogic/Modules/Text/character_settings/character_portrait_mood_settings.gd" id="1_5ni5u"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="1_oggvu"]
[node name="Typing Sound Mood" type="HBoxContainer"]

View file

@ -17,6 +17,7 @@ var text := ""
## it will play.
var character: DialogicCharacter = null
## If a character is set, this setting can change the portrait of that character.
## If a runtime-character is created, the portrait can instead be a color (hex or color name).
var portrait := ""
### Helpers
@ -24,15 +25,15 @@ var portrait := ""
## Used to set the character resource from the unique name identifier and vice versa
var character_identifier: String:
get:
if character:
var identifier := DialogicResourceUtil.get_unique_identifier(character.resource_path)
if character and not "{" in character_identifier:
var identifier := character.get_identifier()
if not identifier.is_empty():
return identifier
return character_identifier
set(value):
character_identifier = value
character = DialogicResourceUtil.get_character_resource(value)
if not character.portraits.has(portrait):
if Engine.is_editor_hint() and ((not character) or (character and not character.portraits.has(portrait))):
portrait = ""
ui_update_needed.emit()
@ -51,44 +52,65 @@ func _clear_state() -> void:
dialogic.current_state_info.erase('text_sub_idx')
_disconnect_signals()
func _execute() -> void:
if text.is_empty():
finish()
return
if (not character or character.custom_info.get('style', '').is_empty()) and dialogic.has_subsystem('Styles'):
# if previous characters had a custom style change back to base style
if dialogic.current_state_info.get('base_style') != dialogic.current_state_info.get('style'):
dialogic.Styles.change_style(dialogic.current_state_info.get('base_style', 'Default'))
await dialogic.get_tree().process_frame
## If the speaker is provided as an expression, parse it now.
if "{" in character_identifier:
character = null
var character_name: String = dialogic.Expressions.execute_string(character_identifier)
get_or_create_character(character_name)
var character_name_text := dialogic.Text.get_character_name_parsed(character)
if character:
if dialogic.has_subsystem('Styles') and character.custom_info.get('style', null):
dialogic.Styles.change_style(character.custom_info.style, false)
await dialogic.get_tree().process_frame
## Change Portrait and Active Speaker
if dialogic.has_subsystem("Portraits"):
if character:
dialogic.Portraits.change_speaker(character, portrait)
if portrait and dialogic.has_subsystem('Portraits') and dialogic.Portraits.is_character_joined(character):
dialogic.Portraits.change_character_portrait(character, portrait)
dialogic.Portraits.change_speaker(character, portrait)
var check_portrait: String = portrait if !portrait.is_empty() else dialogic.current_state_info['portraits'].get(character.resource_path, {}).get('portrait', '')
if portrait and dialogic.Portraits.is_character_joined(character):
dialogic.Portraits.change_character_portrait(character, portrait)
if check_portrait and character.portraits.get(check_portrait, {}).get('sound_mood', '') in character.custom_info.get('sound_moods', {}):
dialogic.Text.update_typing_sound_mood(character.custom_info.get('sound_moods', {}).get(character.portraits[check_portrait].get('sound_mood', {}), {}))
elif !character.custom_info.get('sound_mood_default', '').is_empty():
dialogic.Text.update_typing_sound_mood(character.custom_info.get('sound_moods', {}).get(character.custom_info.get('sound_mood_default'), {}))
else:
dialogic.Text.update_typing_sound_mood()
dialogic.Portraits.change_speaker(null)
## Change and Type Sound Mood
if character:
dialogic.Text.update_name_label(character)
var current_portrait: String = portrait
if portrait.is_empty():
current_portrait = dialogic.current_state_info["portraits"].get(character.get_identifier(), {}).get("portrait", "")
var current_portrait_sound_mood: String = character.portraits.get(current_portrait, {}).get("sound_mood", "")
dialogic.Text.update_typing_sound_mood_from_character(character, current_portrait_sound_mood)
else:
dialogic.Portraits.change_speaker(null)
dialogic.Text.update_name_label(null)
dialogic.Text.update_typing_sound_mood()
## Handle style changes
if dialogic.has_subsystem("Styles"):
var current_base_style: String = dialogic.current_state_info.get("base_style")
var current_style: String = dialogic.current_state_info.get("style", "")
var character_style: String = "" if not character else character.custom_info.get("style", "")
## Change back to base style, if another characters style is currently used
if (not character or character_style.is_empty()) and (current_base_style != current_style):
dialogic.Styles.change_style(dialogic.current_state_info.get("base_style", "Default"))
await dialogic.get_tree().process_frame
## Change to the characters style if this character has one
elif character and not character_style.is_empty():
dialogic.Styles.change_style(character_style, false)
await dialogic.get_tree().process_frame
_connect_signals()
var character_name_text := dialogic.Text.get_character_name_parsed(character)
var final_text: String = get_property_translated('text')
if ProjectSettings.get_setting('dialogic/text/split_at_new_lines', false):
match ProjectSettings.get_setting('dialogic/text/split_at_new_lines_as', 0):
@ -114,10 +136,10 @@ func _execute() -> void:
dialogic.current_state_info['text_sub_idx'] = section_idx
var segment: String = dialogic.Text.parse_text(split_text[section_idx][0])
var segment: String = dialogic.Text.parse_text(split_text[section_idx][0], 0)
var is_append: bool = split_text[section_idx][1]
final_text = segment
final_text = ProjectSettings.get_setting("dialogic/text/dialog_text_prefix", "")+segment
dialogic.Text.about_to_show_text.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append})
await dialogic.Text.update_textbox(final_text, false)
@ -126,6 +148,8 @@ func _execute() -> void:
_try_play_current_line_voice()
final_text = dialogic.Text.update_dialog_text(final_text, false, is_append)
dialogic.Text.text_started.emit({'text':final_text, 'character':character, 'portrait':portrait, 'append': is_append})
_mark_as_read(character_name_text, final_text)
# We must skip text animation before we potentially return when there
@ -257,22 +281,23 @@ func _init() -> void:
event_category = "Main"
event_sorting_index = 0
expand_by_default = true
help_page_path = "https://docs.dialogic.pro/writing-text-events.html"
help_page_path = "https://docs.dialogic.pro/writing-texts.html"
################################################################################
## SAVING/LOADING
#region SAVING/LOADING
################################################################################
func to_text() -> String:
var result := text.replace('\n', '\\\n')
var result := text.replace('\n', '\\\n').strip_edges(false).trim_suffix("\\")
result = result.replace(':', '\\:')
if result.is_empty():
result = "<Empty Text Event>"
if character:
var name := DialogicResourceUtil.get_unique_identifier(character.resource_path)
if character or character_identifier:
var name := character_identifier
if character:
name = character.get_identifier()
if name.count(" ") > 0:
name = '"' + name + '"'
if not portrait.is_empty():
@ -293,27 +318,45 @@ func from_text(string:String) -> void:
character = DialogicResourceUtil.get_character_resource(character_identifier)
var result := regex.search(string.trim_prefix('\\'))
if result.get_string('portrait'):
portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')')
if result and not result.get_string('name').is_empty():
var name := result.get_string('name').strip_edges()
if name == '_':
character = null
elif "{" in name:
## If it's an expression, we load the character in _execute.
character_identifier = name
character = null
else:
character = DialogicResourceUtil.get_character_resource(name)
get_or_create_character(name)
if character == null and Engine.is_editor_hint() == false:
character = DialogicCharacter.new()
character.display_name = name
character.resource_path = "user://"+name+".dch"
DialogicResourceUtil.add_resource_to_directory(character.resource_path, DialogicResourceUtil.get_character_directory())
if not result:
return
if !result.get_string('portrait').is_empty():
portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')')
text = result.get_string('text').replace("\\\n", "\n").replace('\\:', ':').strip_edges().trim_prefix('\\')
if text == '<Empty Text Event>':
text = ""
if result:
text = result.get_string('text').replace("\\\n", "\n").replace('\\:', ':').strip_edges().trim_prefix('\\')
if text == '<Empty Text Event>':
text = ""
func get_or_create_character(name:String) -> void:
character = DialogicResourceUtil.get_character_resource(name)
if character == null:
if Engine.is_editor_hint() == false:
character = DialogicCharacter.new()
character.display_name = name
character.set_identifier(name)
if portrait:
if "{" in portrait:
character.color = Color(dialogic.Expressions.execute_string(portrait))
else:
character.color = Color(portrait)
else:
character_identifier = name
func is_valid_event(_string:String) -> bool:
@ -329,8 +372,8 @@ func is_string_full_event(string:String) -> bool:
func get_shortcode_parameters() -> Dictionary:
return {
#param_name : property_info
"character" : {"property": "character_identifier", "default": ""},
"portrait" : {"property": "portrait", "default": ""},
"character" : {"property": "character_identifier", "default": "", "ext_file":true},
"portrait" : {"property": "portrait", "default": ""},
}
#endregion
@ -364,7 +407,7 @@ func build_event_editor() -> void:
{'file_extension' : '.dch',
'mode' : 2,
'suggestions_func' : get_character_suggestions,
'empty_text' : '(No one)',
'placeholder' : '(No one)',
'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg")}, 'do_any_characters_exist()')
add_header_edit('portrait', ValueType.DYNAMIC_OPTIONS,
{'suggestions_func' : get_portrait_suggestions,
@ -384,8 +427,13 @@ func do_any_characters_exist() -> bool:
func get_character_suggestions(search_text:String) -> Dictionary:
return DialogicUtil.get_character_suggestions(search_text, character, true, false, editor_node)
var suggestions := DialogicUtil.get_character_suggestions(search_text, character, true, false, editor_node)
if search_text and not search_text in suggestions:
suggestions[search_text] = {
"value":search_text,
"tooltip": "A temporary character, created on the spot.",
"editor_icon":["GuiEllipsis", "EditorIcons"]}
return suggestions
func get_portrait_suggestions(search_text:String) -> Dictionary:
return DialogicUtil.get_portrait_suggestions(search_text, character, true, "Don't change")
@ -429,7 +477,7 @@ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:Str
func _get_start_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit) -> void:
CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_CLASS, true)
CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_CLASS, self)
func suggest_bbcode(TextNode:CodeEdit):
@ -462,10 +510,10 @@ var text_effect_color := Color('#898276')
func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary:
load_text_effects()
if text_random_word_regex.get_pattern().is_empty():
text_random_word_regex.compile("(?<!\\\\)\\<[^\\[\\>]+(\\/[^\\>]*)\\>")
text_random_word_regex.compile(r"(?<!\\)\<[^\>]+(\/[^\>]*)\>")
var result := regex.search(line)
if !result:
if not result:
return dict
if Highlighter.mode == Highlighter.Modes.FULL_HIGHLIGHTING:
if result.get_string('name'):
@ -475,24 +523,29 @@ func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, li
dict[result.get_start('portrait')] = {"color":Highlighter.character_portrait_color}
dict[result.get_end('portrait')] = {"color":Highlighter.normal_color}
if result.get_string('text'):
var effects_result := text_effects_regex.search_all(line)
for eff in effects_result:
dict[eff.get_start()] = {"color":text_effect_color}
dict[eff.get_end()] = {"color":Highlighter.normal_color}
dict = Highlighter.color_region(dict, Highlighter.variable_color, line, '{', '}', result.get_start('text'))
## Color the random selection modifier
for replace_mod_match in text_random_word_regex.search_all(result.get_string('text')):
var color: Color = Highlighter.string_color
color = color.lerp(Highlighter.normal_color, 0.4)
dict[replace_mod_match.get_start()+result.get_start('text')] = {'color':Highlighter.string_color}
var offset := 1
for b in replace_mod_match.get_string().trim_suffix('>').trim_prefix('<').split('/'):
for b:RegExMatch in RegEx.create_from_string(r"(\[[^\]]*\]|[^\/]|\/\/)+").search_all(replace_mod_match.get_string().trim_prefix("<").trim_suffix(">")):
color.h = wrap(color.h+0.2, 0, 1)
dict[replace_mod_match.get_start()+result.get_start('text')+offset] = {'color':color}
offset += len(b)
offset += len(b.get_string())
dict[replace_mod_match.get_start()+result.get_start('text')+offset] = {'color':Highlighter.string_color}
offset += 1
dict[replace_mod_match.get_end()+result.get_start('text')] = {'color':Highlighter.normal_color}
## Color bbcode and text effects
var effects_result := text_effects_regex.search_all(line)
for eff in effects_result:
var prev_color: Color = Highlighter.dict_get_color_at_column(dict, eff.get_start())
dict[eff.get_start()] = {"color":text_effect_color.lerp(prev_color, 0.4)}
dict[eff.get_end()] = {"color":prev_color}
dict = Highlighter.color_region(dict, Highlighter.variable_color, line, '{', '}', result.get_start('text'))
return dict
#endregion

View file

@ -1 +1 @@
uid://v1q0c4baexov
uid://datmius068d8h

View file

@ -1 +1 @@
uid://8cq5nqyn5kaa
uid://bthukngr7e6io

View file

@ -1 +1 @@
uid://d3jie3vjagf3x
uid://dbx6k01krd1xx

View file

@ -52,6 +52,12 @@ func _ready() -> void:
textbox_root.hide()
text = ""
var custom_bbcode_effects: Array = ProjectSettings.get_setting("dialogic/text/custom_bbcode_effects", "").split(",", false)
for i in custom_bbcode_effects:
var x : Resource = load(i.strip_edges())
if x is RichTextEffect:
custom_effects.append(x)
# this is called by the DialogicGameHandler to set text
@ -60,6 +66,8 @@ func reveal_text(_text: String, keep_previous:=false) -> void:
return
show()
custom_fx_reset()
if !keep_previous:
text = _text
base_visible_characters = 0
@ -73,6 +81,7 @@ func reveal_text(_text: String, keep_previous:=false) -> void:
else:
base_visible_characters = len(text)
visible_characters = len(get_parsed_text())
custom_fx_update()
text = text + _text
# If Auto-Skip is enabled and we append the text (keep_previous),
@ -113,16 +122,21 @@ func continue_reveal() -> void:
if visible_characters > -1 and visible_characters <= len(get_parsed_text()):
continued_revealing_text.emit(get_parsed_text()[visible_characters-1])
custom_fx_update()
else:
finish_text()
finish_text(true)
# if the text finished organically, add a small input block
# this prevents accidental skipping when you expected the text to be longer
DialogicUtil.autoload().Inputs.block_input(ProjectSettings.get_setting('dialogic/text/advance_delay', 0.1))
## Reveals the entire text instantly.
func finish_text() -> void:
func finish_text(is_organic := false) -> void:
visible_ratio = 1
custom_fx_update()
if not is_organic:
custom_fx_skip()
DialogicUtil.autoload().Text.execute_effects(-1, self, true)
revealing = false
DialogicUtil.autoload().current_state = DialogicGameHandler.States.IDLE
@ -156,3 +170,21 @@ func _on_meta_clicked(_meta:Variant) -> void:
## Handle mouse input
func on_gui_input(event:InputEvent) -> void:
DialogicUtil.autoload().Inputs.handle_node_gui_input(event)
func custom_fx_update() -> void:
for effect in custom_effects:
if "visible_characters" in effect:
effect.visible_characters = visible_characters
func custom_fx_reset() -> void:
for effect in custom_effects:
if effect.has_method("reset"):
effect.reset()
func custom_fx_skip() -> void:
for effect in custom_effects:
if effect.has_method("skip"):
effect.skip()

View file

@ -1 +1 @@
uid://ddkvkdb6nxtyi
uid://drhfq6rmdeuri

View file

@ -1 +1 @@
uid://do47nn7beuhn5
uid://ctog34kdg222q

View file

@ -1 +1 @@
uid://be3h8wr0w68dx
uid://bak74s0kcr0ao

View file

@ -1 +1 @@
uid://dk0xy5qppo033
uid://dve1vwse2peji

View file

@ -1 +1 @@
uid://bkfrnlul8c6cv
uid://dpv2dfiv5dhmr

View file

@ -12,6 +12,8 @@ const _SETTING_TEXT_REVEAL_SKIPPABLE_DELAY := 'dialogic/text/text_reveal_skip_d
const _SETTING_TEXT_ADVANCE_DELAY := 'dialogic/text/advance_delay'
const _SETTING_AUTOCOLOR_NAMES := 'dialogic/text/autocolor_names'
const _SETTING_TEXT_PREFIX := 'dialogic/text/dialog_text_prefix'
const _SETTING_CUSTOM_BBCODE_EFFECTS := 'dialogic/text/custom_bbcode_effects'
const _SETTING_SPLIT_AT_NEW_LINES := 'dialogic/text/split_at_new_lines'
const _SETTING_SPLIT_AT_NEW_LINES_AS := 'dialogic/text/split_at_new_lines_as'
@ -44,6 +46,8 @@ func _ready() -> void:
%AdvanceDelay.value_changed.connect(_on_float_set.bind(_SETTING_TEXT_ADVANCE_DELAY))
%AutocolorNames.toggled.connect(_on_bool_set.bind(_SETTING_AUTOCOLOR_NAMES))
%TextPrefix.text_changed.connect(_on_string_set.bind(_SETTING_TEXT_PREFIX))
%CustomBBCodeEffects.text_changed.connect(_on_string_set.bind(_SETTING_CUSTOM_BBCODE_EFFECTS))
%NewEvents.toggled.connect(_on_bool_set.bind(_SETTING_SPLIT_AT_NEW_LINES))
@ -62,13 +66,15 @@ func _refresh() -> void:
%InputAction.resource_icon = get_theme_icon(&"Mouse", &"EditorIcons")
%InputAction.set_value(ProjectSettings.get_setting(_SETTING_INPUT_ACTION, &'dialogic_default_action'))
%InputAction.get_suggestions_func = suggest_actions
%InputAction.suggestions_func = suggest_actions
%Skippable.button_pressed = ProjectSettings.get_setting(_SETTING_TEXT_REVEAL_SKIPPABLE, true)
%SkippableDelay.value = ProjectSettings.get_setting(_SETTING_TEXT_REVEAL_SKIPPABLE_DELAY, 0.1)
%AdvanceDelay.value = ProjectSettings.get_setting(_SETTING_TEXT_ADVANCE_DELAY, 0.1)
%AutocolorNames.button_pressed = ProjectSettings.get_setting(_SETTING_AUTOCOLOR_NAMES, false)
%TextPrefix.text = ProjectSettings.get_setting(_SETTING_TEXT_PREFIX, "")
%CustomBBCodeEffects.text = ProjectSettings.get_setting(_SETTING_CUSTOM_BBCODE_EFFECTS, "")
%NewEvents.button_pressed = ProjectSettings.get_setting(_SETTING_SPLIT_AT_NEW_LINES, false)
%NewEventOption.select(ProjectSettings.get_setting(_SETTING_SPLIT_AT_NEW_LINES_AS, 0))
@ -115,14 +121,18 @@ func _on_float_set(value:float, setting:String) -> void:
ProjectSettings.save()
func _on_string_set(value:String, setting:String) -> void:
ProjectSettings.set_setting(setting, value)
ProjectSettings.save()
#region BEHAVIOUR
################################################################################
func _on_InputAction_value_changed(property_name:String, value:String) -> void:
func _on_InputAction_value_changed(_property_name:String, value:String) -> void:
ProjectSettings.set_setting(_SETTING_INPUT_ACTION, value)
ProjectSettings.save()
func suggest_actions(search:String) -> Dictionary:
func suggest_actions(_search:String) -> Dictionary:
var suggs := {}
for prop in ProjectSettings.get_property_list():
if prop.name.begins_with('input/') and not prop.name.begins_with('input/ui_') :
@ -237,4 +247,3 @@ func _on_remove_autopauses_set_pressed(index: int) -> void:
for key in autopause_sets[index]:
autopause_sets[index][key].queue_free()
autopause_sets.erase(index)

View file

@ -1 +1 @@
uid://b5ltnfwvbfbfc
uid://cdxck874xobqh

View file

@ -1,26 +1,13 @@
[gd_scene load_steps=6 format=3 uid="uid://cf3qks3v18xmr"]
[gd_scene load_steps=4 format=3 uid="uid://cf3qks3v18xmr"]
[ext_resource type="Script" uid="uid://b5ltnfwvbfbfc" path="res://addons/dialogic/Modules/Text/settings_text.gd" id="2"]
[ext_resource type="Script" uid="uid://cdxck874xobqh" path="res://addons/dialogic/Modules/Text/settings_text.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3"]
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="3_s7xhj"]
[sub_resource type="Image" id="Image_15d2e"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_3xcp4"]
image = SubResource("Image_15d2e")
[node name="DialogText" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_bottom = -156.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2")
@ -48,7 +35,7 @@ text = "Default letter speed"
[node name="HintTooltip2" parent="VBoxContainer/VBox/DefaultSpeedLabel" instance=ExtResource("3_s7xhj")]
layout_mode = 2
tooltip_text = "The speed in seconds per character. A speed of 0 will reveal the full text instantly (still taking pauses into consideration)."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "The speed in seconds per character. A speed of 0 will reveal the full text instantly (still taking pauses into consideration)."
[node name="DefaultSpeed" type="SpinBox" parent="VBoxContainer/VBox"]
@ -67,7 +54,7 @@ text = "Input action"
layout_mode = 2
tooltip_text = "The action that skips text and generally advances to the next event.
You can modify actions in the Project Settings > Input Map."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "The action that skips text and generally advances to the next event.
You can modify actions in the Project Settings > Input Map."
@ -87,7 +74,7 @@ text = "Text Reveal Skippable"
layout_mode = 2
tooltip_text = "If enabled the revealing of text can be skipped with the input action.
If disabled you can only advance to the next event when revealing has finnished."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "If enabled the revealing of text can be skipped with the input action.
If disabled you can only advance to the next event when revealing has finnished."
@ -110,7 +97,7 @@ layout_mode = 2
tooltip_text = "Delay before you can skip.
Use this to prevent users from skipping through your timeline to quickly."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "Delay before you can skip.
Use this to prevent users from skipping through your timeline too quickly."
@ -134,7 +121,7 @@ layout_mode = 2
tooltip_text = "Delay before you can advance (if the text finishes revealing on its own).
This is used to prevent players from advancing when they actually wanted to skip the revealing, but did so very shortly after the text was already fully revealed."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "Delay before you can advance (only if the text finishes revealing on its own).
This is used to prevent players from advancing when they actually wanted to skip the revealing, but did so very shortly after the text was already fully revealed."
@ -156,13 +143,47 @@ text = "Autocolor names"
[node name="HintTooltip5" parent="VBoxContainer/VBox/ColorNames" instance=ExtResource("3_s7xhj")]
layout_mode = 2
tooltip_text = "If enabled character names will be colored in the characters color in text events."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "If enabled character names will be colored in the characters color in text events."
[node name="AutocolorNames" type="CheckBox" parent="VBoxContainer/VBox"]
unique_name_in_owner = true
layout_mode = 2
[node name="TextPrefixLabel" type="HBoxContainer" parent="VBoxContainer/VBox"]
layout_mode = 2
[node name="Label4" type="Label" parent="VBoxContainer/VBox/TextPrefixLabel"]
layout_mode = 2
text = "Text prefix"
[node name="HintTooltip5" parent="VBoxContainer/VBox/TextPrefixLabel" instance=ExtResource("3_s7xhj")]
layout_mode = 2
tooltip_text = "If enabled character names will be colored in the characters color in text events."
texture = null
hint_text = "This is put before the text. Can be used to apply bbcode effects to all texts."
[node name="TextPrefix" type="LineEdit" parent="VBoxContainer/VBox"]
unique_name_in_owner = true
layout_mode = 2
[node name="BBCodeEffectLabel" type="HBoxContainer" parent="VBoxContainer/VBox"]
layout_mode = 2
[node name="Label4" type="Label" parent="VBoxContainer/VBox/BBCodeEffectLabel"]
layout_mode = 2
text = "Custom BBCode Effects"
[node name="HintTooltip5" parent="VBoxContainer/VBox/BBCodeEffectLabel" instance=ExtResource("3_s7xhj")]
layout_mode = 2
tooltip_text = "This is put before the text. Can be used to apply bbcode effects to all texts."
texture = null
hint_text = "Supply a list of bbcode effect resources (paths or uids) separated by comma."
[node name="CustomBBCodeEffects" type="LineEdit" parent="VBoxContainer/VBox"]
unique_name_in_owner = true
layout_mode = 2
[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer/VBox"]
layout_mode = 2
@ -174,7 +195,7 @@ text = "New lines as new events"
layout_mode = 2
tooltip_text = "If enabled dialogic, new lines will be treated as [n] effects,
seemingly waiting for input before starting a new text."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "If enabled dialogic, new lines will be treated as [n] effects,
seemingly waiting for input before starting a new text."
@ -188,9 +209,9 @@ layout_mode = 2
[node name="NewEventOption" type="OptionButton" parent="VBoxContainer/VBox/HBoxContainer4"]
unique_name_in_owner = true
layout_mode = 2
item_count = 2
selected = 0
fit_to_longest_item = false
item_count = 2
popup/item_0/text = "As new event"
popup/item_0/id = 0
popup/item_1/text = "Appended"
@ -219,7 +240,7 @@ These add up, so if any of them is true, Auto-Advance will happen.
Unless manual advancement is disabled, the Auto-Advance time can always be skipped by the player.
The Auto-Advance will wait for Voice audio to finish playing. This behaviour can be disabled via code. "
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "Autoadvance is the concept of automatically progressing to the next event upon completing text display, usually after a certain delay.
You can enabled Auto-Advance from code using either:
@ -245,7 +266,7 @@ text = "Base Delay"
[node name="HintTooltip" parent="VBoxContainer/AutoadvanceSettings/HBox_BaseDelay2" instance=ExtResource("3_s7xhj")]
layout_mode = 2
tooltip_text = "This is the base delay for autoadvancment."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "This is the base delay for autoadvancment."
[node name="FixedDelay" type="SpinBox" parent="VBoxContainer/AutoadvanceSettings"]
@ -268,7 +289,7 @@ layout_mode = 2
tooltip_text = "An additional delay per character or word can be added.
Note: When changing values via code, you can actually use both modes simultaniously."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "An additional delay per character or word can be added.
Note: When changing values via code, you can actually use both modes simultaniously."
@ -279,9 +300,9 @@ layout_mode = 2
[node name="AdditionalDelayMode" type="OptionButton" parent="VBoxContainer/AutoadvanceSettings/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
item_count = 3
selected = 0
fit_to_longest_item = false
item_count = 3
popup/item_0/text = "None"
popup/item_0/id = 0
popup/item_1/text = "Per Word"
@ -308,7 +329,7 @@ tooltip_text = "An ignored character will add no delay, this is useful to exclud
If disabled, the general line of text length will be used, stripping the BBCode tags first.
If enabled, the text will be scanned and the matching characters will be skipped."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "An ignored character will add no delay, this is useful to exclude interpunction and whitespaces.
If disabled, the general line of text length will be used, stripping the BBCode tags first.
@ -336,7 +357,7 @@ layout_mode = 2
tooltip_text = "While you would usually enable Auto-Advance via code,
if this is true it will be initially enabled.
This kind of Auto-Advance (system) only stops when disabled via code. "
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "While you would usually enable Auto-Advance via code,
if this is true it will be initially enabled.
This kind of Auto-Advance (system) only stops when disabled via code. "
@ -366,7 +387,7 @@ Dialogic.Inputs.auto_skip.enabled = true
By default, Auto-Skip will cancel on user input.
You can disable this by calling:
Dialogic.Inputs.auto_skip.disable_on_user_input = false"
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "Auto-Skip is the concept of automatically skipping Timeline Events to the next unread Text Event or Event demanding user inputs (e.g. Choice, Wait Input, and Text Input).
You can enable Auto-Skip from code via:
@ -393,7 +414,7 @@ tooltip_text = "The time until Auto-Skip will execute the next event.
If this is set to 0.1s, each event should finish within that time.
Custom events must respect this time, built-in events already handle Auto-Skip."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "The time until Auto-Skip will execute the next event.
If this is set to 0.1s, each event should finish within that time.
@ -424,7 +445,7 @@ tooltip_text = "Adds pauses after certain letters.
Each set can contain multiple letters that will (individually)
have a pause of the given length added after them."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "Adds pauses after certain letters.
Each set can contain multiple letters that will (individually)
@ -452,7 +473,7 @@ text = "Absolute auto-pause times"
[node name="HintTooltip7" parent="VBoxContainer/HBoxContainer3" instance=ExtResource("3_s7xhj")]
layout_mode = 2
tooltip_text = "If not enabled autopauses will be multiplied by the speed and user speed. When enabled those will be ignored."
texture = SubResource("ImageTexture_3xcp4")
texture = null
hint_text = "If not enabled autopauses will be multiplied by the speed and user speed. When enabled those will be ignored."
[node name="AutoPausesAbsolute" type="CheckBox" parent="VBoxContainer/HBoxContainer3"]

View file

@ -4,18 +4,48 @@ extends DialogicSubsystem
#region SIGNALS
## Emitted when a text event is reached or a new text section is about to be shown.
## Gives a dictionary with the following keys: [br]
## [br]
## Key | Value Type | Value [br]
## ----------- | ------------- | ----- [br]
## `text` | [type String] | The text that is being displayed. [br]
## `character` | [type DialogicCharacter] | The character that says this text. [br]
## `portrait` | [type String] | The name of the portrait the character will use. [br]
## `append` | [type bool] | Whether the text will be appended to the previous text. [br]
@warning_ignore("unused_signal") # This is emitted by the text event.
signal about_to_show_text(info:Dictionary)
## Emitted when a text event (or a new text section) starts displaying.
## This will be AFTER the textox animation, while [signal about_to_show_text] is before.
## Gives a dictionary with the same values as [signal about_to_show_text]
@warning_ignore("unused_signal") # This is emitted by the text event.
signal text_started(info:Dictionary)
## When the text has finished revealing.
## Gives a dictionary with the keys text and character.
signal text_finished(info:Dictionary)
## Emitted when the speaker changes.
signal speaker_updated(character:DialogicCharacter)
## Emitted when the textbox is shown or hidden.
signal textbox_visibility_changed(visible:bool)
signal animation_textbox_new_text
## Emitted when the textbox appears.
## Use this together with the Animations subsystem to implement animations.
## If you start an animation and want dialogic to wait for it to finish before showing text,
## call Dialogic.Animations.start_animating() and then Dialogic.animation_finished() once it's done.
signal animation_textbox_show
## Emitted when the textbox is hiding. Use like [signal animation_textbox_show].
signal animation_textbox_hide
## Emitted when a new text starts. Use like [signal animation_textbox_show].
signal animation_textbox_new_text
# forwards of the dialog_text signals of all present dialog_text nodes
signal meta_hover_ended(meta:Variant)
## Emitted when a meta text on any DialogText node is hovered.
@warning_ignore("unused_signal") # These are emitted by the NodeDialogText
signal meta_hover_started(meta:Variant)
## Emitted when a meta text on any DialogText node is not hovered anymore.
@warning_ignore("unused_signal") # These are emitted by the NodeDialogText
signal meta_hover_ended(meta:Variant)
## Emitted when a meta text on any DialogText node is clicked.
@warning_ignore("unused_signal") # These are emitted by the NodeDialogText
signal meta_clicked(meta:Variant)
#endregion
@ -29,7 +59,7 @@ var text_already_read := false
var text_effects := {}
var parsed_text_effect_info: Array[Dictionary] = []
var text_effects_regex := RegEx.new()
enum TextModifierModes {ALL=-1, TEXT_ONLY=0, CHOICES_ONLY=1}
enum ParserModes {ALL=-1, TEXT_ONLY=0, CHOICES_ONLY=1}
enum TextTypes {DIALOG_TEXT, CHOICE_TEXT}
var text_modifiers := []
@ -44,15 +74,17 @@ var _voice_synced_text := false
var _autopauses := {}
var parse_stack: Array[Dictionary] = []
#region STATE
####################################################################################################
func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
update_dialog_text('', true)
update_dialog_text("", true)
update_name_label(null)
dialogic.current_state_info['speaker'] = ""
dialogic.current_state_info['text'] = ''
dialogic.current_state_info["speaker"] = ""
dialogic.current_state_info["text"] = ""
set_text_reveal_skippable(ProjectSettings.get_setting('dialogic/text/initial_text_reveal_skippable', true))
@ -65,9 +97,7 @@ func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) ->
func load_game_state(_load_flag:=LoadFlags.FULL_LOAD) -> void:
update_textbox(dialogic.current_state_info.get('text', ''), true)
update_dialog_text(dialogic.current_state_info.get('text', ''), true)
var character: DialogicCharacter = null
if dialogic.current_state_info.get('speaker', ""):
character = load(dialogic.current_state_info.get('speaker', ""))
var character: DialogicCharacter = get_current_speaker()
if character:
update_name_label(character)
@ -85,21 +115,57 @@ func post_install() -> void:
#region MAIN METHODS
####################################################################################################
## Applies modifiers, effects and coloring to the text
func parse_text(text:String, type:int=TextTypes.DIALOG_TEXT, variables := true, glossary := true, modifiers:= true, effects:= true, color_names:= true) -> String:
if modifiers:
text = parse_text_modifiers(text, type)
if variables and dialogic.has_subsystem('VAR'):
text = dialogic.VAR.parse_variables(text)
if effects:
text = parse_text_effects(text)
if color_names:
text = color_character_names(text)
if glossary and dialogic.has_subsystem('Glossary'):
text = dialogic.Glossary.parse_glossary(text)
## Applies modifiers, effects and coloring to the text.
## Utilizes the parse stack created and sorted in [method load_parse_stack()].
func parse_text(text:String, type:int=TextTypes.DIALOG_TEXT) -> String:
if parse_stack.is_empty():
load_parse_stack()
for i in parse_stack:
if i.type != ParserModes.ALL and type != -1 and i.type != type:
continue
text = i.method.call(text)
return text
## Creates and sorts a stack of methods that take a text and return it.
## This includes: variables, text modifiers, text effects, autocolor names and the glossary.
func load_parse_stack() -> void:
parse_stack.clear()
if dialogic.has_subsystem('VAR'):
parse_stack.append(
{
"method":dialogic.VAR.parse_variables,
"type": ParserModes.ALL,
"order": 30,
})
parse_stack.append(
{
"method": parse_text_effects,
"type": ParserModes.TEXT_ONLY,
"order": 50,
})
for i in text_modifiers:
parse_stack.append(i)
parse_stack.append(
{
"method": color_character_names,
"type": ParserModes.TEXT_ONLY,
"order": 90,
})
parse_stack.append(
{
"method": dialogic.Glossary.parse_glossary,
"type": ParserModes.TEXT_ONLY,
"order": 95,
})
parse_stack.sort_custom(func(a,b):return a["order"] < b["order"])
## When an event updates the text spoken, this can adjust the state of
## the dialog text box.
## This method is async.
@ -138,6 +204,13 @@ func update_dialog_text(text: String, instant := false, additional := false) ->
text_node.text = text
else:
var current_character := get_current_speaker()
if current_character:
var character_prefix: String = current_character.custom_info.get(DialogicCharacterPrefixSuffixSection.PREFIX_CUSTOM_KEY, DialogicCharacterPrefixSuffixSection.DEFAULT_PREFIX)
var character_suffix: String = current_character.custom_info.get(DialogicCharacterPrefixSuffixSection.SUFFIX_CUSTOM_KEY, DialogicCharacterPrefixSuffixSection.DEFAULT_SUFFIX)
text = character_prefix + text + character_suffix
text_node.reveal_text(text, additional)
if !text_node.finished_revealing_text.is_connected(_on_dialog_text_finished):
@ -158,18 +231,18 @@ func update_dialog_text(text: String, instant := false, additional := false) ->
func _on_dialog_text_finished() -> void:
text_finished.emit({'text':dialogic.current_state_info['text'], 'character':dialogic.current_state_info['speaker']})
text_finished.emit({"text":dialogic.current_state_info["text"], "character":dialogic.current_state_info["speaker"]})
## Updates the visible name on all name labels nodes.
## If a name changes, the [signal speaker_updated] signal is emitted.
func update_name_label(character:DialogicCharacter):
var character_path := character.resource_path if character else ""
var current_character_path: String = dialogic.current_state_info.get("speaker", "")
var character_id := character.get_identifier() if character else ""
var current_character_id: String = dialogic.current_state_info.get("speaker", "")
if character_path != current_character_path:
dialogic.current_state_info['speaker'] = character_path
if character_id != current_character_id:
speaker_updated.emit(character)
dialogic.current_state_info["speaker"] = character_id
var name_label_text := get_character_name_parsed(character)
@ -182,8 +255,19 @@ func update_name_label(character:DialogicCharacter):
name_label.self_modulate = Color(1,1,1,1)
func update_typing_sound_mood_from_character(character:DialogicCharacter, mood:String) -> void:
if character.custom_info.get("sound_moods", {}).is_empty():
update_typing_sound_mood()
elif mood in character.custom_info.get("sound_moods", {}):
update_typing_sound_mood(character.custom_info.get("sound_moods", {})[mood])
else:
var default_mood : String = character.custom_info.get("sound_mood_default", "")
update_typing_sound_mood(character.custom_info.get("sound_moods", {}).get(default_mood, {}))
func update_typing_sound_mood(mood:Dictionary = {}) -> void:
for typing_sound in get_tree().get_nodes_in_group('dialogic_type_sounds'):
for typing_sound in get_tree().get_nodes_in_group("dialogic_type_sounds"):
typing_sound.load_overwrite(mood)
@ -335,8 +419,12 @@ func collect_text_effects() -> void:
## Use get_parsed_text_effects() after calling this to get all effect information
func parse_text_effects(text:String) -> String:
parsed_text_effect_info.clear()
var rtl := RichTextLabel.new()
rtl.bbcode_enabled = true
var rtl: RichTextLabel = null
if get_tree().get_first_node_in_group("dialogic_dialog_text"):
rtl = get_tree().get_first_node_in_group("dialogic_dialog_text").duplicate()
else:
rtl = RichTextLabel.new()
rtl.bbcode_enabled = true
var position_correction := 0
var bbcode_correction := 0
for effect_match in text_effects_regex.search_all(text):
@ -361,7 +449,9 @@ func execute_effects(current_index:int, text_node:Control, skipping := false) ->
if current_index != -1 and current_index < parsed_text_effect_info[0]['index']:
return
var effect: Dictionary = parsed_text_effect_info.pop_front()
await (effect['execution_info']['callable'] as Callable).call(text_node, skipping, effect['value'])
var callable: Callable = effect['execution_info']['callable']
if is_instance_valid(text_node):
await callable.call(text_node, skipping, effect['value'])
func collect_text_modifiers() -> void:
@ -372,15 +462,8 @@ func collect_text_modifiers() -> void:
text_modifiers.append({'method':Callable(dialogic.get_subsystem(modifier.subsystem), modifier.method)})
elif modifier.has('node_path') and modifier.has('method'):
text_modifiers.append({'method':Callable(get_node(modifier.node_path), modifier.method)})
text_modifiers[-1]['mode'] = modifier.get('mode', TextModifierModes.TEXT_ONLY)
func parse_text_modifiers(text:String, type:int=TextTypes.DIALOG_TEXT) -> String:
for mod in text_modifiers:
if mod.mode != TextModifierModes.ALL and type != -1 and type != mod.mode:
continue
text = mod.method.call(text)
return text
text_modifiers[-1]['type'] = modifier.get('mode', ParserModes.TEXT_ONLY)
text_modifiers[-1]['order'] = modifier.get('order', 40)
#endregion
@ -415,19 +498,12 @@ func get_character_name_parsed(character:DialogicCharacter) -> String:
## Returns the [class DialogicCharacter] of the current speaker.
## If there is no current speaker or the speaker is not found, returns null.
func get_current_speaker() -> DialogicCharacter:
var speaker_path: String = dialogic.current_state_info.get("speaker", "")
var speaker_id: String = dialogic.current_state_info.get("speaker", "")
if speaker_path.is_empty():
if speaker_id.is_empty():
return null
var speaker_resource := load(speaker_path)
if speaker_resource == null:
return null
var speaker_character := speaker_resource as DialogicCharacter
return speaker_character
return DialogicResourceUtil.get_character_resource(speaker_id)
func _update_user_speed(_user_speed:float) -> void:
@ -475,8 +551,8 @@ func collect_character_names() -> void:
character_colors = {}
for dch_path in DialogicResourceUtil.get_character_directory().values():
var character := (load(dch_path) as DialogicCharacter)
for dch_identifier in DialogicResourceUtil.get_character_directory():
var character := (DialogicResourceUtil.get_character_resource(dch_identifier) as DialogicCharacter)
if character.display_name:
if "{" in character.display_name and "}" in character.display_name:
@ -564,19 +640,20 @@ func effect_signal(_text_node:Control, _skipped:bool, argument:String) -> void:
func effect_mood(_text_node:Control, _skipped:bool, argument:String) -> void:
if argument.is_empty(): return
if dialogic.current_state_info.get('speaker', ""):
if get_current_speaker():
update_typing_sound_mood(
load(dialogic.current_state_info.speaker).custom_info.get('sound_moods', {}).get(argument, {}))
get_current_speaker().custom_info.get('sound_moods', {}).get(argument, {}))
var modifier_words_select_regex := RegEx.create_from_string(r"(?<!\\)\<[^\[\>]+(\/[^\>]*)\>")
var modifier_select_regex := RegEx.create_from_string(r"(?<!\\)\<[^\>]+(\/[^\>]*)\>")
var modifier_select_split_regex := RegEx.create_from_string(r"(\[[^\]]*\]|[^\/]|\/\/)+")
func modifier_random_selection(text:String) -> String:
for replace_mod_match in modifier_words_select_regex.search_all(text):
for replace_mod_match: RegExMatch in modifier_select_regex.search_all(text):
var string: String = replace_mod_match.get_string().trim_prefix("<").trim_suffix(">")
string = string.replace('//', '<slash>')
var list: PackedStringArray = string.split('/')
var item: String = list[randi()%len(list)]
item = item.replace('<slash>', '/')
var options := []
for split: RegExMatch in modifier_select_split_regex.search_all(string):
options.append(split.get_string())
var item: String = options.pick_random()
text = text.replace(replace_mod_match.get_string(), item.strip_edges())
return text

View file

@ -1 +1 @@
uid://dtxvpn61hx7ki
uid://os6fyykwoljl