Add addons

This commit is contained in:
Joey Yakimowich-Payne 2023-01-03 16:53:53 -07:00
commit 3367bad24f
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
527 changed files with 36197 additions and 0 deletions

View file

@ -0,0 +1,39 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://2fkk71l86stl"
path="res://.godot/imported/DNextIndicator_icon.svg-d8f962ed97aa9f929346c153ee39c52b.ctex"
metadata={
"editor_scale": 1.25,
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Events/Text/DNextIndicator_icon.svg"
dest_files=["res://.godot/imported/DNextIndicator_icon.svg-d8f962ed97aa9f929346c153ee39c52b.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,93 @@
@tool
extends DialogicCharacterEditorMainTab
## Character editor tab that allows editing typing sound moods.
################################################################################
## COMMUNICATION WITH EDITOR
################################################################################
func _load_character(character:DialogicCharacter):
for mood in %Moods.get_children():
mood.queue_free()
%PortraitMood.set_value('')
%DefaultMood.set_value(character.custom_info.get('sound_moods_default', ''))
for mood in character.custom_info.get('sound_moods', {}):
create_mood_item(character.custom_info.sound_moods[mood])
func _save_changes(character:DialogicCharacter) -> DialogicCharacter:
var moods := {}
for node in %Moods.get_children():
moods[node.get_data().name] = node.get_data()
character.custom_info['sound_mood_default'] = %DefaultMood.current_value
character.custom_info['sound_moods'] = moods
return character
func get_portrait_data() -> Dictionary:
if character_editor.selected_item and is_instance_valid(character_editor.selected_item):
return character_editor.selected_item.get_metadata(0)
return {}
func set_portrait_data(data:Dictionary) -> void:
if character_editor.selected_item and is_instance_valid(character_editor.selected_item):
character_editor.selected_item.set_metadata(0, data)
func _ready():
character_editor.portrait_selected.connect(_on_portrait_selected)
%PortraitMood.get_suggestions_func = mood_suggestions
%DefaultMood.get_suggestions_func = mood_suggestions
func _on_portrait_selected(portrait_name:String, data:Dictionary) -> void:
%PortraitMood.set_value(data.get('sound_mood', ''))
%PortraitMoodLabel.text = 'Mood for "%s":'%portrait_name
################################################################################
## OWN STUFF
################################################################################
func _on_AddMood_pressed() -> void:
create_mood_item({})
changed.emit()
func create_mood_item(data:Dictionary) -> void:
var new_mood = load(get_script().resource_path.get_base_dir().path_join('ui_mood_item.tscn')).instantiate()
%Moods.add_child(new_mood)
new_mood.load_data(data)
new_mood.duplicate.connect(duplicate_mood_item.bind(new_mood))
new_mood.changed.connect(emit_signal.bind('changed'))
func duplicate_mood_item(item:Control) -> void:
changed.emit()
create_mood_item(item.get_data())
func mood_suggestions(filter:String) -> Dictionary:
var suggestions := {}
for child in %Moods.get_children():
suggestions[child.get_data().name] = {'value':child.get_data().name}
return suggestions
func _on_PortraitMood_value_changed(property_name:String, value:Variant):
var data: Dictionary = get_portrait_data()
data['sound_mood'] = value
set_portrait_data(data)
changed.emit()
func _on_default_mood_value_changed(property_name:String, value:Variant):
var data: Dictionary = get_portrait_data()
data['sound_mood_default'] = value
set_portrait_data(data)
changed.emit()

View file

@ -0,0 +1,86 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://addons/dialogic/Events/Text/character_settings/character_settings_text.gd" id="1"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="2"]
[node name="TypingSounds" type="VBoxContainer"]
custom_minimum_size = Vector2(0, 130)
offset_right = 443.0
offset_bottom = 100.0
size_flags_vertical = 3
script = ExtResource("1")
[node name="DefaultMood" type="HBoxContainer" parent="."]
offset_right = 315.0
offset_bottom = 33.0
size_flags_horizontal = 0
[node name="DefaultMoodLabel" type="Label" parent="DefaultMood"]
offset_top = 3.0
offset_right = 111.0
offset_bottom = 29.0
size_flags_horizontal = 3
text = "Default mood:"
[node name="DefaultMood" parent="DefaultMood" instance=ExtResource("2")]
unique_name_in_owner = true
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 115.0
offset_right = 315.0
offset_bottom = 33.0
placeholder_text = "Select Mood"
[node name="PortraitMood" type="HBoxContainer" parent="."]
offset_top = 37.0
offset_right = 325.0
offset_bottom = 70.0
size_flags_horizontal = 0
[node name="PortraitMoodLabel" type="Label" parent="PortraitMood"]
unique_name_in_owner = true
offset_top = 3.0
offset_right = 121.0
offset_bottom = 29.0
size_flags_horizontal = 3
text = "Mood for \"sad\":"
[node name="PortraitMood" parent="PortraitMood" instance=ExtResource("2")]
unique_name_in_owner = true
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 125.0
offset_right = 325.0
offset_bottom = 33.0
placeholder_text = "Select Mood"
[node name="HBoxContainer" type="HBoxContainer" parent="."]
offset_top = 74.0
offset_right = 443.0
offset_bottom = 105.0
alignment = 2
[node name="AddMood" type="Button" parent="HBoxContainer"]
unique_name_in_owner = true
offset_left = 355.0
offset_right = 443.0
offset_bottom = 31.0
text = "Add mood"
[node name="ScrollContainer" type="ScrollContainer" parent="."]
offset_top = 109.0
offset_right = 443.0
offset_bottom = 130.0
size_flags_vertical = 3
horizontal_scroll_mode = 0
[node name="Moods" type="VBoxContainer" parent="ScrollContainer"]
unique_name_in_owner = true
offset_right = 443.0
offset_bottom = 21.0
size_flags_horizontal = 3
size_flags_vertical = 3
[connection signal="value_changed" from="DefaultMood/DefaultMood" to="." method="_on_default_mood_value_changed"]
[connection signal="value_changed" from="PortraitMood/PortraitMood" to="." method="_on_PortraitMood_value_changed"]
[connection signal="pressed" from="HBoxContainer/AddMood" to="." method="_on_AddMood_pressed"]

View file

@ -0,0 +1,81 @@
@tool
extends PanelContainer
signal duplicate
signal changed
func _ready():
add_theme_stylebox_override('panel', get_theme_stylebox("sub_inspector_bg12", "Editor"))
%Name.tooltip_text = "Mood name"
%Duplicate.icon = get_theme_icon("Duplicate", "EditorIcons")
%Duplicate.tooltip_text = "Duplicate"
%Duplicate.button_up.connect(emit_signal.bind("duplicate"))
%Delete.icon = get_theme_icon("Remove", "EditorIcons")
%Delete.tooltip_text = "Delete"
%ChangeSoundFolderButton.icon = get_theme_icon("Folder", "EditorIcons")
%ChangeSoundFolderButton.tooltip_text = "Change sounds folder"
%Fold.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
%Fold.tooltip_text = "Fold/Unfold"
%Play.icon = get_theme_icon("Play", "EditorIcons")
%Play.tooltip_text = "Preview"
_on_Fold_toggled(true)
%Name.text_changed.connect(something_changed)
%PitchBase.value_changed.connect(something_changed)
%PitchVariance.value_changed.connect(something_changed)
%VolumeBase.value_changed.connect(something_changed)
%VolumeVariance.value_changed.connect(something_changed)
func load_data(dict:Dictionary):
%Name.text = dict.get('name', '')
%SoundFolder.text = dict.get('sound_folder', '').get_file()
%SoundFolder.tooltip_text = dict.get('sound_folder', '')
%PitchBase.value = dict.get('pitch_base', 1)
%PitchVariance.value = dict.get('pitch_variance', 0)
%VolumeBase.value = dict.get('volume_base', 0)
%VolumeVariance.value = dict.get('volume_variance', 0)
func get_data():
var dict = {}
dict['name'] = %Name.text
dict['sound_folder'] = %SoundFolder.tooltip_text
dict['pitch_base'] = %PitchBase.value
dict['pitch_variance'] = %PitchVariance.value
dict['volume_base'] = %VolumeBase.value
dict['volume_variance'] = %VolumeVariance.value
return dict
func something_changed(fake_arg= ''): emit_signal("changed")
func _on_Fold_toggled(button_pressed):
%Fold.button_pressed = button_pressed
if button_pressed:
%Fold.icon = get_theme_icon("GuiVisibilityHidden", "EditorIcons")
else:
%Fold.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
%Content.visible = !button_pressed
func _on_Delete_pressed():
emit_signal("changed")
queue_free()
func open_file_folder_dialog():
find_parent('EditorView').godot_file_dialog(file_folder_selected, '', EditorFileDialog.FILE_MODE_OPEN_DIR, 'Select folder with sounds')
func file_folder_selected(path):
%SoundFolder.tooltip_text = path
%SoundFolder.text = path.get_file()
emit_signal("changed")
func preview():
if %SoundFolder.tooltip_text.is_empty(): return
$DialogicNode_TypeSounds.load_overwrite(get_data())
var preview_timer = Timer.new()
DialogicUtil.update_timer_process_callback(preview_timer)
add_child(preview_timer)
preview_timer.start(DialogicUtil.get_project_setting('text/speed', 0.01))
for i in range(20):
$DialogicNode_TypeSounds._on_continued_revealing_text("a")
await preview_timer.timeout
preview_timer.queue_free()

View file

@ -0,0 +1,157 @@
[gd_scene load_steps=6 format=3 uid="uid://x8alx74fnm10"]
[ext_resource type="Script" path="res://addons/dialogic/Events/Text/character_settings/ui_mood_item.gd" id="2"]
[ext_resource type="Script" path="res://addons/dialogic/Events/Text/node_type_sound.gd" id="3"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rsmy0"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(1, 0.365, 0.365, 1)
draw_center = false
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
corner_detail = 1
[sub_resource type="Image" id="Image_uegt4"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 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, 93, 93, 131, 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, 93, 93, 131, 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, 93, 93, 131, 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, 252, 255, 93, 93, 252, 255, 93, 93, 252, 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, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 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, 93, 93, 55, 255, 97, 97, 58, 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, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 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, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 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, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 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_osvex"]
image = SubResource("Image_uegt4")
[node name="MoodItem" type="PanelContainer"]
size_flags_horizontal = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_rsmy0")
script = ExtResource("2")
[node name="VBox" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="General" type="HBoxContainer" parent="VBox"]
layout_mode = 2
[node name="Name" type="LineEdit" parent="VBox/General"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "Mood name"
caret_blink = true
[node name="Play" type="Button" parent="VBox/General"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Preview"
icon = SubResource("ImageTexture_osvex")
[node name="Duplicate" type="Button" parent="VBox/General"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Duplicate"
icon = SubResource("ImageTexture_osvex")
[node name="Delete" type="Button" parent="VBox/General"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Delete"
icon = SubResource("ImageTexture_osvex")
[node name="Fold" type="Button" parent="VBox/General"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Fold/Unfold"
toggle_mode = true
button_pressed = true
icon = SubResource("ImageTexture_osvex")
[node name="Content" type="HBoxContainer" parent="VBox"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="Control" type="Control" parent="VBox/Content"]
layout_mode = 2
[node name="Content" type="VBoxContainer" parent="VBox/Content"]
layout_mode = 2
size_flags_horizontal = 3
[node name="SoundFolderSect" type="HBoxContainer" parent="VBox/Content/Content"]
layout_mode = 2
[node name="Label" type="Label" parent="VBox/Content/Content/SoundFolderSect"]
layout_mode = 2
size_flags_horizontal = 3
text = "Sounds folder"
[node name="SoundFolder" type="Label" parent="VBox/Content/Content/SoundFolderSect"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
horizontal_alignment = 2
[node name="ChangeSoundFolderButton" type="Button" parent="VBox/Content/Content/SoundFolderSect"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Change sounds folder"
icon = SubResource("ImageTexture_osvex")
[node name="Pitch" type="HBoxContainer" parent="VBox/Content/Content"]
layout_mode = 2
[node name="Label2" type="Label" parent="VBox/Content/Content/Pitch"]
layout_mode = 2
size_flags_horizontal = 3
text = "Pitch"
[node name="PitchBase" type="SpinBox" parent="VBox/Content/Content/Pitch"]
unique_name_in_owner = true
layout_mode = 2
step = 0.001
value = 1.0
[node name="PitchVariance" type="SpinBox" parent="VBox/Content/Content/Pitch"]
unique_name_in_owner = true
layout_mode = 2
step = 0.001
prefix = "+/-"
[node name="Volume" type="HBoxContainer" parent="VBox/Content/Content"]
layout_mode = 2
[node name="Label3" type="Label" parent="VBox/Content/Content/Volume"]
layout_mode = 2
size_flags_horizontal = 3
text = "Volume"
[node name="VolumeBase" type="SpinBox" parent="VBox/Content/Content/Volume"]
unique_name_in_owner = true
layout_mode = 2
min_value = -80.0
step = 0.001
suffix = "db"
[node name="VolumeVariance" type="SpinBox" parent="VBox/Content/Content/Volume"]
unique_name_in_owner = true
layout_mode = 2
step = 0.001
prefix = "+/-"
[node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="."]
script = ExtResource("3")
sounds = []
play_every_character = 0
[connection signal="pressed" from="VBox/General/Play" to="." method="preview"]
[connection signal="pressed" from="VBox/General/Duplicate" to="." method="_on_Duplicate_pressed"]
[connection signal="pressed" from="VBox/General/Delete" to="." method="_on_Delete_pressed"]
[connection signal="toggled" from="VBox/General/Fold" to="." method="_on_Fold_toggled"]
[connection signal="pressed" from="VBox/Content/Content/SoundFolderSect/ChangeSoundFolderButton" to="." method="open_file_folder_dialog"]

View file

@ -0,0 +1,253 @@
@tool
class_name DialogicTextEvent
extends DialogicEvent
## Event that stores text. Can be said by a character.
## Should be shown by a DialogicNode_DialogText.
### Settings
## This is the content of the text event.
## It is supposed to be displayed by a DialogicNode_DialogText node.
## That means you can use bbcode, but also some custom commands.
var text: String = ""
## If this is not null, the given character (as a resource) will be associated with this event.
## The DialogicNode_NameLabel will show the characters display_name. If a typing sound is setup,
## it will play.
var character: DialogicCharacter = null
## If a character is set, this setting can change the portrait of that character.
var portrait: String = ""
### Helpers
## This returns the unique name_identifier of the character. This is used by the editor.
var _character_from_directory: String:
get:
for item in _character_directory.keys():
if _character_directory[item]['resource'] == character:
return item
break
return ""
set(value):
_character_from_directory = value
if value in _character_directory.keys():
character = _character_directory[value]['resource']
## Used by [_character_from_directory] to fetch the unique name_identifier or resource.
var _character_directory: Dictionary = {}
################################################################################
## EXECUTION
################################################################################
func _execute() -> void:
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'))
if character:
if dialogic.has_subsystem('Styles') and character.custom_info.get('style', null):
dialogic.Styles.change_style(character.custom_info.style)
dialogic.Text.update_name_label(character)
if portrait and dialogic.has_subsystem('Portraits') and dialogic.Portraits.is_character_joined(character):
dialogic.Portraits.change_portrait(character, portrait)
var check_portrait = portrait if !portrait.is_empty() else dialogic.current_state_info['portraits'].get(character.resource_path, {}).get('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_name_label(null)
# this will only do something if the rpg portrait mode is enabled
if dialogic.has_subsystem('Portraits'):
dialogic.Portraits.update_rpg_portrait_mode(character, portrait)
# RENDER DIALOG
# Placeholder wrap. Replace with a loop iterating over text event's lines. - KvaGram
var index: int = 0
if true:
var final_text: String = get_property_translated('text')
if dialogic.has_subsystem('VAR'):
final_text = dialogic.VAR.parse_variables(final_text)
if dialogic.has_subsystem('Glossary'):
final_text = dialogic.Glossary.parse_glossary(final_text)
dialogic.Text.update_dialog_text(dialogic.Text.color_names(final_text))
#Plays the audio region for the current line.
if dialogic.has_subsystem('Voice') and dialogic.Voice.is_voiced(dialogic.current_event_idx):
dialogic.Voice.play_voice_region(index) #voice data is set by voice event.
await dialogic.Text.text_finished
#end of dialog
if dialogic.has_subsystem('Choices') and dialogic.Choices.is_question(dialogic.current_event_idx):
dialogic.Text.show_next_indicators(true)
dialogic.Choices.show_current_choices()
dialogic.current_state = dialogic.states.AWAITING_CHOICE
elif DialogicUtil.get_project_setting('dialogic/text/autocontinue', false):
dialogic.Text.show_next_indicators(false, true)
var wait:float = DialogicUtil.get_project_setting('dialogic/text/autocontinue_delay', 1)
# if voiced, grab remaining time left on the voiceed line's audio region - KvaGram
if dialogic.has_subsystem('Voice') and dialogic.Voice.is_voiced(dialogic.current_event_idx):
#autocontinue settings is set as minimal. change or keep this? - Kvagram
wait = max(wait, dialogic.Voice.get_remaining_time())
await dialogic.get_tree().create_timer(wait, true, DialogicUtil.is_physics_timer()).timeout
dialogic.handle_next_event()
else:
dialogic.Text.show_next_indicators()
finish()
################################################################################
## INITIALIZE
################################################################################
func _init() -> void:
event_name = "Text"
set_default_color('Color1')
event_category = Category.Main
event_sorting_index = 0
help_page_path = "https://dialogic.coppolaemilio.com/documentation/Events/000/"
continue_at_end = false
################################################################################
## SAVING/LOADING
################################################################################
func to_text() -> String:
if character:
var name = ""
for path in _character_directory.keys():
if _character_directory[path]['resource'] == character:
name = path
break
if name.count(" ") > 0:
name = '"' + name + '"'
if not portrait.is_empty():
return name+" ("+portrait+"): "+text.replace("\n", "\\\n")
return name+": "+text.replace("\n", "\\\n")
return text.replace("\n", "\\\n")
func from_text(string:String) -> void:
_character_directory = {}
if Engine.is_editor_hint() == false:
_character_directory = Dialogic.character_directory
else:
_character_directory = self.get_meta("editor_character_directory")
var reg := RegEx.new()
# Reference regex without Godot escapes: ((")?(?<name>(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(\W*\((?<portrait>.*)\))?\s*(?<!\\):)?(?<text>.*)
reg.compile("((\")?(?<name>(?(2)[^\"\\n]*|[^(: \\n]*))(?(2)\"|)(\\W*\\((?<portrait>.*)\\))?\\s*(?<!\\\\):)?(?<text>.*)")
var result := reg.search(string)
if result and !result.get_string('name').is_empty():
var name := result.get_string('name').strip_edges()
if _character_directory != null:
if _character_directory.size() > 0:
character = null
if _character_directory.has(name):
character = _character_directory[name]['resource']
else:
name = name.replace('"', "")
# First do a full search to see if more of the path is there then necessary:
for character in _character_directory:
if name in _character_directory[character]['full_path']:
character = _character_directory[character]['resource']
break
# If it doesn't exist, at runtime we'll consider it a guest and create a temporary character
if character == null:
if Engine.is_editor_hint() == false:
character = DialogicCharacter.new()
character.display_name = name
var entry: Dictionary = {}
entry['resource'] = character
entry['full_path'] = "runtime://" + name
_character_directory[name] = entry
if !result.get_string('portrait').is_empty():
portrait = result.get_string('portrait').strip_edges()
if result:
text = result.get_string('text').replace("\\\n", "\n").strip_edges()
func is_valid_event(string:String) -> bool:
return true
func is_string_full_event(string:String) -> bool:
return !string.ends_with('\\')
################################################################################
## TRANSLATIONS
################################################################################
func _get_translatable_properties() -> Array:
return ['text']
func _get_property_original_translation(property:String) -> String:
match property:
'text':
return text
return ''
################################################################################
## EVENT EDITOR
################################################################################
func build_event_editor():
add_header_edit('_character_from_directory', ValueType.ComplexPicker, '', '',
{'file_extension' : '.dch',
'suggestions_func' : get_character_suggestions,
'empty_text' : '(No one)',
'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg")})
add_header_edit('portrait', ValueType.ComplexPicker, '', '',
{'suggestions_func' : get_portrait_suggestions,
'placeholder' : "Don't change",
'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")},
'character != null and !has_no_portraits()')
add_body_edit('text', ValueType.MultilineText)
func has_no_portraits() -> bool:
return character and character.portraits.is_empty()
func get_character_suggestions(search_text:String) -> Dictionary:
var suggestions := {}
#override the previous _character_directory with the meta, specifically for searching otherwise new nodes wont work
_character_directory = Engine.get_meta('dialogic_character_directory')
var icon = load("res://addons/dialogic/Editor/Images/Resources/character.svg")
suggestions['(No one)'] = {'value':null, 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
for resource in _character_directory.keys():
suggestions[resource] = {
'value' : resource,
'tooltip' : _character_directory[resource]['full_path'],
'icon' : icon.duplicate()}
return suggestions
func get_portrait_suggestions(search_text:String) -> Dictionary:
var suggestions := {}
var icon = load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")
suggestions["Don't change"] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]}
if character != null:
for portrait in character.portraits:
suggestions[portrait] = {'value':portrait, 'icon':icon}
return suggestions

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://qjpkitm0gily"
path="res://.godot/imported/icon.png-2a0e30dbedb6741edbe8b5e3b94a5634.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Events/Text/icon.png"
dest_files=["res://.godot/imported/icon.png-2a0e30dbedb6741edbe8b5e3b94a5634.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1,18 @@
@tool
extends DialogicIndexer
func _get_events() -> Array:
return [this_folder.path_join('event_text.gd')]
func _get_subsystems() -> Array:
return [{'name':'Text', 'script':this_folder.path_join('subsystem_text.gd')}]
func _get_settings_pages() -> Array:
return [this_folder.path_join('settings_text.tscn')]
func _get_character_editor_tabs() -> Array:
return [this_folder.path_join('character_settings/character_settings_text.tscn')]

View file

@ -0,0 +1,147 @@
extends RichTextLabel
class_name DialogicNode_DialogText
enum ALIGNMENT {LEFT, CENTER, RIGHT}
@export var Align : ALIGNMENT = ALIGNMENT.LEFT
@onready var timer :Timer = null
var effect_regex = RegEx.new()
var modifier_words_select_regex = RegEx.new()
var effects:Array = []
var speed:float = 0.01
signal started_revealing_text()
signal continued_revealing_text(new_character)
signal finished_revealing_text()
func _ready() -> void:
# add to necessary
add_to_group('dialogic_dialog_text')
bbcode_enabled = true
text = ""
# setup my timer
timer = Timer.new()
add_child(timer)
timer.wait_time = 0.01
timer.one_shot = true
DialogicUtil.update_timer_process_callback(timer)
timer.timeout.connect(continue_reveal)
# compile effects regex
effect_regex.compile("(?<!\\\\)\\[\\s*(?<command>mood|portrait|speed|signal|pause)\\s*(=\\s*(?<value>.+?)\\s*)?\\]")
# compule modifier regexs
modifier_words_select_regex.compile("(?<!\\\\)\\[[^\\[\\]]+(,[^\\]]*)\\]")
# this is called by the DialogicGameHandler to set text
func reveal_text(_text:String) -> void:
speed = DialogicUtil.get_project_setting('dialogic/text/speed', 0.01)
text = parse_effects(parse_modifiers(_text))
if Align == ALIGNMENT.CENTER:
text = '[center]'+text
elif Align == ALIGNMENT.RIGHT:
text = '[right]'+text
visible_characters = 0
if speed <= 0:
timer.start(0.01)
else:
timer.start(speed)
emit_signal('started_revealing_text')
# called by the timer -> reveals more text
func continue_reveal() -> void:
if visible_characters < get_total_character_count():
visible_characters += 1
emit_signal("continued_revealing_text", text[visible_characters-1])
execute_effects()
if timer.is_stopped():
if speed <= 0:
continue_reveal()
else:
timer.start(speed)
else:
finish_text()
# shows all the text imidiatly
# called by this thing itself or the DialogicGameHandler
func finish_text() -> void:
visible_ratio = 1
execute_effects(true)
timer.stop()
Dialogic.current_state = Dialogic.states.IDLE
emit_signal("finished_revealing_text")
func parse_effects(_text:String) -> String:
effects.clear()
var position_correction = 0
for effect_match in effect_regex.search_all(_text):
# append [index, command, value] to effects array
effects.append([effect_match.get_start()-position_correction, effect_match.get_string('command'), effect_match.get_string('value').strip_edges()])
## TODO MIGHT BE BROKEN, because I had to replace string.erase for godot 4
_text = _text.substr(0,effect_match.get_start()-position_correction)+_text.substr(effect_match.get_start()-position_correction+len(effect_match.get_string()))
position_correction += len(effect_match.get_string())
_text = _text.replace('\\[', '[')
return _text
func execute_effects(skip :bool= false) -> void:
# might have to execute multiple effects
while effects and (visible_characters >= effects.front()[0] or visible_characters== -1):
var effect = effects.pop_front()
match effect[1]:
'pause':
if skip:
continue
if effect[2].is_valid_float():
timer.start(effect[2].to_float())
else:
timer.start(1)
'speed':
if skip:
continue
if effect[2].is_valid_float():
speed = effect[2].to_float()
else:
speed = DialogicUtil.get_project_setting('dialogic/text/speed', 0.01)
'signal':
Dialogic.emit_signal("text_signal", effect[2])
'portrait':
if effect[2]:
if Dialogic.current_state_info.get('character', null):
Dialogic.Portraits.change_portrait(load(Dialogic.current_state_info.character), effect[2])
'mood':
if effect[2]:
if Dialogic.current_state_info.get('character', null):
var Character = load(Dialogic.current_state_info.character)
Dialogic.Text.update_typing_sound_mood(Character.custom_info.get('sound_moods', {}).get(effect[2], {}))
func parse_modifiers(_text:String) -> String:
# [word, select] effect
for replace_mod_match in modifier_words_select_regex.search_all(_text):
var string = replace_mod_match.get_string().trim_prefix("[").trim_suffix("]")
string = string.replace('\\,', '<comma>')
var list = string.split(',')
var item = list[randi()%len(list)]
item = item.replace('<comma>', ',')
_text = _text.replace(replace_mod_match.get_string(), item.strip_edges())
# [br] effect
_text = _text.replace('[br]', '\n')
return _text
func pause() -> void:
timer.stop()
func resume() -> void:
continue_reveal()

View file

@ -0,0 +1,19 @@
@tool
extends Node
class_name DialogicNode_InputHandler
################################################################################
## INPUT
################################################################################
func _input(event:InputEvent) -> void:
if Input.is_action_just_pressed(DialogicUtil.get_project_setting('dialogic/text/input_action', 'dialogic_default_action')):
if Dialogic.paused: return
if Dialogic.current_state == Dialogic.states.IDLE:
Dialogic.handle_next_event()
elif Dialogic.current_state == Dialogic.states.SHOWING_TEXT:
if DialogicUtil.get_project_setting('dialogic/text/skippable', true):
Dialogic.Text.skip_text_animation()

View file

@ -0,0 +1,19 @@
extends Label
class_name DialogicNode_NameLabel
# If true, the label will be hidden if no character speaks.
@export var hide_when_empty := true
@export var use_character_color := true
func _ready():
add_to_group('dialogic_name_label')
text = ""
func _set(property, what):
if property == 'text' and typeof(what) == TYPE_STRING:
text = what
if hide_when_empty:
visible = !what.is_empty()
return true

View file

@ -0,0 +1,59 @@
class_name DNextIndicator
extends Control
@icon("res://addons/dialogic/Events/Text/node_next_indicator_icon.svg")
## Node that is shown when the text is fully revealed.
## The default implementation allows to set an icon and animation.
## If true the next indicator will also be shown if the text is a question.
@export var show_on_questions := false
## If true the next indicator will be shown even if dialogic will autocontinue.
@export var show_on_autocontinue := false
## What animation should the indicator do.
@export_enum('bounce', 'blink', 'none') var animation := 0
## Set the image to use as the indicator.
@export var texture := preload("res://addons/dialogic/Example Assets/next-indicator/next-indicator.png")
func _ready():
add_to_group('dialogic_next_indicator')
# Creating texture
if texture:
var icon := TextureRect.new()
icon.ignore_texture_size = true
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.size = Vector2(32,32)
icon.position -= icon.size
add_child(icon)
icon.texture = texture
hide()
visibility_changed.connect(_on_visibility_changed)
func _on_visibility_changed():
if visible:
play_animation(animation, 1.0)
func play_animation(animation: int, time:float) -> void:
if animation == 0:
var tween:Tween = (create_tween() as Tween)
var distance := 4
tween.set_parallel(false)
tween.set_trans(Tween.TRANS_SINE)
tween.set_ease(Tween.EASE_IN_OUT)
tween.set_loops()
tween.tween_property(self, 'position', Vector2(0,distance), time*0.3).as_relative()
tween.tween_property(self, 'position', - Vector2(0,distance), time*0.3).as_relative()
if animation == 1:
var tween:Tween = (create_tween() as Tween)
tween.set_parallel(false)
tween.set_trans(Tween.TRANS_SINE)
tween.set_ease(Tween.EASE_IN_OUT)
tween.set_loops()
tween.tween_property(self, 'modulate:a', 0, time*0.3)
tween.tween_property(self, 'modulate:a', 1, time*0.3)

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.0971 4L5.00122 4C4.26622 4 3.78236 4.76633 4.09836 5.42993L7.00122 11.604C7.36317 12.3641 8.63928 12.3641 9.00122 11.604L12 5.42993C12.316 4.76633 11.8321 4 11.0971 4Z" stroke="#7DFFF7" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 321 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ekyju8t2e3we"
path="res://.godot/imported/node_next_indicator_icon.svg-c0c6c044917169a17adca537aa56f3e7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/dialogic/Events/Text/node_next_indicator_icon.svg"
dest_files=["res://.godot/imported/node_next_indicator_icon.svg-c0c6c044917169a17adca537aa56f3e7.ctex"]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,111 @@
@tool
class_name DialogicNode_TypeSounds
extends AudioStreamPlayer
## Node that allows playing sounds when text characters are revealed.
## Should be the child of a DialogicNode_DialogText node!
## Usefull if you want to change the sounds ot different node's sounds
@export var enabled := true
## If true, interrupts the current sound to play a new one
@export var interrupt := true
## Array of sounds. Will pick a random one each time.
@export var sounds: Array
## A sound to be played as the last sound.
@export var end_sound:AudioStream
## Determines how many characters are between each sound. Default is 1 for playing it every character.
@export var play_every_character := 1
## Allows changing the pitch by a random value from (pitch - pitch_variance) to (pitch + pitch_variance)
@export_range(0, 3, 0.01) var pitch_variance := 0.0
## Allows changing the volume by a random value from (volume - volume_variance) to (volume + volume_variance)
@export_range(0, 10, 0.01) var volume_variance := 0.0
## Allow ters that don't increase the 'characters_since_last_sound' variable, useful for the space or fullstop
@export var ignore_characters:String = ' .,'
var dialogic:Node #singleton refrence
var sound_finished = true
var characters_since_last_sound: int = 0
var base_pitch = pitch_scale
var base_volume = volume_db
var RNG = RandomNumberGenerator.new()
var current_overwrite_data = {}
func _ready():
# add to necessary group
add_to_group('dialogic_type_sounds')
if !Engine.is_editor_hint() and get_parent() is DialogicNode_DialogText:
dialogic = get_node("/root/Dialogic")
get_parent().started_revealing_text.connect(_on_started_revealing_text)
get_parent().continued_revealing_text.connect(_on_continued_revealing_text)
get_parent().finished_revealing_text.connect(_on_finished_revealing_text)
func _on_started_revealing_text() -> void:
if !enabled:
return
characters_since_last_sound = 0
func _on_continued_revealing_text(new_character) -> void:
#pretty obvious
if !enabled:
return
#don't play if a voice-track is running
if !Engine.is_editor_hint() and get_parent() is DialogicNode_DialogText:
if dialogic.has_subsystem("Voice") and dialogic.Voice.is_running():
return
#if sound playing and can't interrupt
if !sound_finished and !interrupt:
return
#if no sounds were given
if current_overwrite_data.get('sounds', sounds).size() == 0:
return
#if the new character is not allowed
if new_character in ignore_characters:
return
characters_since_last_sound += 1
if characters_since_last_sound < play_every_character:
return
characters_since_last_sound = 0
#choose the random sound
stream = current_overwrite_data.get('sounds', sounds)[RNG.randi_range(0, sounds.size() - 1)]
#choose a random pitch and volume
pitch_scale = max(0, current_overwrite_data.get('pitch_base', base_pitch) + current_overwrite_data.get('pitch_variance', pitch_variance) * RNG.randf_range(-1.0, 1.0))
volume_db = current_overwrite_data.get('volume_base', base_volume) + current_overwrite_data.get('volume_variance',volume_variance) * RNG.randf_range(-1.0, 1.0)
#play the sound
play()
sound_finished = false
func _on_Sound_finished() -> void:
sound_finished = true
func _on_finished_revealing_text() -> void:
if end_sound != null:
stream = end_sound
pitch_variance = 1.0
volume_db = 0.0
play()
func _get_configuration_warning():
if not get_parent() is DialogicNode_DialogText:
return "This should be the child of a DialogText node!"
return ""
func load_sounds_from_folder(folder:String):
var x_sounds = []
for i in DialogicUtil.listdir(folder, true, false, true):
if i.get_extension().to_lower() in ['mp3', 'wav', 'ogg'] and load(i) is AudioStream:
x_sounds.append(load(i))
return x_sounds
func load_overwrite(dictionary:Dictionary) -> void:
current_overwrite_data = dictionary
if dictionary.has('sound_folder'):
current_overwrite_data['sounds'] = load_sounds_from_folder(dictionary.sound_folder)

View file

@ -0,0 +1,51 @@
@tool
extends HBoxContainer
func refresh():
%Info.add_theme_color_override('default_color', get_theme_color("accent_color", "Editor"))
%DefaultSpeed.value = DialogicUtil.get_project_setting('dialogic/text/speed', 0.01)
%Skippable.button_pressed = DialogicUtil.get_project_setting('dialogic/text/skippable', true)
%Autocontinue.button_pressed = DialogicUtil.get_project_setting('dialogic/text/autocontinue', false)
%AutocontinueDelay.value = DialogicUtil.get_project_setting('dialogic/text/autocontinue_delay', 1)
%AutocolorNames.button_pressed = DialogicUtil.get_project_setting('dialogic/text/autocolor_names', false)
%InputAction.resource_icon = get_theme_icon("Mouse", "EditorIcons")
%InputAction.set_value(DialogicUtil.get_project_setting('dialogic/text/input_action', 'dialogic_default_action'))
%InputAction.get_suggestions_func = suggest_actions
func _on_AutocontinueDelay_value_changed(value):
ProjectSettings.set_setting('dialogic/text/autocontinue_delay', value)
ProjectSettings.save()
func _on_Autocontinue_toggled(button_pressed):
ProjectSettings.set_setting('dialogic/text/autocontinue', button_pressed)
ProjectSettings.save()
func _on_Skippable_toggled(button_pressed):
ProjectSettings.set_setting('dialogic/text/skippable', button_pressed)
ProjectSettings.save()
func _on_DefaultSpeed_value_changed(value):
ProjectSettings.set_setting('dialogic/text/speed', value)
ProjectSettings.save()
func _on_InputAction_value_changed(property_name, value):
ProjectSettings.set_setting('dialogic/text/input_action', value)
ProjectSettings.save()
func suggest_actions(search):
var suggs = {}
for prop in ProjectSettings.get_property_list():
if prop.name.begins_with('input/'):
suggs[prop.name.trim_prefix('input/')] = {'value':prop.name.trim_prefix('input/')}
return suggs
func _on_AutocolorNames_toggled(button_pressed):
ProjectSettings.set_setting('dialogic/text/autocolor_names', button_pressed)
ProjectSettings.save()

View file

@ -0,0 +1,183 @@
[gd_scene load_steps=6 format=3]
[ext_resource type="Script" path="res://addons/dialogic/Events/Text/settings_text.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_oy8x6"]
content_margin_left = 5.0
content_margin_top = 5.0
content_margin_right = 5.0
content_margin_bottom = 5.0
bg_color = Color(0.545098, 0.545098, 0.545098, 0.211765)
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5tav8"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mamwx"]
[node name="DialogText" type="HBoxContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource("2")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
offset_right = 506.0
offset_bottom = 600.0
size_flags_horizontal = 3
[node name="Title" type="Label" parent="VBoxContainer"]
offset_right = 506.0
offset_bottom = 36.0
theme_override_styles/normal = SubResource("StyleBoxFlat_oy8x6")
text = "Dialog Text Settings"
[node name="DefaultSpeed" type="HBoxContainer" parent="VBoxContainer"]
offset_top = 40.0
offset_right = 506.0
offset_bottom = 71.0
[node name="Label" type="Label" parent="VBoxContainer/DefaultSpeed"]
offset_top = 2.0
offset_right = 419.0
offset_bottom = 28.0
size_flags_horizontal = 3
text = "Default speed"
[node name="DefaultSpeed" type="SpinBox" parent="VBoxContainer/DefaultSpeed"]
unique_name_in_owner = true
offset_left = 423.0
offset_right = 506.0
offset_bottom = 31.0
step = 0.001
[node name="InputAction" type="HBoxContainer" parent="VBoxContainer"]
offset_top = 75.0
offset_right = 506.0
offset_bottom = 108.0
[node name="Label2" type="Label" parent="VBoxContainer/InputAction"]
offset_top = 3.0
offset_right = 302.0
offset_bottom = 29.0
size_flags_horizontal = 3
text = "Input action"
[node name="InputAction" parent="VBoxContainer/InputAction" instance=ExtResource("3")]
unique_name_in_owner = true
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 306.0
offset_right = 506.0
offset_bottom = 33.0
[node name="Skippable" type="HBoxContainer" parent="VBoxContainer"]
offset_top = 112.0
offset_right = 506.0
offset_bottom = 138.0
[node name="Label3" type="Label" parent="VBoxContainer/Skippable"]
offset_right = 478.0
offset_bottom = 26.0
size_flags_horizontal = 3
text = "Skippable"
[node name="Skippable" type="CheckBox" parent="VBoxContainer/Skippable"]
unique_name_in_owner = true
offset_left = 482.0
offset_right = 506.0
offset_bottom = 26.0
[node name="ColorNames" type="HBoxContainer" parent="VBoxContainer"]
offset_top = 142.0
offset_right = 506.0
offset_bottom = 168.0
[node name="Label4" type="Label" parent="VBoxContainer/ColorNames"]
offset_right = 478.0
offset_bottom = 26.0
size_flags_horizontal = 3
text = "Autocolor names"
[node name="AutocolorNames" type="CheckBox" parent="VBoxContainer/ColorNames"]
unique_name_in_owner = true
offset_left = 482.0
offset_right = 506.0
offset_bottom = 26.0
[node name="AutoContinue" type="HBoxContainer" parent="VBoxContainer"]
offset_top = 172.0
offset_right = 506.0
offset_bottom = 203.0
[node name="Label5" type="Label" parent="VBoxContainer/AutoContinue"]
offset_top = 2.0
offset_right = 391.0
offset_bottom = 28.0
size_flags_horizontal = 3
text = "Autocontinue"
[node name="Autocontinue" type="CheckBox" parent="VBoxContainer/AutoContinue"]
unique_name_in_owner = true
offset_left = 395.0
offset_right = 419.0
offset_bottom = 31.0
[node name="AutocontinueDelay" type="SpinBox" parent="VBoxContainer/AutoContinue"]
unique_name_in_owner = true
offset_left = 423.0
offset_right = 506.0
offset_bottom = 31.0
step = 0.01
suffix = "s"
[node name="VSeparator" type="VSeparator" parent="."]
offset_left = 510.0
offset_right = 514.0
offset_bottom = 600.0
[node name="VBoxContainer2" type="VBoxContainer" parent="."]
offset_left = 518.0
offset_right = 1024.0
offset_bottom = 600.0
size_flags_horizontal = 3
[node name="Title" type="Label" parent="VBoxContainer2"]
offset_right = 506.0
offset_bottom = 36.0
theme_override_styles/normal = SubResource("StyleBoxFlat_oy8x6")
text = "Tipps and tricks:"
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer2"]
offset_top = 40.0
offset_right = 506.0
offset_bottom = 600.0
size_flags_vertical = 3
horizontal_scroll_mode = 0
[node name="Info" type="RichTextLabel" parent="VBoxContainer2/ScrollContainer"]
unique_name_in_owner = true
offset_right = 506.0
offset_bottom = 560.0
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_colors/default_color = Color(1, 1, 1, 0.611765)
theme_override_styles/focus = SubResource("StyleBoxEmpty_5tav8")
theme_override_styles/normal = SubResource("StyleBoxEmpty_mamwx")
bbcode_enabled = true
text = "There are a number of useful commands you can use in text events:
- [b][speed=x][/b] changes the speed until the next speed command or the end of the text
- [b][br][/b] adds a line break
- [b][Word, Other Word][/b] selects a random entry from the comma separated list. You can write \\, if one of the entry should contain a comma.
- [b][portrait=sad][/b] changes the portrait of the speaker to the given portrait.
- [b][pause=x][/b] pauses for x seconds.
- [b][mood=sad][/b] changes the typing sound mood.
- [b][signal=arg][/b] emits the Dialogic.text_signal signal with the given argument.
Also you can use the bbcode commands like [i][ i ] [/i], [b][ b ][/b], [u][ u ] [/u], [color=orange][ color=orange ][/color], [[wave]wave[/wave]], or [ img] [img=30x30]res://addons/dialogic/Editor/Images/plugin-icon.svg[/img] [/img].
Learn more about bbcode in the official godot docs."
[connection signal="value_changed" from="VBoxContainer/DefaultSpeed/DefaultSpeed" to="." method="_on_DefaultSpeed_value_changed"]
[connection signal="value_changed" from="VBoxContainer/InputAction/InputAction" to="." method="_on_InputAction_value_changed"]
[connection signal="toggled" from="VBoxContainer/Skippable/Skippable" to="." method="_on_Skippable_toggled"]
[connection signal="toggled" from="VBoxContainer/ColorNames/AutocolorNames" to="." method="_on_AutocolorNames_toggled"]
[connection signal="toggled" from="VBoxContainer/AutoContinue/Autocontinue" to="." method="_on_Autocontinue_toggled"]
[connection signal="value_changed" from="VBoxContainer/AutoContinue/AutocontinueDelay" to="." method="_on_AutocontinueDelay_value_changed"]

View file

@ -0,0 +1,148 @@
extends DialogicSubsystem
# used to color names without searching for all characters each time
var character_colors := {}
var color_regex := RegEx.new()
var text_already_read:bool = false
signal text_finished
signal speaking_character(argument)
####################################################################################################
## STATE
####################################################################################################
func clear_game_state() -> void:
update_dialog_text('', true)
update_name_label(null)
dialogic.current_state_info['character'] = null
dialogic.current_state_info['text'] = ''
func load_game_state() -> void:
update_dialog_text(dialogic.current_state_info.get('text', ''), true)
var character:DialogicCharacter = null
if dialogic.current_state_info.get('character', null):
character = load(dialogic.current_state_info.get('character', null))
if character:
update_name_label(character)
func pause() -> void:
for text_node in get_tree().get_nodes_in_group('dialogic_dialog_text'):
if text_node.is_visible_in_tree():
text_node.pause()
func resume() -> void:
for text_node in get_tree().get_nodes_in_group('dialogic_dialog_text'):
if text_node.is_visible_in_tree():
text_node.resume()
####################################################################################################
## MAIN METHODS
####################################################################################################
## Shows the given text on all visible DialogText nodes.
## Instant can be used to skip all reveiling (effects won't work).
func update_dialog_text(text:String, instant:bool= false) -> void:
if !instant: dialogic.current_state = dialogic.states.SHOWING_TEXT
dialogic.current_state_info['text'] = text
for text_node in get_tree().get_nodes_in_group('dialogic_dialog_text'):
if text_node.is_visible_in_tree():
if instant:
text_node.text = text
else:
text_node.reveal_text(text)
if !text_node.finished_revealing_text.is_connected(_on_dialog_text_finished):
text_node.finished_revealing_text.connect(_on_dialog_text_finished)
func _on_dialog_text_finished():
text_finished.emit()
func update_name_label(character:DialogicCharacter) -> void:
for name_label in get_tree().get_nodes_in_group('dialogic_name_label'):
if character:
dialogic.current_state_info['character'] = character.resource_path
if dialogic.has_subsystem('VAR'):
name_label.text = dialogic.VAR.parse_variables(character.display_name)
else:
name_label.text = character.display_name
if !'use_character_color' in name_label or name_label.use_character_color:
name_label.self_modulate = character.color
speaking_character.emit(name_label.text)
else:
dialogic.current_state_info['character'] = null
name_label.text = ''
name_label.self_modulate = Color(1,1,1,1)
speaking_character.emit("(Nobody)")
func update_typing_sound_mood(mood:Dictionary = {}) -> void:
for typing_sound in get_tree().get_nodes_in_group('dialogic_type_sounds'):
typing_sound.load_overwrite(mood)
func hide_text_boxes() -> void:
for name_label in get_tree().get_nodes_in_group('dialogic_name_label'):
name_label.text = ""
for text_node in get_tree().get_nodes_in_group('dialogic_dialog_text'):
text_node.get_parent().visible = false
func show_text_boxes() -> void:
for text_node in get_tree().get_nodes_in_group('dialogic_dialog_text'):
text_node.get_parent().visible = true
func show_next_indicators(question=false, autocontinue=false) -> void:
for next_indicator in get_tree().get_nodes_in_group('dialogic_next_indicator'):
if (question and 'show_on_questions' in next_indicator and next_indicator.show_on_questions) or \
(autocontinue and 'show_on_autocontinue' in next_indicator and next_indicator.show_on_autocontinue) or (!question and !autocontinue):
next_indicator.show()
func hide_next_indicators(fake_arg=null) -> void:
for next_indicator in get_tree().get_nodes_in_group('dialogic_next_indicator'):
next_indicator.hide()
####################################################################################################
## HELPERS
####################################################################################################
func skip_text_animation() -> void:
for text_node in get_tree().get_nodes_in_group('dialogic_dialog_text'):
if text_node.is_visible_in_tree():
text_node.finish_text()
if dialogic.has_subsystem('Voice'):
dialogic.Voice.stop_audio()
func get_current_speaker() -> DialogicCharacter:
return (load(dialogic.current_state_info['character']) as DialogicCharacter)
func _ready():
update_character_names()
Dialogic.event_handled.connect(hide_next_indicators)
func color_names(text:String) -> String:
if !DialogicUtil.get_project_setting('dialogic/text/autocolor_names', false):
return text
var counter = 0
for result in color_regex.search_all(text):
text = text.insert(result.get_start("name")+((9+8+8)*counter), '[color=#' + character_colors[result.get_string('name')].to_html() + ']')
text = text.insert(result.get_end("name")+9+8+((9+8+8)*counter), '[/color]')
counter += 1
return text
func update_character_names() -> void:
#don't do this at all if we're not using autocolor names to begin with
if !DialogicUtil.get_project_setting('dialogic/text/autocolor_names', false):
return
character_colors = {}
for dch_path in DialogicUtil.list_resources_of_type('.dch'):
var dch = (load(dch_path) as DialogicCharacter)
if dch.display_name:
character_colors[dch.display_name] = dch.color
for nickname in dch.nicknames:
if nickname.strip_edges():
character_colors[nickname.strip_edges()] = dch.color
color_regex.compile('(?<=\\W|^)(?<name>'+str(character_colors.keys()).trim_prefix('["').trim_suffix('"]').replace('", "', '|')+')(?=\\W|$)')