Add addons
This commit is contained in:
parent
2c862b2235
commit
3367bad24f
527 changed files with 36197 additions and 0 deletions
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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()
|
||||
|
|
@ -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"]
|
||||
253
Sprout/addons/dialogic/Events/Text/event_text.gd
Normal file
253
Sprout/addons/dialogic/Events/Text/event_text.gd
Normal 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
|
||||
BIN
Sprout/addons/dialogic/Events/Text/icon.png
Normal file
BIN
Sprout/addons/dialogic/Events/Text/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 314 B |
34
Sprout/addons/dialogic/Events/Text/icon.png.import
Normal file
34
Sprout/addons/dialogic/Events/Text/icon.png.import
Normal 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
|
||||
18
Sprout/addons/dialogic/Events/Text/index.gd
Normal file
18
Sprout/addons/dialogic/Events/Text/index.gd
Normal 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')]
|
||||
147
Sprout/addons/dialogic/Events/Text/node_dialog_text.gd
Normal file
147
Sprout/addons/dialogic/Events/Text/node_dialog_text.gd
Normal 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()
|
||||
19
Sprout/addons/dialogic/Events/Text/node_input_handler.gd
Normal file
19
Sprout/addons/dialogic/Events/Text/node_input_handler.gd
Normal 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()
|
||||
19
Sprout/addons/dialogic/Events/Text/node_name_label.gd
Normal file
19
Sprout/addons/dialogic/Events/Text/node_name_label.gd
Normal 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
|
||||
59
Sprout/addons/dialogic/Events/Text/node_next_indicator.gd
Normal file
59
Sprout/addons/dialogic/Events/Text/node_next_indicator.gd
Normal 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)
|
||||
|
|
@ -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 |
|
|
@ -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
|
||||
111
Sprout/addons/dialogic/Events/Text/node_type_sound.gd
Normal file
111
Sprout/addons/dialogic/Events/Text/node_type_sound.gd
Normal 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)
|
||||
51
Sprout/addons/dialogic/Events/Text/settings_text.gd
Normal file
51
Sprout/addons/dialogic/Events/Text/settings_text.gd
Normal 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()
|
||||
183
Sprout/addons/dialogic/Events/Text/settings_text.tscn
Normal file
183
Sprout/addons/dialogic/Events/Text/settings_text.tscn
Normal 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"]
|
||||
148
Sprout/addons/dialogic/Events/Text/subsystem_text.gd
Normal file
148
Sprout/addons/dialogic/Events/Text/subsystem_text.gd
Normal 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|$)')
|
||||
Loading…
Add table
Add a link
Reference in a new issue