First work on dialogic, resized guild, and started implementing portraits.

This commit is contained in:
2025-08-14 10:26:24 -04:00
parent 95a7db036b
commit 3aeb3d44e6
959 changed files with 47688 additions and 46 deletions

View File

@@ -0,0 +1,13 @@
@tool
extends Label
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# don't load the label settings when opening as a scene
# prevents HUGE diffs
if owner.get_parent() is SubViewport:
return
label_settings = LabelSettings.new()
label_settings.font = get_theme_font("doc_italic", "EditorFonts")
label_settings.font_size = get_theme_font_size('font_size', 'Label')
label_settings.font_color = get_theme_color("accent_color", "Editor")

View File

@@ -0,0 +1 @@
uid://ds8mckibk1a8t

View File

@@ -0,0 +1,356 @@
class_name DialogicCsvFile
extends RefCounted
## Handles translation of a [class DialogicTimeline] to a CSV file.
var lines: Array[PackedStringArray] = []
## Dictionary of lines from the original file.
## Key: String, Value: PackedStringArray
var old_lines: Dictionary = {}
## The amount of columns the CSV file has after loading it.
## Used to add trailing commas to new lines.
var column_count := 0
## Whether this CSV file was able to be loaded a defined
## file path.
var is_new_file: bool = false
## The underlying file used to read and write the CSV file.
var file: FileAccess
## File path used to load the CSV file.
var used_file_path: String
## The amount of events that were updated in the CSV file.
var updated_rows: int = 0
## The amount of events that were added to the CSV file.
var new_rows: int = 0
## Whether this CSV handler should add newlines as a separator between sections.
## A section may be a new character, new timeline, or new glossary item inside
## a per-project file.
var add_separator: bool = false
enum PropertyType {
String = 0,
Array = 1,
Other = 2,
}
## The translation property used for the glossary item translation.
const TRANSLATION_ID := DialogicGlossary.TRANSLATION_PROPERTY
## Attempts to load the CSV file from [param file_path].
## If the file does not exist, a single entry is added to the [member lines]
## array.
## The [param separator_enabled] enables adding newlines as a separator to
## per-project files. This is useful for readability.
func _init(file_path: String, original_locale: String, separator_enabled: bool) -> void:
used_file_path = file_path
add_separator = separator_enabled
# The first entry must be the locale row.
# [method collect_lines_from_timeline] will add the other locales, if any.
var locale_array_line := PackedStringArray(["keys", original_locale])
lines.append(locale_array_line)
if not ResourceLoader.exists(file_path):
is_new_file = true
# The "keys" and original locale are the only columns in a new file.
# For example: "keys, en"
column_count = 2
return
file = FileAccess.open(file_path, FileAccess.READ)
var locale_csv_row := file.get_csv_line()
column_count = locale_csv_row.size()
var locale_key := locale_csv_row[0]
old_lines[locale_key] = locale_csv_row
_read_file_into_lines()
## Private function to read the CSV file into the [member lines] array.
## Cannot be called on a new file.
func _read_file_into_lines() -> void:
while not file.eof_reached():
var line := file.get_csv_line()
var row_key := line[0]
old_lines[row_key] = line
## Collects names from the given [param characters] and adds them to the
## [member lines].
##
## If this is the character name CSV file, use this method to
## take previously collected characters from other [class DialogicCsvFile]s.
func collect_lines_from_characters(characters: Dictionary) -> void:
for character: DialogicCharacter in characters.values():
# Add row for display names.
var name_property := DialogicCharacter.TranslatedProperties.NAME
var display_name_key: String = character.get_property_translation_key(name_property)
var line_value: String = character.display_name
var array_line := PackedStringArray([display_name_key, line_value])
lines.append(array_line)
var nicknames: Array = character.nicknames
if not nicknames.is_empty():
var nick_name_property := DialogicCharacter.TranslatedProperties.NICKNAMES
var nickname_string: String = ",".join(nicknames)
var nickname_name_line_key: String = character.get_property_translation_key(nick_name_property)
var nick_array_line := PackedStringArray([nickname_name_line_key, nickname_string])
lines.append(nick_array_line)
# New character item, if needed, add a separator.
if add_separator:
_append_empty()
## Appends an empty line to the [member lines] array.
func _append_empty() -> void:
var empty_line := PackedStringArray(["", ""])
lines.append(empty_line)
## Returns the property type for the given [param key].
func _get_key_type(key: String) -> PropertyType:
if key.ends_with(DialogicGlossary.NAME_PROPERTY):
return PropertyType.String
if key.ends_with(DialogicGlossary.ALTERNATIVE_PROPERTY):
return PropertyType.Array
return PropertyType.Other
func _process_line_into_array(csv_values: PackedStringArray, property_type: PropertyType) -> Array[String]:
const KEY_VALUE_INDEX := 0
var values_as_array: Array[String] = []
for i in csv_values.size():
if i == KEY_VALUE_INDEX:
continue
var csv_value := csv_values[i]
if csv_value.is_empty():
continue
match property_type:
PropertyType.String:
values_as_array = [csv_value]
PropertyType.Array:
var split_values := csv_value.split(",")
for value in split_values:
values_as_array.append(value)
return values_as_array
func _add_keys_to_glossary(glossary: DialogicGlossary, names: Array) -> void:
var glossary_prefix_key := glossary._get_glossary_translation_id_prefix()
var glossary_translation_id_prefix := _get_glossary_translation_key_prefix(glossary)
for glossary_line: PackedStringArray in names:
if glossary_line.is_empty():
continue
var csv_key := glossary_line[0]
# CSV line separators will be empty.
if not csv_key.begins_with(glossary_prefix_key):
continue
var value_type := _get_key_type(csv_key)
# String and Array are the only valid types.
if (value_type == PropertyType.Other
or not csv_key.begins_with(glossary_translation_id_prefix)):
continue
var new_line_to_add := _process_line_into_array(glossary_line, value_type)
for name_to_add: String in new_line_to_add:
glossary._translation_keys[name_to_add.strip_edges()] = csv_key
## Reads all [member lines] and adds them to the given [param glossary]'s
## internal collection of words-to-translation-key mappings.
##
## Populate the CSV's lines with the method [method collect_lines_from_glossary]
## before.
func add_translation_keys_to_glossary(glossary: DialogicGlossary) -> void:
glossary._translation_keys.clear()
_add_keys_to_glossary(glossary, lines)
_add_keys_to_glossary(glossary, old_lines.values())
## Returns the translation key prefix for the given [param glossary_translation_id].
## The resulting format will look like this: Glossary/a2/
## You can use this to find entries in [member lines] that to a glossary.
func _get_glossary_translation_key_prefix(glossary: DialogicGlossary) -> String:
return (
DialogicGlossary.RESOURCE_NAME
.path_join(glossary._translation_id)
)
## Returns whether [param value_b] is greater than [param value_a].
##
## This method helps to sort glossary entry properties by their importance
## matching the order in the editor.
##
## TODO: Allow Dialogic users to define their own order.
func _sort_glossary_entry_property_keys(property_key_a: String, property_key_b: String) -> bool:
const GLOSSARY_CSV_LINE_ORDER := {
DialogicGlossary.NAME_PROPERTY: 0,
DialogicGlossary.ALTERNATIVE_PROPERTY: 1,
DialogicGlossary.TEXT_PROPERTY: 2,
DialogicGlossary.EXTRA_PROPERTY: 3,
}
const UNKNOWN_PROPERTY_ORDER := 100
var value_a: int = GLOSSARY_CSV_LINE_ORDER.get(property_key_a, UNKNOWN_PROPERTY_ORDER)
var value_b: int = GLOSSARY_CSV_LINE_ORDER.get(property_key_b, UNKNOWN_PROPERTY_ORDER)
return value_a < value_b
## Collects properties from glossary entries from the given [param glossary] and
## adds them to the [member lines].
func collect_lines_from_glossary(glossary: DialogicGlossary) -> void:
for glossary_value: Variant in glossary.entries.values():
if glossary_value is String:
continue
var glossary_entry: Dictionary = glossary_value
var glossary_entry_name: String = glossary_entry[DialogicGlossary.NAME_PROPERTY]
var _glossary_translation_id := glossary.get_set_glossary_translation_id()
var entry_translation_id := glossary.get_set_glossary_entry_translation_id(glossary_entry_name)
var entry_property_keys := glossary_entry.keys().duplicate()
entry_property_keys.sort_custom(_sort_glossary_entry_property_keys)
var entry_name_property: String = glossary_entry[DialogicGlossary.NAME_PROPERTY]
for entry_key: String in entry_property_keys:
# Ignore private keys.
if entry_key.begins_with(DialogicGlossary.PRIVATE_PROPERTY_PREFIX):
continue
var item_value: Variant = glossary_entry[entry_key]
var item_value_str := ""
if item_value is Array:
var item_array := item_value as Array
# We use a space after the comma to make it easier to read.
item_value_str = " ,".join(item_array)
elif not item_value is String or item_value.is_empty():
continue
else:
item_value_str = item_value
var glossary_csv_key := glossary._get_glossary_translation_key(entry_translation_id, entry_key)
if (entry_key == DialogicGlossary.NAME_PROPERTY
or entry_key == DialogicGlossary.ALTERNATIVE_PROPERTY):
glossary.entries[glossary_csv_key] = entry_name_property
var glossary_line := PackedStringArray([glossary_csv_key, item_value_str])
lines.append(glossary_line)
# New glossary item, if needed, add a separator.
if add_separator:
_append_empty()
## Collects translatable events from the given [param timeline] and adds
## them to the [member lines].
func collect_lines_from_timeline(timeline: DialogicTimeline) -> void:
for event: DialogicEvent in timeline.events:
if event.can_be_translated():
if event._translation_id.is_empty():
event.add_translation_id()
event.update_text_version()
var properties: Array = event._get_translatable_properties()
for property: String in properties:
var line_key: String = event.get_property_translation_key(property)
var line_value: String = event._get_property_original_translation(property)
var array_line := PackedStringArray([line_key, line_value])
lines.append(array_line)
# End of timeline, if needed, add a separator.
if add_separator:
_append_empty()
## Clears the CSV file on disk and writes the current [member lines] array to it.
## Uses the [member old_lines] dictionary to update existing translations.
## If a translation row misses a column, a trailing comma will be added to
## conform to the CSV file format.
##
## If the locale CSV line was collected only, a new file won't be created and
## already existing translations won't be updated.
func update_csv_file_on_disk() -> void:
# None or locale row only.
if lines.size() < 2:
print_rich("[color=yellow]No lines for the CSV file, skipping: " + used_file_path)
return
# Clear the current CSV file.
file = FileAccess.open(used_file_path, FileAccess.WRITE)
for line in lines:
var row_key := line[0]
# In case there might be translations for this line already,
# add them at the end again (orig locale text is replaced).
if row_key in old_lines:
var old_line: PackedStringArray = old_lines[row_key]
var updated_line: PackedStringArray = line + old_line.slice(2)
var line_columns: int = updated_line.size()
var line_columns_to_add := column_count - line_columns
# Add trailing commas to match the amount of columns.
for _i in range(line_columns_to_add):
updated_line.append("")
file.store_csv_line(updated_line)
updated_rows += 1
else:
var line_columns: int = line.size()
var line_columns_to_add := column_count - line_columns
# Add trailing commas to match the amount of columns.
for _i in range(line_columns_to_add):
line.append("")
file.store_csv_line(line)
new_rows += 1
file.close()

View File

@@ -0,0 +1 @@
uid://q7qb1gsp5nyj

View File

@@ -0,0 +1,169 @@
@tool
extends DialogicEditor
## Editor that contains all settings
var button_group := ButtonGroup.new()
var registered_sections: Array[DialogicSettingsPage] = []
func _get_title() -> String:
return "Settings"
func _get_icon() -> Texture:
return get_theme_icon("PluginScript", "EditorIcons")
func _register() -> void:
editors_manager.register_simple_editor(self)
self.alternative_text = "Customize dialogic and it's behaviour"
func _ready() -> void:
if get_parent() is SubViewport:
return
register_settings_section("res://addons/dialogic/Editor/Settings/settings_general.tscn")
register_settings_section("res://addons/dialogic/Editor/Settings/settings_translation.tscn")
register_settings_section("res://addons/dialogic/Editor/Settings/settings_modules.tscn")
for indexer in DialogicUtil.get_indexers():
for settings_page in indexer._get_settings_pages():
register_settings_section(settings_page)
add_registered_sections()
%SettingsTabs.get_child(0).button_pressed = true
%SettingsContent.get_child(0).show()
func register_settings_section(path:String) -> void:
var section: Control = load(path).instantiate()
registered_sections.append(section)
func add_registered_sections() -> void:
for i in %SettingsTabs.get_children():
i.queue_free()
for i in %FeatureTabs.get_children():
i.queue_free()
for i in %SettingsContent.get_children():
i.queue_free()
registered_sections.sort_custom(section_sort)
for section in registered_sections:
section.name = section._get_title()
var vbox := VBoxContainer.new()
vbox.set_meta('section', section)
vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
vbox.name = section.name
var hbox := HBoxContainer.new()
var title := Label.new()
title.text = section.name
title.theme_type_variation = 'DialogicSectionBig'
hbox.add_child(title)
vbox.add_child(hbox)
if !section.short_info.is_empty():
var tooltip_hint: Control = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate()
tooltip_hint.hint_text = section.short_info
hbox.add_child(tooltip_hint)
var scroll := ScrollContainer.new()
scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
var inner_vbox := VBoxContainer.new()
inner_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
inner_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
scroll.add_child(inner_vbox)
var panel := PanelContainer.new()
panel.theme_type_variation = "DialogicPanelA"
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if section.size_flags_vertical == Control.SIZE_EXPAND_FILL:
panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
inner_vbox.add_child(panel)
var info_section: Control = section._get_info_section()
if info_section != null:
inner_vbox.add_child(Control.new())
inner_vbox.get_child(-1).custom_minimum_size.y = 50
inner_vbox.add_child(title.duplicate())
inner_vbox.get_child(-1).text = "Information"
var info_panel := panel.duplicate()
info_panel.theme_type_variation = "DialogicPanelDarkA"
inner_vbox.add_child(info_panel)
info_section.get_parent().remove_child(info_section)
info_panel.add_child(info_section)
panel.add_child(section)
vbox.add_child(scroll)
var button := Button.new()
button.text = " "+section.name
button.tooltip_text = section.name
button.toggle_mode = true
button.button_group = button_group
button.expand_icon = true
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
button.flat = true
button.add_theme_color_override('font_pressed_color', get_theme_color("property_color_z", "Editor"))
button.add_theme_color_override('font_hover_color', get_theme_color('warning_color', 'Editor'))
button.add_theme_color_override('font_focus_color', get_theme_color('warning_color', 'Editor'))
button.add_theme_stylebox_override('focus', StyleBoxEmpty.new())
button.pressed.connect(open_tab.bind(vbox))
if section._is_feature_tab():
%FeatureTabs.add_child(button)
else:
%SettingsTabs.add_child(button)
vbox.hide()
# if section.has_method('_get_icon'):
# icon.texture = section._get_icon()
%SettingsContent.add_child(vbox)
func open_tab(tab_to_show:Control) -> void:
for tab in %SettingsContent.get_children():
tab.hide()
tab_to_show.show()
func section_sort(item1:DialogicSettingsPage, item2:DialogicSettingsPage) -> bool:
if !item1._is_feature_tab() and item2._is_feature_tab():
return true
if item1._get_priority() > item2._get_priority():
return true
return false
func _open(extra_information:Variant = null) -> void:
refresh()
if typeof(extra_information) == TYPE_STRING:
if %SettingsContent.has_node(extra_information):
open_tab(%SettingsContent.get_node(extra_information))
func _close() -> void:
for child in %SettingsContent.get_children():
if child.get_meta('section').has_method('_about_to_close'):
child.get_meta('section')._about_to_close()
func refresh() -> void:
for child in %SettingsContent.get_children():
if child.get_meta('section').has_method('_refresh'):
child.get_meta('section')._refresh()

View File

@@ -0,0 +1 @@
uid://ddt0uutspr055

View File

@@ -0,0 +1,59 @@
[gd_scene load_steps=2 format=3 uid="uid://dganirw26brfb"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_editor.gd" id="1"]
[node name="Settings" type="HSplitContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1")
[node name="TabList" type="ScrollContainer" parent="."]
layout_mode = 2
horizontal_scroll_mode = 0
[node name="Margin" type="MarginContainer" parent="TabList"]
layout_mode = 2
theme_override_constants/margin_top = 3
[node name="VBox" type="VBoxContainer" parent="TabList/Margin"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="TabList/Margin/VBox"]
layout_mode = 2
theme_type_variation = &"DialogicSection"
text = "Settings"
[node name="SettingsTabs" type="VBoxContainer" parent="TabList/Margin/VBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 0
[node name="Control" type="Control" parent="TabList/Margin/VBox"]
custom_minimum_size = Vector2(0, 30)
layout_mode = 2
[node name="Title2" type="Label" parent="TabList/Margin/VBox"]
layout_mode = 2
theme_type_variation = &"DialogicSection"
text = "Features"
[node name="FeatureTabs" type="VBoxContainer" parent="TabList/Margin/VBox"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 0
[node name="SettingsContent" type="VBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3

View File

@@ -0,0 +1,247 @@
@tool
extends DialogicSettingsPage
## Settings tab that holds genreal dialogic settings.
func _get_title() -> String:
return "General"
func _get_priority() -> int:
return 99
func _ready() -> void:
var s := DCSS.inline({
'padding': 5,
'background': Color(0.545098, 0.545098, 0.545098, 0.211765)
})
%ExtensionsFolderPicker.resource_icon = get_theme_icon("Folder", "EditorIcons")
# Signals
%ExtensionsFolderPicker.value_changed.connect(_on_ExtensionsFolder_value_changed)
%PhysicsTimerButton.toggled.connect(_on_physics_timer_button_toggled)
# Colors
%ResetColorsButton.icon = get_theme_icon("Reload", "EditorIcons")
%ResetColorsButton.button_up.connect(_on_reset_colors_button)
# Extension creator
%ExtensionCreator.hide()
func _refresh() -> void:
%PhysicsTimerButton.button_pressed = DialogicUtil.is_physics_timer()
%LayoutNodeEndBehaviour.select(ProjectSettings.get_setting('dialogic/layout/end_behaviour', 0))
%ExtensionsFolderPicker.set_value(ProjectSettings.get_setting('dialogic/extensions_folder', 'res://addons/dialogic_additions'))
update_color_palette()
%SectionList.clear()
%SectionList.create_item()
var cached_events := DialogicResourceUtil.get_event_cache()
var sections := []
var section_order: Array = DialogicUtil.get_editor_setting('event_section_order', ['Main', 'Logic', 'Flow', 'Audio', 'Visuals','Other', 'Helper'])
for ev in cached_events:
if !ev.event_category in sections:
sections.append(ev.event_category)
var item: TreeItem = %SectionList.create_item(null)
item.set_text(0, ev.event_category)
item.add_button(0, get_theme_icon("ArrowUp", "EditorIcons"))
item.add_button(0, get_theme_icon("ArrowDown", "EditorIcons"))
if ev.event_category in section_order:
item.move_before(item.get_parent().get_child(min(section_order.find(ev.event_category),item.get_parent().get_child_count()-1)))
%SectionList.get_root().get_child(0).set_button_disabled(0, 0, true)
%SectionList.get_root().get_child(-1).set_button_disabled(0, 1, true)
func _on_section_list_button_clicked(item:TreeItem, column, id, mouse_button_index):
if id == 0:
item.move_before(item.get_parent().get_child(item.get_index()-1))
else:
item.move_after(item.get_parent().get_child(item.get_index()+1))
for child in %SectionList.get_root().get_children():
child.set_button_disabled(0, 0, false)
child.set_button_disabled(0, 1, false)
%SectionList.get_root().get_child(0).set_button_disabled(0, 0, true)
%SectionList.get_root().get_child(-1).set_button_disabled(0, 1, true)
var sections := []
for child in %SectionList.get_root().get_children():
sections.append(child.get_text(0))
DialogicUtil.set_editor_setting('event_section_order', sections)
force_event_button_list_reload()
func force_event_button_list_reload() -> void:
find_parent('EditorsManager').editors['Timeline'].node.get_node('%VisualEditor').load_event_buttons()
func update_color_palette() -> void:
# Color Palette
for child in %Colors.get_children():
child.queue_free()
for color in DialogicUtil.get_color_palette():
var button := ColorPickerButton.new()
button.custom_minimum_size = Vector2(50 ,50) * DialogicUtil.get_editor_scale()
%Colors.add_child(button)
button.color = DialogicUtil.get_color(color)
button.color_changed.connect(_on_color_change)
func _on_color_change(color:Color) -> void:
var new_palette := {}
for i in %Colors.get_children():
new_palette['Color'+str(i.get_index()+1)] = i.color
DialogicUtil.set_editor_setting('color_palette', new_palette)
func _on_reset_colors_button() -> void:
DialogicUtil.set_editor_setting('color_palette', null)
update_color_palette()
func _on_physics_timer_button_toggled(is_toggled: bool) -> void:
ProjectSettings.set_setting('dialogic/timer/process_in_physics', is_toggled)
ProjectSettings.save()
func _on_ExtensionsFolder_value_changed(property:String, value:String) -> void:
if value == null or value.is_empty():
value = 'res://addons/dialogic_additions'
ProjectSettings.set_setting('dialogic/extensions_folder', value)
ProjectSettings.save()
func _on_layout_node_end_behaviour_item_selected(index:int) -> void:
ProjectSettings.set_setting('dialogic/layout/end_behaviour', index)
ProjectSettings.save()
################################################################################
## EXTENSION CREATOR
################################################################################
func _on_create_extension_button_pressed() -> void:
%CreateExtensionButton.hide()
%ExtensionCreator.show()
%NameEdit.text = ""
%NameEdit.grab_focus()
func _on_submit_extension_button_pressed() -> void:
if %NameEdit.text.is_empty():
return
var extensions_folder: String = ProjectSettings.get_setting('dialogic/extensions_folder', 'res://addons/dialogic_additions')
extensions_folder = extensions_folder.path_join(%NameEdit.text.to_pascal_case())
DirAccess.make_dir_recursive_absolute(extensions_folder)
var mode: int = %ExtensionMode.selected
var file: FileAccess
var indexer_content := "@tool\nextends DialogicIndexer\n\n"
if mode != 2: # don't add event in Subsystem Only mode
indexer_content += """func _get_events() -> Array:
return [this_folder.path_join('event_"""+%NameEdit.text.to_snake_case()+""".gd')]\n\n"""
file = FileAccess.open(extensions_folder.path_join('event_'+%NameEdit.text.to_snake_case()+'.gd'), FileAccess.WRITE)
file.store_string(
#region EXTENDED EVENT SCRIPT
"""@tool
extends DialogicEvent
class_name Dialogic"""+%NameEdit.text.to_pascal_case()+"""Event
# Define properties of the event here
func _execute() -> void:
# This will execute when the event is reached
finish() # called to continue with the next event
#region INITIALIZE
################################################################################
# Set fixed settings of this event
func _init() -> void:
event_name = \""""+%NameEdit.text.capitalize()+"""\"
event_category = "Other"
\n
#endregion
#region SAVING/LOADING
################################################################################
func get_shortcode() -> String:
return \""""+%NameEdit.text.to_snake_case()+"""\"
func get_shortcode_parameters() -> Dictionary:
return {
#param_name : property_info
#"my_parameter" : {"property": "property", "default": "Default"},
}
# You can alternatively overwrite these 3 functions: to_text(), from_text(), is_valid_event()
#endregion
#region EDITOR REPRESENTATION
################################################################################
func build_event_editor() -> void:
pass
#endregion
""")
#endregion
if mode != 0: # don't add subsystem in event only mode
indexer_content += """func _get_subsystems() -> Array:
return [{'name':'"""+%NameEdit.text.to_pascal_case()+"""', 'script':this_folder.path_join('subsystem_"""+%NameEdit.text.to_snake_case()+""".gd')}]"""
file = FileAccess.open(extensions_folder.path_join('subsystem_'+%NameEdit.text.to_snake_case()+'.gd'), FileAccess.WRITE)
file.store_string(
# region EXTENDED SUBSYSTEM SCRIPT
"""extends DialogicSubsystem
## Describe the subsystems purpose here.
#region STATE
####################################################################################################
func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR) -> void:
pass
func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void:
pass
#endregion
#region MAIN METHODS
####################################################################################################
# Add some useful methods here.
#endregion
""")
file = FileAccess.open(extensions_folder.path_join('index.gd'), FileAccess.WRITE)
file.store_string(indexer_content)
%ExtensionCreator.hide()
%CreateExtensionButton.show()
find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources()
force_event_button_list_reload()
func _on_reload_pressed() -> void:
DialogicUtil._update_autoload_subsystem_access()

View File

@@ -0,0 +1 @@
uid://dd68ldgly88gk

View File

@@ -0,0 +1,283 @@
[gd_scene load_steps=6 format=3 uid="uid://b873ho41sklv8"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_general.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_kqhx5"]
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="3_i7rug"]
[sub_resource type="Image" id="Image_e1gle"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_4wgbv"]
image = SubResource("Image_e1gle")
[node name="General" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2")
[node name="PaletteTitle" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="SectionPaletteTitle" type="Label" parent="PaletteTitle"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Color Palette"
[node name="HintTooltip" parent="PaletteTitle" instance=ExtResource("2_kqhx5")]
layout_mode = 2
tooltip_text = "These colors are used for the events."
texture = SubResource("ImageTexture_4wgbv")
hint_text = "These colors are used for the events."
[node name="ResetColorsButton" type="Button" parent="PaletteTitle"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 0
tooltip_text = "Reset Colors to default"
icon = SubResource("ImageTexture_4wgbv")
flat = true
[node name="ScrollContainer" type="ScrollContainer" parent="."]
layout_mode = 2
horizontal_scroll_mode = 3
vertical_scroll_mode = 0
[node name="Colors" type="HBoxContainer" parent="ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="HSeparator" type="HSeparator" parent="."]
layout_mode = 2
[node name="HBoxContainer2" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="SectionBehaviourTitle" type="Label" parent="HBoxContainer2"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Layout Node Behaviour"
[node name="HintTooltip" parent="HBoxContainer2" instance=ExtResource("2_kqhx5")]
layout_mode = 2
tooltip_text = "The layout scene configured in the Layout editor is automatically
instanced when calling Dialogic.start(). Depending on your game,
you might want it to be deleted after the dialogue, be hidden
(as reinstancing often is wasting resources) or kept visible. "
texture = SubResource("ImageTexture_4wgbv")
hint_text = "The layout scene configured in the Layout editor is automatically
instanced when calling Dialogic.start(). Depending on your game,
you might want it to be deleted after the dialogue, be hidden
(as reinstancing often is wasting resources) or kept visible. "
[node name="HBoxContainer3" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="Label" type="Label" parent="HBoxContainer3"]
layout_mode = 2
text = "On timeline end"
[node name="LayoutNodeEndBehaviour" type="OptionButton" parent="HBoxContainer3"]
unique_name_in_owner = true
layout_mode = 2
item_count = 3
selected = 0
fit_to_longest_item = false
popup/item_0/text = "Delete Layout Node"
popup/item_0/id = 0
popup/item_1/text = "Hide Layout Node"
popup/item_1/id = 1
popup/item_2/text = "Keep Layout Node"
popup/item_2/id = 2
[node name="HSeparator4" type="HSeparator" parent="."]
layout_mode = 2
[node name="HBoxContainer6" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="HBoxContainer4" type="VBoxContainer" parent="HBoxContainer6"]
layout_mode = 2
size_flags_horizontal = 3
[node name="HBoxContainer5" type="HBoxContainer" parent="HBoxContainer6/HBoxContainer4"]
layout_mode = 2
[node name="SectionExtensionsTitle" type="Label" parent="HBoxContainer6/HBoxContainer4/HBoxContainer5"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Extensions"
[node name="HintTooltip" parent="HBoxContainer6/HBoxContainer4/HBoxContainer5" instance=ExtResource("2_kqhx5")]
layout_mode = 2
tooltip_text = "Configure where dialogic looks for custom modules.
You will have to restart the project to see the change take action."
texture = SubResource("ImageTexture_4wgbv")
hint_text = "Configure where dialogic looks for custom modules.
You will have to restart the project to see the change take action."
[node name="Reload" type="Button" parent="HBoxContainer6/HBoxContainer4/HBoxContainer5"]
layout_mode = 2
text = "Reload"
flat = true
[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer6/HBoxContainer4"]
layout_mode = 2
[node name="Label" type="Label" parent="HBoxContainer6/HBoxContainer4/HBoxContainer"]
layout_mode = 2
text = "Extensions folder"
[node name="ExtensionsFolderPicker" parent="HBoxContainer6/HBoxContainer4/HBoxContainer" instance=ExtResource("3_i7rug")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder = "res://addons/dialogic_additions/Events"
file_mode = 2
resource_icon = SubResource("ImageTexture_4wgbv")
[node name="VSeparator" type="VSeparator" parent="HBoxContainer6"]
layout_mode = 2
[node name="ExtensionsPanel" type="PanelContainer" parent="HBoxContainer6"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"DialogicPanelA"
[node name="VBox" type="VBoxContainer" parent="HBoxContainer6/ExtensionsPanel"]
layout_mode = 2
size_flags_horizontal = 3
[node name="HBoxContainer6" type="HBoxContainer" parent="HBoxContainer6/ExtensionsPanel/VBox"]
layout_mode = 2
[node name="Label" type="Label" parent="HBoxContainer6/ExtensionsPanel/VBox/HBoxContainer6"]
layout_mode = 2
theme_type_variation = &"DialogicSubTitle"
text = "Extension Creator "
[node name="HintTooltip" parent="HBoxContainer6/ExtensionsPanel/VBox/HBoxContainer6" instance=ExtResource("2_kqhx5")]
layout_mode = 2
tooltip_text = "Use the Exension Creator to quickly setup custom modules!"
texture = SubResource("ImageTexture_4wgbv")
hint_text = "Use the Exension Creator to quickly setup custom modules!"
[node name="CreateExtensionButton" type="Button" parent="HBoxContainer6/ExtensionsPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
text = "Create New Extension"
[node name="ExtensionCreator" type="VBoxContainer" parent="HBoxContainer6/ExtensionsPanel/VBox"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ExtensionCreatorOptions" type="GridContainer" parent="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator"]
layout_mode = 2
columns = 2
[node name="NameLabel" type="Label" parent="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator/ExtensionCreatorOptions"]
layout_mode = 2
text = "Name:"
[node name="NameEdit" type="LineEdit" parent="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator/ExtensionCreatorOptions"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "e.g. \"Print\", \"Item\", \"Door\", \"Quest\""
[node name="ModeLabel" type="Label" parent="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator/ExtensionCreatorOptions"]
layout_mode = 2
text = "Setup mode:"
[node name="ExtensionMode" type="OptionButton" parent="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator/ExtensionCreatorOptions"]
unique_name_in_owner = true
layout_mode = 2
item_count = 4
selected = 0
popup/item_0/text = "Event only"
popup/item_0/id = 0
popup/item_1/text = "Event+Subsystem"
popup/item_1/id = 1
popup/item_2/text = "Subsystem only"
popup/item_2/id = 2
popup/item_3/text = "Complex"
popup/item_3/id = 3
[node name="SubmitExtensionButton" type="Button" parent="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator"]
unique_name_in_owner = true
layout_mode = 2
text = "Create"
[node name="HSeparator2" type="HSeparator" parent="."]
layout_mode = 2
[node name="HBoxContainer7" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="TimerTitle" type="Label" parent="HBoxContainer7"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Timer processing"
[node name="HintTooltip" parent="HBoxContainer7" instance=ExtResource("2_kqhx5")]
layout_mode = 2
tooltip_text = "Change whether dialogics timers process in physics_process (frame-rate independent) or process."
texture = SubResource("ImageTexture_4wgbv")
hint_text = "Change whether dialogics timers process in physics_process (frame-rate independent) or process."
[node name="HBoxContainer4" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="Label" type="Label" parent="HBoxContainer4"]
layout_mode = 2
text = "Process timers in physics_process"
[node name="PhysicsTimerButton" type="CheckBox" parent="HBoxContainer4"]
unique_name_in_owner = true
layout_mode = 2
[node name="HSeparator5" type="HSeparator" parent="."]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="SectionSections" type="Label" parent="HBoxContainer"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Section Order"
[node name="HintTooltip" parent="HBoxContainer" instance=ExtResource("2_kqhx5")]
layout_mode = 2
tooltip_text = "You can change the order of the event sections here. "
texture = SubResource("ImageTexture_4wgbv")
hint_text = "You can change the order of the event sections here. "
[node name="SectionList" type="Tree" parent="."]
unique_name_in_owner = true
custom_minimum_size = Vector2(150, 150)
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/button_margin = 0
allow_reselect = true
allow_rmb_select = true
hide_folding = true
hide_root = true
drop_mode_flags = 1
[connection signal="item_selected" from="HBoxContainer3/LayoutNodeEndBehaviour" to="." method="_on_layout_node_end_behaviour_item_selected"]
[connection signal="pressed" from="HBoxContainer6/HBoxContainer4/HBoxContainer5/Reload" to="." method="_on_reload_pressed"]
[connection signal="pressed" from="HBoxContainer6/ExtensionsPanel/VBox/CreateExtensionButton" to="." method="_on_create_extension_button_pressed"]
[connection signal="pressed" from="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator/SubmitExtensionButton" to="." method="_on_submit_extension_button_pressed"]
[connection signal="button_clicked" from="SectionList" to="." method="_on_section_list_button_clicked"]

View File

@@ -0,0 +1,443 @@
@tool
extends DialogicSettingsPage
func _get_title() -> String:
return "Modules"
func _get_priority() -> int:
return 0
func _is_feature_tab() -> bool:
return true
func _ready() -> void:
if get_parent() is SubViewport:
return
%Refresh.icon = get_theme_icon("Loop", "EditorIcons")
%Search.right_icon = get_theme_icon("Search", "EditorIcons")
%Filter_Events.icon = get_theme_icon("Favorites", "EditorIcons")
%Filter_Subsystems.icon = get_theme_icon("Callable", "EditorIcons")
%Filter_Styles.icon = get_theme_icon("PopupMenu", "EditorIcons")
%Filter_EffectsAndModifiers.icon = get_theme_icon("RichTextEffect", "EditorIcons")
%Filter_Editors.icon = get_theme_icon("ConfirmationDialog", "EditorIcons")
%Filter_Settings.icon = get_theme_icon("PluginScript", "EditorIcons")
%Collapse.icon = get_theme_icon("CollapseTree", "EditorIcons")
%EventDefaultsPanel.add_theme_stylebox_override('panel', get_theme_stylebox("Background", "EditorStyles"))
%ExternalLink.icon = get_theme_icon("Help", "EditorIcons")
func _refresh() -> void:
%EventDefaultsPanel.hide()
load_modules_tree()
func _on_refresh_pressed() -> void:
DialogicUtil.get_indexers(true, true)
DialogicResourceUtil.update_event_cache()
load_modules_tree()
func filters_updated(fake_arg:Variant) -> void:
load_modules_tree()
func _on_collapse_toggled(button_pressed:bool) -> void:
for item in %Tree.get_root().get_children():
item.collapsed = button_pressed
if button_pressed:
%Collapse.icon = get_theme_icon("ExpandTree", "EditorIcons")
%Collapse.tooltip_text = "Expand All"
else:
%Collapse.icon = get_theme_icon("CollapseTree", "EditorIcons")
%Collapse.tooltip_text = "Collapse All"
func _on_search_text_changed(new_text:String) -> void:
for filter in [%Filter_Events, %Filter_Subsystems, %Filter_Editors, %Filter_EffectsAndModifiers, %Filter_Settings, %Filter_Styles]:
filter.text = ""
filter.set_meta("counter", 0)
var hidden_events: Array = DialogicUtil.get_editor_setting('hidden_event_buttons', [])
for child in %Tree.get_root().get_children():
if new_text.to_lower() in child.get_text(0).to_lower() or new_text.is_empty():
for sub_child in child.get_children():
sub_child.visible = sub_child.get_meta('filter_button').button_pressed
sub_child.get_meta('filter_button').set_meta('counter', sub_child.get_meta('filter_button').get_meta('counter')+1)
sub_child.get_meta('filter_button').text = str(sub_child.get_meta('filter_button').get_meta('counter'))
child.visible = true
else:
for sub_child in child.get_children():
sub_child.visible = sub_child.get_meta('filter_button').button_pressed and new_text.to_lower() in sub_child.get_text(0).to_lower()
if new_text.to_lower() in sub_child.get_text(0).to_lower():
sub_child.get_meta('filter_button').set_meta('counter', sub_child.get_meta('filter_button').get_meta('counter')+1)
sub_child.get_meta('filter_button').text = str(sub_child.get_meta('filter_button').get_meta('counter'))
for i in range(child.get_button_count(0)):
child.erase_button(0, child.get_button_count(0)-1)
var any_visible := false
var counter := 0
for sub_child in child.get_children():
if sub_child.visible:
child.add_button(0, sub_child.get_icon(0), counter, false, sub_child.get_text(0))
if sub_child.get_metadata(0) and sub_child.get_metadata(0)['type'] == 'Event' and sub_child.get_metadata(0)['hidden']:
var color: Color = sub_child.get_icon_modulate(0)
color.a = 0.5
child.set_button_color(0, counter, color)
else:
child.set_button_color(0, counter, sub_child.get_icon_modulate(0))
counter += 1
any_visible = true
child.visible = any_visible
func load_modules_tree() -> void:
%Tree.clear()
var root: TreeItem = %Tree.create_item()
var cached_events := DialogicResourceUtil.get_event_cache()
var hidden_events: Array = DialogicUtil.get_editor_setting('hidden_event_buttons', [])
var indexers := DialogicUtil.get_indexers()
for i in indexers:
var module_item: TreeItem = %Tree.create_item(root)
module_item.set_text(0, i.get_script().resource_path.trim_suffix('/index.gd').get_file())
module_item.set_metadata(0, {'type':'Module'})
# Events
for ev in i._get_events():
if not ResourceLoader.exists(ev):
continue
var event_item: TreeItem = %Tree.create_item(module_item)
event_item.set_icon(0, get_theme_icon("Favorites", "EditorIcons"))
for cached_event in cached_events:
if cached_event.get_script().resource_path == ev:
event_item.set_text(0, cached_event.event_name + " Event")
event_item.set_icon_modulate(0, cached_event.event_color)
var hidden: bool = cached_event.event_name in hidden_events
event_item.set_metadata(0, {'type':'Event', 'event':cached_event, 'hidden':hidden})
event_item.add_button(0, get_theme_icon("GuiVisibilityVisible", "EditorIcons"), 0, false, "Toggle Event Button Visibility")
if hidden:
event_item.set_button(0, 0, get_theme_icon("GuiVisibilityHidden", "EditorIcons"))
event_item.set_meta('filter_button', %Filter_Events)
event_item.visible = %Filter_Events.button_pressed
# Subsystems
for subsys in i._get_subsystems():
var subsys_item: TreeItem = %Tree.create_item(module_item)
subsys_item.set_icon(0, get_theme_icon("Callable", "EditorIcons"))
subsys_item.set_text(0, subsys.name + " Subsystem")
subsys_item.set_icon_modulate(0, get_theme_color("readonly_color", "Editor"))
subsys_item.set_metadata(0, {'type':'Subsystem', 'info':subsys})
subsys_item.set_meta('filter_button', %Filter_Subsystems)
subsys_item.visible = %Filter_Subsystems.button_pressed
# Style scenes
for style in i._get_layout_parts():
var style_item: TreeItem = %Tree.create_item(module_item)
style_item.set_icon(0, get_theme_icon("PopupMenu", "EditorIcons"))
style_item.set_text(0, style.name)
style_item.set_icon_modulate(0, get_theme_color("property_color_x", "Editor"))
style_item.set_metadata(0, {'type':'Style', 'info':style})
style_item.set_meta('filter_button', %Filter_Styles)
style_item.visible = %Filter_Styles.button_pressed
# Text Effects
for effect in i._get_text_effects():
var effect_item: TreeItem = %Tree.create_item(module_item)
effect_item.set_icon(0, get_theme_icon("RichTextEffect", "EditorIcons"))
effect_item.set_text(0, "Text effect ["+effect.command+"]")
effect_item.set_icon_modulate(0, get_theme_color("property_color_z", "Editor"))
effect_item.set_metadata(0, {'type':'Effect', 'info':effect})
effect_item.set_meta('filter_button', %Filter_EffectsAndModifiers)
effect_item.visible = %Filter_EffectsAndModifiers.button_pressed
# Text Modifiers
for mod in i._get_text_modifiers():
var mod_item: TreeItem = %Tree.create_item(module_item)
mod_item.set_icon(0, get_theme_icon("RichTextEffect", "EditorIcons"))
mod_item.set_text(0, mod.method.capitalize())
mod_item.set_icon_modulate(0, get_theme_color("property_color_z", "Editor"))
mod_item.set_metadata(0, {'type':'Modifier', 'info':mod})
mod_item.set_meta('filter_button', %Filter_EffectsAndModifiers)
mod_item.visible = %Filter_EffectsAndModifiers.button_pressed
# Settings
for settings in i._get_settings_pages():
var settings_item: TreeItem = %Tree.create_item(module_item)
settings_item.set_icon(0, get_theme_icon("PluginScript", "EditorIcons"))
settings_item.set_text(0, module_item.get_text(0) + " Settings")
settings_item.set_icon_modulate(0, get_theme_color("readonly_color", "Editor"))
settings_item.set_metadata(0, {'type':'Settings', 'info':settings})
settings_item.set_meta('filter_button', %Filter_Settings)
settings_item.visible = %Filter_Settings.button_pressed
# Editors
for editor in i._get_editors():
var editor_item: TreeItem = %Tree.create_item(module_item)
editor_item.set_icon(0, get_theme_icon("ConfirmationDialog", "EditorIcons"))
editor_item.set_text(0, editor.get_file().trim_suffix('.tscn').capitalize())
editor_item.set_icon_modulate(0, get_theme_color("readonly_color", "Editor"))
editor_item.set_metadata(0, {'type':'Editor', 'info':editor})
editor_item.set_meta('filter_button', %Filter_Editors)
editor_item.visible = %Filter_Editors.button_pressed
module_item.collapsed = %Collapse.button_pressed
_on_search_text_changed(%Search.text)
if %Tree.get_root().get_child_count(): %Tree.set_selected(%Tree.get_root().get_child(0), 0)
func _on_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_index:int) -> void:
match item.get_metadata(0)['type']:
'Module':
item.collapsed = false
%Tree.set_selected(item.get_child(id), 0)
'Event':
# Visibility item clicked
if id == 0:
var meta: Dictionary= item.get_metadata(0)
if meta['hidden']:
item.set_button(0, 0, get_theme_icon("GuiVisibilityVisible", "EditorIcons"))
item.get_parent().set_button_color(0, item.get_index(), item.get_icon_modulate(0))
if item == %Tree.get_selected():
%VisibilityToggle.button_pressed = true
else:
item.set_button(0, 0, get_theme_icon("GuiVisibilityHidden", "EditorIcons"))
var color: Color = item.get_icon_modulate(0)
color.a = 0.5
item.get_parent().set_button_color(0, item.get_index(), color)
if item == %Tree.get_selected():
%VisibilityToggle.button_pressed = false
meta['hidden'] = !meta['hidden']
item.set_metadata(0, meta)
change_event_visibility(meta['event'], !meta['hidden'])
func _on_tree_item_selected() -> void:
var selected_item: TreeItem = %Tree.get_selected()
var metadata: Variant = selected_item.get_metadata(0)
%Title.text = selected_item.get_text(0)
%EventDefaultsPanel.hide()
%Icon.texture = null
%ExternalLink.hide()
%VisibilityToggle.hide()
if metadata is Dictionary:
match metadata.type:
'Event':
%GeneralInfo.text = "Events can be used in timelines and do all kinds of things. They often interact with subsystems and dialogic nodes."
load_event_settings(metadata.event)
if %EventDefaults.get_child_count():
%EventDefaultsPanel.show()
if metadata.event.help_page_path:
%ExternalLink.show()
%ExternalLink.set_meta('url', metadata.event.help_page_path)
%Icon.texture = metadata.event._get_icon()
if !metadata.event.disable_editor_button:
%VisibilityToggle.show()
%VisibilityToggle.button_pressed = !metadata.event.event_name in DialogicUtil.get_editor_setting('hidden_event_buttons', [])
if %VisibilityToggle.button_pressed:
%VisibilityToggle.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
else:
%VisibilityToggle.icon = get_theme_icon("GuiVisibilityHidden", "EditorIcons")
# -------------------------------------------------
'Subsystem':
%GeneralInfo.text = "Subsystems hold specialized functionality. They mostly manage communication between events and dialogic nodes. Often they provide handy methods that can be accessed by the user like this: Dialogic.Subsystem.a_method()."
# -------------------------------------------------
'Effect':
%GeneralInfo.text = "Text effects can be used in text events. They will be executed once reached and can take a single argument."
# -------------------------------------------------
'Modifier':
%GeneralInfo.text = "Modifiers can modify text from text events before it is shown."
# -------------------------------------------------
'Style':
%GeneralInfo.text = "Style presets can be activated and modified in the Styles editor. They provide the design of the dialog interface in your game."
# -------------------------------------------------
'Editor':
%GeneralInfo.text = "Editors provide a user interface for editing dialogic data."
# -------------------------------------------------
'Settings':
%GeneralInfo.text = "Settings pages provide settings that are usually used by subsystems, events and dialogic nodes."
# -------------------------------------------------
'_':
%GeneralInfo.text = ""
func _on_external_link_pressed() -> void:
if %ExternalLink.has_meta('url'):
OS.shell_open(%ExternalLink.get_meta('url'))
func change_event_visibility(event:DialogicEvent, visibility:bool) -> void:
if event:
var list: Array= DialogicUtil.get_editor_setting('hidden_event_buttons', [])
if visibility:
list.erase(event.event_name)
else:
list.append(event.event_name)
DialogicUtil.set_editor_setting('hidden_event_buttons', list)
force_event_button_list_update()
func _on_visibility_toggle_toggled(button_pressed:bool) -> void:
change_event_visibility(%Tree.get_selected().get_metadata(0).event, button_pressed)
if button_pressed:
%VisibilityToggle.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
%Tree.get_selected().set_button(0, 0, get_theme_icon("GuiVisibilityVisible", "EditorIcons"))
%Tree.get_selected().get_parent().set_button_color(0, %Tree.get_selected().get_index(), %Tree.get_selected().get_icon_modulate(0))
else:
%VisibilityToggle.icon = get_theme_icon("GuiVisibilityHidden", "EditorIcons")
%Tree.get_selected().set_button(0, 0, get_theme_icon("GuiVisibilityHidden", "EditorIcons"))
var color: Color = %Tree.get_selected().get_icon_modulate(0)
color.a = 0.5
%Tree.get_selected().get_parent().set_button_color(0, %Tree.get_selected().get_index(), color)
func force_event_button_list_update() -> void:
find_parent('EditorsManager').editors['Timeline'].node.get_node('%VisualEditor').load_event_buttons()
################################################################################
## EVENT DEFAULT SETTINGS
################################################################################
func load_event_settings(event:DialogicEvent) -> void:
for child in %EventDefaults.get_children():
child.queue_free()
var event_default_overrides: Dictionary = ProjectSettings.get_setting('dialogic/event_default_overrides', {})
var params := event.get_shortcode_parameters()
for prop in params:
var current_value: Variant = params[prop].default
if event_default_overrides.get(event.event_name, {}).has(params[prop].property):
current_value = event_default_overrides.get(event.event_name, {}).get(params[prop].property)
# Label
var label := Label.new()
label.text = prop.capitalize()
%EventDefaults.add_child(label)
var reset := Button.new()
reset.icon = get_theme_icon("Clear", "EditorIcons")
reset.flat = true
%EventDefaults.add_child(reset)
# Editing field
var editor_node: Node = null
match typeof(event.get(params[prop].property)):
TYPE_STRING:
editor_node = LineEdit.new()
editor_node.custom_minimum_size.x = 150
editor_node.text = str(current_value)
editor_node.text_changed.connect(_on_event_default_string_submitted.bind(params[prop].property))
TYPE_INT, TYPE_FLOAT:
if params[prop].has('suggestions'):
editor_node = OptionButton.new()
for i in params[prop].suggestions.call():
editor_node.add_item(i, int(params[prop].suggestions.call()[i].value))
editor_node.select(int(current_value))
editor_node.item_selected.connect(_on_event_default_option_selected.bind(editor_node, params[prop].property))
else:
editor_node = SpinBox.new()
editor_node.allow_greater = true
editor_node.allow_lesser = true
if typeof(event.get(params[prop].property)) == TYPE_INT:
editor_node.step = 1
else:
editor_node.step = 0.001
editor_node.value = float(current_value)
editor_node.value_changed.connect(_on_event_default_number_changed.bind(params[prop].property))
TYPE_VECTOR2:
editor_node = load("res://addons/dialogic/Editor/Events/Fields/field_vector2.tscn").instantiate()
editor_node.set_value(current_value)
editor_node.property_name = params[prop].property
editor_node.value_changed.connect(_on_event_default_value_changed)
TYPE_BOOL:
editor_node = CheckBox.new()
editor_node.button_pressed = bool(current_value)
editor_node.toggled.connect(_on_event_default_bool_toggled.bind(params[prop].property))
TYPE_ARRAY:
editor_node = load("res://addons/dialogic/Editor/Events/Fields/field_array.tscn").instantiate()
editor_node.set_value(current_value)
editor_node.property_name = params[prop].property
editor_node.value_changed.connect(_on_event_default_value_changed)
TYPE_DICTIONARY:
editor_node = load("res://addons/dialogic/Editor/Events/Fields/field_dictionary.tscn").instantiate()
editor_node.set_value(current_value)
editor_node.property_name = params[prop].property
editor_node.value_changed.connect(_on_event_default_value_changed)
%EventDefaults.add_child(editor_node)
reset.pressed.connect(reset_event_default_override.bind(prop, editor_node, params[prop].default))
func set_event_default_override(prop:String, value:Variant) -> void:
var event_default_overrides: Dictionary = ProjectSettings.get_setting('dialogic/event_default_overrides', {})
var event: DialogicEvent = %Tree.get_selected().get_metadata(0).event
if not event_default_overrides.has(event.event_name):
event_default_overrides[event.event_name] = {}
event_default_overrides[event.event_name][prop] = value
ProjectSettings.set_setting('dialogic/event_default_overrides', event_default_overrides)
func reset_event_default_override(prop:String, node:Node, default:Variant) -> void:
var event_default_overrides: Dictionary = ProjectSettings.get_setting('dialogic/event_default_overrides', {})
var event: DialogicEvent = %Tree.get_selected().get_metadata(0).event
if not event_default_overrides.has(event.event_name):
return
event_default_overrides[event.event_name].erase(prop)
ProjectSettings.set_setting('dialogic/event_default_overrides', event_default_overrides)
if node is CheckBox:
node.button_pressed = default
elif node is LineEdit:
node.text = default
elif node.has_method('set_value'):
node.set_value(default)
elif node is ColorPickerButton:
node.color = default
elif node is OptionButton:
node.select(default)
elif node is SpinBox:
node.value = default
func _on_event_default_string_submitted(text:String, prop:String) -> void:
set_event_default_override(prop, text)
func _on_event_default_option_selected(index:int, option_button:OptionButton, prop:String) -> void:
set_event_default_override(prop, option_button.get_item_id(index))
func _on_event_default_number_changed(value:float, prop:String) -> void:
set_event_default_override(prop, value)
func _on_event_default_value_changed(prop:String, value:Variant) -> void:
set_event_default_override(prop, value)
func _on_event_default_bool_toggled(value:bool, prop:String) -> void:
set_event_default_override(prop, value)

View File

@@ -0,0 +1 @@
uid://bpyif5ki478i2

View File

@@ -0,0 +1,236 @@
[gd_scene load_steps=7 format=3 uid="uid://o7ljiritpgap"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_modules.gd" id="1_l2hk0"]
[sub_resource type="Image" id="Image_570p8"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_lce2m"]
image = SubResource("Image_570p8")
[sub_resource type="Image" id="Image_ihhvm"]
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_137g7"]
image = SubResource("Image_ihhvm")
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_315cl"]
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
[node name="ModuleManagement" type="HSplitContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_bottom = -157.0
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 3
theme_override_constants/separation = 0
script = ExtResource("1_l2hk0")
short_info = "Here you can manage modules:
- change event defaults
- hide events from the event list"
[node name="Overview" type="VBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="ScrollContainer" type="ScrollContainer" parent="Overview"]
layout_mode = 2
size_flags_horizontal = 3
follow_focus = true
horizontal_scroll_mode = 3
vertical_scroll_mode = 0
[node name="HBox" type="HBoxContainer" parent="Overview/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 8
alignment = 2
[node name="Filter_Events" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Include Events"
toggle_mode = true
button_pressed = true
text = "0"
flat = true
icon_alignment = 2
[node name="Filter_Subsystems" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Include Subsystems"
toggle_mode = true
button_pressed = true
text = "0"
flat = true
icon_alignment = 2
[node name="Filter_EffectsAndModifiers" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Include Text Effects and Modifiers"
toggle_mode = true
button_pressed = true
text = "0"
flat = true
icon_alignment = 2
[node name="Filter_Styles" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Include Preset Style Scenes"
toggle_mode = true
button_pressed = true
text = "0"
flat = true
icon_alignment = 2
[node name="Filter_Settings" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Include Settings Pages"
toggle_mode = true
text = "0"
flat = true
icon_alignment = 2
[node name="Filter_Editors" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Include Editors"
toggle_mode = true
text = "0"
flat = true
icon_alignment = 2
[node name="Search" type="LineEdit" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
placeholder_text = "Search"
clear_button_enabled = true
[node name="Refresh" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Refresh"
[node name="Collapse" type="Button" parent="Overview/ScrollContainer/HBox"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Collapse All"
toggle_mode = true
[node name="Tree" type="Tree" parent="Overview"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
allow_reselect = true
hide_root = true
[node name="Scroll" type="ScrollContainer" parent="."]
show_behind_parent = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
size_flags_stretch_ratio = 0.75
horizontal_scroll_mode = 3
vertical_scroll_mode = 0
[node name="Settings" type="VBoxContainer" parent="Scroll"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="HBox" type="HBoxContainer" parent="Scroll/Settings"]
layout_mode = 2
[node name="Icon" type="TextureRect" parent="Scroll/Settings/HBox"]
unique_name_in_owner = true
layout_mode = 2
expand_mode = 3
[node name="Title" type="Label" parent="Scroll/Settings/HBox"]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"DialogicSubTitle"
[node name="ExternalLink" type="Button" parent="Scroll/Settings/HBox"]
unique_name_in_owner = true
visible = false
layout_mode = 2
icon = SubResource("ImageTexture_lce2m")
flat = true
[node name="VisibilityToggle" type="Button" parent="Scroll/Settings/HBox"]
unique_name_in_owner = true
visible = false
layout_mode = 2
toggle_mode = true
button_pressed = true
icon = SubResource("ImageTexture_137g7")
flat = true
[node name="EventDefaultsPanel" type="PanelContainer" parent="Scroll/Settings"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_315cl")
[node name="VBox" type="VBoxContainer" parent="Scroll/Settings/EventDefaultsPanel"]
layout_mode = 2
[node name="Title" type="Label" parent="Scroll/Settings/EventDefaultsPanel/VBox"]
layout_mode = 2
text = "Edit event defaults:"
[node name="EventDefaults" type="GridContainer" parent="Scroll/Settings/EventDefaultsPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
columns = 3
[node name="GeneralInfo" type="Label" parent="Scroll/Settings"]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"DialogicHintText2"
autowrap_mode = 3
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Filter_Events" to="." method="filters_updated"]
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Filter_Subsystems" to="." method="filters_updated"]
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Filter_EffectsAndModifiers" to="." method="filters_updated"]
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Filter_Styles" to="." method="filters_updated"]
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Filter_Settings" to="." method="filters_updated"]
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Filter_Editors" to="." method="filters_updated"]
[connection signal="text_changed" from="Overview/ScrollContainer/HBox/Search" to="." method="_on_search_text_changed"]
[connection signal="pressed" from="Overview/ScrollContainer/HBox/Refresh" to="." method="_on_refresh_pressed"]
[connection signal="toggled" from="Overview/ScrollContainer/HBox/Collapse" to="." method="_on_collapse_toggled"]
[connection signal="button_clicked" from="Overview/Tree" to="." method="_on_tree_button_clicked"]
[connection signal="item_selected" from="Overview/Tree" to="." method="_on_tree_item_selected"]
[connection signal="pressed" from="Scroll/Settings/HBox/ExternalLink" to="." method="_on_external_link_pressed"]
[connection signal="toggled" from="Scroll/Settings/HBox/VisibilityToggle" to="." method="_on_visibility_toggle_toggled"]

View File

@@ -0,0 +1,35 @@
@tool
extends Control
class_name DialogicSettingsPage
@export_multiline var short_info := ""
## Called to get the title of the page
func _get_title() -> String:
return name
## Called to get the ordering of the page
func _get_priority() -> int:
return 0
## Called to know whether to put this in the features section
func _is_feature_tab() -> bool:
return false
## Called when the settings editor is opened
func _refresh() -> void:
pass
## Called before the settings editor closes (another editor is opened)
## Can be used to safe stuff
func _about_to_close() -> void:
pass
## Return a section with information.
func _get_info_section() -> Control:
return null

View File

@@ -0,0 +1 @@
uid://dlk077afvj4jy

View File

@@ -0,0 +1,660 @@
@tool
extends DialogicSettingsPage
## Settings tab that allows enabeling and updating translation csv-files.
enum TranslationModes {PER_PROJECT, PER_TIMELINE, NONE}
enum SaveLocationModes {INSIDE_TRANSLATION_FOLDER, NEXT_TO_TIMELINE, NONE}
var loading := false
@onready var settings_editor: Control = find_parent('Settings')
## The default CSV filename that contains the translations for character
## properties.
const DEFAULT_CHARACTER_CSV_NAME := "dialogic_character_translations.csv"
## The default CSV filename that contains the translations for timelines.
## Only used when all timelines are supposed to be translated in one file.
const DEFAULT_TIMELINE_CSV_NAME := "dialogic_timeline_translations.csv"
const DEFAULT_GLOSSARY_CSV_NAME := "dialogic_glossary_translations.csv"
const _USED_LOCALES_SETTING := "dialogic/translation/locales"
## Contains translation changes that were made during the last update.
## Unique locales that will be set after updating the CSV files.
var _unique_locales := []
func _get_icon() -> Texture2D:
return get_theme_icon("Translation", "EditorIcons")
func _is_feature_tab() -> bool:
return true
func _ready() -> void:
%TransEnabled.toggled.connect(store_changes)
%OrigLocale.get_suggestions_func = get_locales
%OrigLocale.resource_icon = get_theme_icon("Translation", "EditorIcons")
%OrigLocale.value_changed.connect(store_changes)
%TestingLocale.get_suggestions_func = get_locales
%TestingLocale.resource_icon = get_theme_icon("Translation", "EditorIcons")
%TestingLocale.value_changed.connect(store_changes)
%TransFolderPicker.value_changed.connect(store_changes)
%AddSeparatorEnabled.toggled.connect(store_changes)
%SaveLocationMode.item_selected.connect(store_changes)
%TransMode.item_selected.connect(store_changes)
%UpdateCsvFiles.pressed.connect(_on_update_translations_pressed)
%UpdateCsvFiles.icon = get_theme_icon("Add", "EditorIcons")
%CollectTranslations.pressed.connect(collect_translations)
%CollectTranslations.icon = get_theme_icon("File", "EditorIcons")
%TransRemove.pressed.connect(_on_erase_translations_pressed)
%TransRemove.icon = get_theme_icon("Remove", "EditorIcons")
%UpdateConfirmationDialog.add_button("Keep old & Generate new", false, "generate_new")
%UpdateConfirmationDialog.custom_action.connect(_on_custom_action)
_verify_translation_file()
func _on_custom_action(action: String) -> void:
if action == "generate_new":
update_csv_files()
func _refresh() -> void:
loading = true
%TransEnabled.button_pressed = ProjectSettings.get_setting('dialogic/translation/enabled', false)
%TranslationSettings.visible = %TransEnabled.button_pressed
%OrigLocale.set_value(ProjectSettings.get_setting('dialogic/translation/original_locale', TranslationServer.get_tool_locale()))
%TransMode.select(ProjectSettings.get_setting('dialogic/translation/file_mode', 1))
%TransFolderPicker.set_value(ProjectSettings.get_setting('dialogic/translation/translation_folder', ''))
%TestingLocale.set_value(ProjectSettings.get_setting('internationalization/locale/test', ''))
%AddSeparatorEnabled.button_pressed = ProjectSettings.get_setting('dialogic/translation/add_separator', false)
_verify_translation_file()
loading = false
func store_changes(_fake_arg: Variant = null, _fake_arg2: Variant = null) -> void:
if loading:
return
_verify_translation_file()
ProjectSettings.set_setting('dialogic/translation/enabled', %TransEnabled.button_pressed)
%TranslationSettings.visible = %TransEnabled.button_pressed
ProjectSettings.set_setting('dialogic/translation/original_locale', %OrigLocale.current_value)
ProjectSettings.set_setting('dialogic/translation/file_mode', %TransMode.selected)
ProjectSettings.set_setting('dialogic/translation/translation_folder', %TransFolderPicker.current_value)
ProjectSettings.set_setting('internationalization/locale/test', %TestingLocale.current_value)
ProjectSettings.set_setting('dialogic/translation/save_mode', %SaveLocationMode.selected)
ProjectSettings.set_setting('dialogic/translation/add_separator', %AddSeparatorEnabled.button_pressed)
ProjectSettings.save()
## Checks whether the translation folder path is required.
## If it is, disables the "Update CSV files" button and shows a warning.
##
## The translation folder path is required when either of the following is true:
## - The translation mode is set to "Per Project".
## - The save location mode is set to "Inside Translation Folder".
func _verify_translation_file() -> void:
var translation_folder: String = %TransFolderPicker.current_value
var file_mode: TranslationModes = %TransMode.selected
if file_mode == TranslationModes.PER_PROJECT:
%SaveLocationMode.disabled = true
else:
%SaveLocationMode.disabled = false
var valid_translation_folder := (!translation_folder.is_empty()
and DirAccess.dir_exists_absolute(translation_folder))
%UpdateCsvFiles.disabled = not valid_translation_folder
var status_message := ""
if not valid_translation_folder:
status_message += "⛔ Requires valid translation folder to translate character names"
if file_mode == TranslationModes.PER_PROJECT:
status_message += " and the project CSV file."
else:
status_message += "."
%StatusMessage.text = status_message
func get_locales(_filter: String) -> Dictionary:
var suggestions := {}
suggestions['Default'] = {'value':'', 'tooltip':"Will use the fallback locale set in the project settings."}
suggestions[TranslationServer.get_tool_locale()] = {'value':TranslationServer.get_tool_locale()}
var used_locales: Array = ProjectSettings.get_setting(_USED_LOCALES_SETTING, TranslationServer.get_all_languages())
for locale: String in used_locales:
var language_name := TranslationServer.get_language_name(locale)
# Invalid locales return an empty String.
if language_name.is_empty():
continue
suggestions[locale] = { 'value': locale, 'tooltip': language_name }
return suggestions
func _on_update_translations_pressed() -> void:
var save_mode: SaveLocationModes = %SaveLocationMode.selected
var file_mode: TranslationModes = %TransMode.selected
var translation_folder: String = %TransFolderPicker.current_value
var old_save_mode: SaveLocationModes = ProjectSettings.get_setting('dialogic/translation/intern/save_mode', save_mode)
var old_file_mode: TranslationModes = ProjectSettings.get_setting('dialogic/translation/intern/file_mode', file_mode)
var old_translation_folder: String = ProjectSettings.get_setting('dialogic/translation/intern/translation_folder', translation_folder)
if (old_save_mode == save_mode
and old_file_mode == file_mode
and old_translation_folder == translation_folder):
update_csv_files()
return
%UpdateConfirmationDialog.popup_centered()
## Used by the dialog to inform that the settings were changed.
func _delete_and_update() -> void:
erase_translations()
update_csv_files()
## Creates or updates the glossary CSV files.
func _handle_glossary_translation(
csv_data: CsvUpdateData,
save_location_mode: SaveLocationModes,
translation_mode: TranslationModes,
translation_folder_path: String,
orig_locale: String) -> void:
var glossary_csv: DialogicCsvFile = null
var glossary_paths: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
var add_separator_lines: bool = ProjectSettings.get_setting('dialogic/translation/add_separator', false)
for glossary_path: String in glossary_paths:
if glossary_csv == null:
var csv_name := ""
# Get glossary CSV file name.
match translation_mode:
TranslationModes.PER_PROJECT:
csv_name = DEFAULT_GLOSSARY_CSV_NAME
TranslationModes.PER_TIMELINE:
var glossary_name: String = glossary_path.trim_suffix('.tres')
var path_parts := glossary_name.split("/")
var file_name := path_parts[-1]
csv_name = "dialogic_" + file_name + '_translation.csv'
var glossary_csv_path := ""
# Get glossary CSV file path.
match save_location_mode:
SaveLocationModes.INSIDE_TRANSLATION_FOLDER:
glossary_csv_path = translation_folder_path.path_join(csv_name)
SaveLocationModes.NEXT_TO_TIMELINE:
glossary_csv_path = glossary_path.get_base_dir().path_join(csv_name)
# Create or update glossary CSV file.
glossary_csv = DialogicCsvFile.new(glossary_csv_path, orig_locale, add_separator_lines)
if (glossary_csv.is_new_file):
csv_data.new_glossaries += 1
else:
csv_data.updated_glossaries += 1
var glossary: DialogicGlossary = load(glossary_path)
glossary_csv.collect_lines_from_glossary(glossary)
glossary_csv.add_translation_keys_to_glossary(glossary)
ResourceSaver.save(glossary)
#If per-file mode is used, save this csv and begin a new one
if translation_mode == TranslationModes.PER_TIMELINE:
glossary_csv.update_csv_file_on_disk()
glossary_csv = null
# If a Per-Project glossary is still open, we need to save it.
if glossary_csv != null:
glossary_csv.update_csv_file_on_disk()
glossary_csv = null
## Keeps information about the amount of new and updated CSV rows and what
## resources were populated with translation IDs.
## The final data can be used to display a status message.
class CsvUpdateData:
var new_events := 0
var updated_events := 0
var new_timelines := 0
var updated_timelines := 0
var new_names := 0
var updated_names := 0
var new_glossaries := 0
var updated_glossaries := 0
var new_glossary_entries := 0
var updated_glossary_entries := 0
func update_csv_files() -> void:
_unique_locales = []
var orig_locale: String = ProjectSettings.get_setting('dialogic/translation/original_locale', '').strip_edges()
var save_location_mode: SaveLocationModes = ProjectSettings.get_setting('dialogic/translation/save_mode', SaveLocationModes.NEXT_TO_TIMELINE)
var translation_mode: TranslationModes = ProjectSettings.get_setting('dialogic/translation/file_mode', TranslationModes.PER_PROJECT)
var translation_folder_path: String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://')
var add_separator_lines: bool = ProjectSettings.get_setting('dialogic/translation/add_separator', false)
var csv_data := CsvUpdateData.new()
if orig_locale.is_empty():
orig_locale = ProjectSettings.get_setting('internationalization/locale/fallback')
ProjectSettings.set_setting('dialogic/translation/intern/save_mode', save_location_mode)
ProjectSettings.set_setting('dialogic/translation/intern/file_mode', translation_mode)
ProjectSettings.set_setting('dialogic/translation/intern/translation_folder', translation_folder_path)
var current_timeline := _close_active_timeline()
var csv_per_project: DialogicCsvFile = null
var per_project_csv_path := translation_folder_path.path_join(DEFAULT_TIMELINE_CSV_NAME)
if translation_mode == TranslationModes.PER_PROJECT:
csv_per_project = DialogicCsvFile.new(per_project_csv_path, orig_locale, add_separator_lines)
if (csv_per_project.is_new_file):
csv_data.new_timelines += 1
else:
csv_data.updated_timelines += 1
# Iterate over all timelines.
# Create or update CSV files.
# Transform the timeline into translatable lines and collect into the CSV file.
for timeline_path: String in DialogicResourceUtil.list_resources_of_type('.dtl'):
var csv_file: DialogicCsvFile = csv_per_project
# Swap the CSV file to the Per Timeline one.
if translation_mode == TranslationModes.PER_TIMELINE:
var per_timeline_path: String = timeline_path.trim_suffix('.dtl')
var path_parts := per_timeline_path.split("/")
var timeline_name: String = path_parts[-1]
# Adjust the file path to the translation location mode.
if save_location_mode == SaveLocationModes.INSIDE_TRANSLATION_FOLDER:
var prefixed_timeline_name := "dialogic_" + timeline_name
per_timeline_path = translation_folder_path.path_join(prefixed_timeline_name)
per_timeline_path += '_translation.csv'
csv_file = DialogicCsvFile.new(per_timeline_path, orig_locale, false)
csv_data.new_timelines += 1
# Load and process timeline, turn events into resources.
var timeline: DialogicTimeline = load(timeline_path)
if timeline.events.size() == 0:
print_rich("[color=yellow]Empty timeline, skipping: " + timeline_path + "[/color]")
continue
timeline.process()
# Collect timeline into CSV.
csv_file.collect_lines_from_timeline(timeline)
# in case new translation_id's were added, we save the timeline again
timeline.set_meta("timeline_not_saved", true)
ResourceSaver.save(timeline, timeline_path)
if translation_mode == TranslationModes.PER_TIMELINE:
csv_file.update_csv_file_on_disk()
csv_data.new_events += csv_file.new_rows
csv_data.updated_events += csv_file.updated_rows
_handle_glossary_translation(
csv_data,
save_location_mode,
translation_mode,
translation_folder_path,
orig_locale
)
_handle_character_names(
csv_data,
orig_locale,
translation_folder_path,
add_separator_lines
)
if translation_mode == TranslationModes.PER_PROJECT:
csv_per_project.update_csv_file_on_disk()
_silently_open_timeline(current_timeline)
# Trigger reimport.
find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources()
var status_message := "Events created {new_events} found {updated_events}
Names created {new_names} found {updated_names}
CSVs created {new_timelines} found {updated_timelines}
Glossary created {new_glossaries} found {updated_glossaries}
Entries created {new_glossary_entries} found {updated_glossary_entries}"
var status_message_args := {
'new_events': csv_data.new_events,
'updated_events': csv_data.updated_events,
'new_timelines': csv_data.new_timelines,
'updated_timelines': csv_data.updated_timelines,
'new_glossaries': csv_data.new_glossaries,
'updated_glossaries': csv_data.updated_glossaries,
'new_names': csv_data.new_names,
'updated_names': csv_data.updated_names,
'new_glossary_entries': csv_data.new_glossary_entries,
'updated_glossary_entries': csv_data.updated_glossary_entries,
}
%StatusMessage.text = status_message.format(status_message_args)
ProjectSettings.set_setting(_USED_LOCALES_SETTING, _unique_locales)
## Iterates over all character resource files and creates or updates CSV files
## that contain the translations for character properties.
## This will save each character resource file to disk.
func _handle_character_names(
csv_data: CsvUpdateData,
original_locale: String,
translation_folder_path: String,
add_separator_lines: bool) -> void:
var names_csv_path := translation_folder_path.path_join(DEFAULT_CHARACTER_CSV_NAME)
var character_name_csv: DialogicCsvFile = DialogicCsvFile.new(names_csv_path,
original_locale,
add_separator_lines
)
var all_characters := {}
for character_path: String in DialogicResourceUtil.list_resources_of_type('.dch'):
var character: DialogicCharacter = load(character_path)
if character._translation_id.is_empty():
csv_data.new_names += 1
else:
csv_data.updated_names += 1
var translation_id := character.get_set_translation_id()
all_characters[translation_id] = character
ResourceSaver.save(character)
character_name_csv.collect_lines_from_characters(all_characters)
character_name_csv.update_csv_file_on_disk()
func collect_translations() -> void:
var translation_files := []
var translation_mode: TranslationModes = ProjectSettings.get_setting('dialogic/translation/file_mode', TranslationModes.PER_PROJECT)
if translation_mode == TranslationModes.PER_TIMELINE:
for timeline_path: String in DialogicResourceUtil.list_resources_of_type('.translation'):
for file: String in DialogicUtil.listdir(timeline_path.get_base_dir()):
file = timeline_path.get_base_dir().path_join(file)
if file.ends_with('.translation'):
if not file in translation_files:
translation_files.append(file)
if translation_mode == TranslationModes.PER_PROJECT:
var translation_folder: String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://')
for file: String in DialogicUtil.listdir(translation_folder):
file = translation_folder.path_join(file)
if file.ends_with('.translation'):
if not file in translation_files:
translation_files.append(file)
var all_translation_files: Array = ProjectSettings.get_setting('internationalization/locale/translations', [])
var orig_file_amount := len(all_translation_files)
# This array keeps track of valid translation file paths.
var found_file_paths := []
var removed_translation_files := 0
for file_path: String in translation_files:
# If the file path is not valid, we must clean it up.
if ResourceLoader.exists(file_path):
found_file_paths.append(file_path)
else:
removed_translation_files += 1
continue
if not file_path in all_translation_files:
all_translation_files.append(file_path)
var path_without_suffix := file_path.trim_suffix('.translation')
var locale_part := path_without_suffix.split(".")[-1]
_collect_locale(locale_part)
var valid_translation_files := PackedStringArray(all_translation_files)
ProjectSettings.set_setting('internationalization/locale/translations', valid_translation_files)
ProjectSettings.save()
%StatusMessage.text = (
"Added translation files: " + str(len(all_translation_files)-orig_file_amount)
+ "\nRemoved translation files: " + str(removed_translation_files)
+ "\nTotal translation files: " + str(len(all_translation_files)))
func _on_erase_translations_pressed() -> void:
%EraseConfirmationDialog.popup_centered()
## Deletes translation files generated by [param csv_name].
## The [param csv_name] may not contain the file extension (.csv).
##
## Returns a vector, value 1 is amount of deleted translation files.
## Value
func delete_translations_files(translation_files: Array, csv_name: String) -> int:
var deleted_files := 0
for file_path: String in DialogicResourceUtil.list_resources_of_type('.translation'):
var base_name: String = file_path.get_basename()
var path_parts := base_name.split("/")
var translation_name: String = path_parts[-1]
if translation_name.begins_with(csv_name):
if OK == DirAccess.remove_absolute(file_path):
var project_translation_file_index := translation_files.find(file_path)
if project_translation_file_index > -1:
translation_files.remove_at(project_translation_file_index)
deleted_files += 1
print_rich("[color=green]Deleted translation file: " + file_path + "[/color]")
else:
print_rich("[color=yellow]Failed to delete translation file: " + file_path + "[/color]")
return deleted_files
## Iterates over all timelines and deletes their CSVs and timeline
## translation IDs.
## Deletes the Per-Project CSV file and the character name CSV file.
func erase_translations() -> void:
var files: PackedStringArray = ProjectSettings.get_setting('internationalization/locale/translations', [])
var translation_files := Array(files)
ProjectSettings.set_setting(_USED_LOCALES_SETTING, [])
var deleted_csv_files := 0
var deleted_translation_files := 0
var cleaned_timelines := 0
var cleaned_characters := 0
var cleaned_events := 0
var cleaned_glossaries := 0
var current_timeline := _close_active_timeline()
# Delete all Dialogic CSV files and their translation files.
for csv_path: String in DialogicResourceUtil.list_resources_of_type(".csv"):
var csv_path_parts: PackedStringArray = csv_path.split("/")
var csv_name: String = csv_path_parts[-1].trim_suffix(".csv")
# Handle Dialogic CSVs only.
if not csv_name.begins_with("dialogic_"):
continue
# Delete the CSV file.
if OK == DirAccess.remove_absolute(csv_path):
deleted_csv_files += 1
print_rich("[color=green]Deleted CSV file: " + csv_path + "[/color]")
deleted_translation_files += delete_translations_files(translation_files, csv_name)
else:
print_rich("[color=yellow]Failed to delete CSV file: " + csv_path + "[/color]")
# Clean timelines.
for timeline_path: String in DialogicResourceUtil.list_resources_of_type(".dtl"):
# Process the timeline.
var timeline: DialogicTimeline = load(timeline_path)
timeline.process()
cleaned_timelines += 1
# Remove event translation IDs.
for event: DialogicEvent in timeline.events:
if event._translation_id and not event._translation_id.is_empty():
event.remove_translation_id()
event.update_text_version()
cleaned_events += 1
if "character" in event:
# Remove character translation IDs.
var character: DialogicCharacter = event.character
if character != null and not character._translation_id.is_empty():
character.remove_translation_id()
cleaned_characters += 1
timeline.set_meta("timeline_not_saved", true)
ResourceSaver.save(timeline, timeline_path)
_erase_glossary_translation_ids()
_erase_character_name_translation_ids()
ProjectSettings.set_setting('dialogic/translation/id_counter', 16)
ProjectSettings.set_setting('internationalization/locale/translations', PackedStringArray(translation_files))
ProjectSettings.save()
find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources()
var status_message := "Timelines cleaned {cleaned_timelines}
Events cleaned {cleaned_events}
Characters cleaned {cleaned_characters}
Glossaries cleaned {cleaned_glossaries}
CSVs erased {erased_csv_files}
Translations erased {erased_translation_files}"
var status_message_args := {
'cleaned_timelines': cleaned_timelines,
'cleaned_characters': cleaned_characters,
'cleaned_events': cleaned_events,
'cleaned_glossaries': cleaned_glossaries,
'erased_csv_files': deleted_csv_files,
'erased_translation_files': deleted_translation_files,
}
_silently_open_timeline(current_timeline)
# Trigger reimport.
find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources()
# Clear the internal settings.
ProjectSettings.clear('dialogic/translation/intern/save_mode')
ProjectSettings.clear('dialogic/translation/intern/file_mode')
ProjectSettings.clear('dialogic/translation/intern/translation_folder')
_verify_translation_file()
%StatusMessage.text = status_message.format(status_message_args)
func _erase_glossary_translation_ids() -> void:
# Clean glossary.
var glossary_paths: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
for glossary_path: String in glossary_paths:
var glossary: DialogicGlossary = load(glossary_path)
glossary.remove_translation_id()
glossary.remove_entry_translation_ids()
glossary.clear_translation_keys()
ResourceSaver.save(glossary, glossary_path)
print_rich("[color=green]Cleaned up glossary file: " + glossary_path + "[/color]")
func _erase_character_name_translation_ids() -> void:
for character_path: String in DialogicResourceUtil.list_resources_of_type('.dch'):
var character: DialogicCharacter = load(character_path)
character.remove_translation_id()
ResourceSaver.save(character)
## Closes the current timeline in the Dialogic Editor and returns the timeline
## as a resource.
## If no timeline has been opened, returns null.
func _close_active_timeline() -> Resource:
var timeline_node: DialogicEditor = settings_editor.editors_manager.editors['Timeline']['node']
# We will close this timeline to ensure it will properly update.
# By saving this reference, we can open it again.
var current_timeline := timeline_node.current_resource
# Clean the current editor, this will also close the timeline.
settings_editor.editors_manager.clear_editor(timeline_node)
return current_timeline
## Opens the timeline resource into the Dialogic Editor.
## If the timeline is null, does nothing.
func _silently_open_timeline(timeline_to_open: Resource) -> void:
if timeline_to_open != null:
settings_editor.editors_manager.edit_resource(timeline_to_open, true, true)
## Checks [param locale] for unique locales that have not been added
## to the [_unique_locales] array yet.
func _collect_locale(locale: String) -> void:
if _unique_locales.has(locale):
return
_unique_locales.append(locale)

View File

@@ -0,0 +1 @@
uid://b1w1n8qm57s4b

View File

@@ -0,0 +1,368 @@
[gd_scene load_steps=7 format=3 uid="uid://chpb1mj03xjxv"]
[ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_translation.gd" id="1_dvmyi"]
[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_k2lou"]
[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3_dq4j2"]
[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="4_kvsma"]
[sub_resource type="Image" id="Image_g2hic"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_xbph7"]
image = SubResource("Image_g2hic")
[node name="Translations" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -101.0
offset_bottom = 102.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_dvmyi")
[node name="HBox" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="Basics" type="VBoxContainer" parent="HBox"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Title" type="Label" parent="HBox/Basics"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Basics"
[node name="VBox4" type="HBoxContainer" parent="HBox/Basics"]
layout_mode = 2
[node name="Label" type="Label" parent="HBox/Basics/VBox4"]
layout_mode = 2
text = "Enable translations"
[node name="TransEnabled" type="CheckBox" parent="HBox/Basics/VBox4"]
unique_name_in_owner = true
layout_mode = 2
[node name="HSeparator5" type="VSeparator" parent="HBox"]
layout_mode = 2
[node name="Testing" type="VBoxContainer" parent="HBox"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Title2" type="Label" parent="HBox/Testing"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Testing"
[node name="VBox3" type="HBoxContainer" parent="HBox/Testing"]
layout_mode = 2
[node name="Label3" type="Label" parent="HBox/Testing/VBox3"]
layout_mode = 2
text = "Testing locale"
[node name="HintTooltip8" parent="HBox/Testing/VBox3" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Change this locale to test your game in a different language (only in-editor).
Equivalent of the testing local project setting. "
texture = SubResource("ImageTexture_xbph7")
hint_text = "Change this locale to test your game in a different language (only in-editor).
Equivalent of the testing local project setting.
Update dropdown list via \"Collect Translation\"."
[node name="TestingLocale" parent="HBox/Testing/VBox3" instance=ExtResource("3_dq4j2")]
unique_name_in_owner = true
layout_mode = 2
[node name="HSeparator4" type="HSeparator" parent="."]
layout_mode = 2
[node name="TranslationSettings" type="HBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="TranslationSettings"]
layout_mode = 2
size_flags_horizontal = 3
[node name="SettingsTitle" type="Label" parent="TranslationSettings/VBoxContainer"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Settings"
[node name="Grid" type="GridContainer" parent="TranslationSettings/VBoxContainer"]
layout_mode = 2
columns = 2
[node name="VBox" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"]
layout_mode = 2
[node name="Label3" type="Label" parent="TranslationSettings/VBoxContainer/Grid/VBox"]
layout_mode = 2
text = "Default locale"
[node name="HintTooltip" parent="TranslationSettings/VBoxContainer/Grid/VBox" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "The locale of the language your timelines are written in."
texture = SubResource("ImageTexture_xbph7")
hint_text = "The locale of the language your timelines are written in."
[node name="OrigLocale" parent="TranslationSettings/VBoxContainer/Grid" instance=ExtResource("3_dq4j2")]
unique_name_in_owner = true
layout_mode = 2
[node name="TransFile" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"]
layout_mode = 2
[node name="Label" type="Label" parent="TranslationSettings/VBoxContainer/Grid/TransFile"]
layout_mode = 2
text = "Translation folder"
[node name="HintTooltip3" parent="TranslationSettings/VBoxContainer/Grid/TransFile" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Choose a folder to let Dialogic save CSV files in.
Also used when saving \"Inside Translation Folder\""
texture = SubResource("ImageTexture_xbph7")
hint_text = "Choose a folder to let Dialogic save CSV files in.
Also used when saving \"Inside Translation Folder\""
[node name="TransFolderPicker" parent="TranslationSettings/VBoxContainer/Grid" instance=ExtResource("4_kvsma")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
file_mode = 2
[node name="VBox2" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"]
layout_mode = 2
[node name="OutputModeLabel" type="Label" parent="TranslationSettings/VBoxContainer/Grid/VBox2"]
layout_mode = 2
text = "Output mode"
[node name="OutputModeTooltip" parent="TranslationSettings/VBoxContainer/Grid/VBox2" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Decides how many CSV files will be created.
• \"Per Type\": Uses one CSV file for each type of resource: Timelines, characters, and glossaries.
For example, 10 timelines will be combined into 1 CSV file.
• \"Per File\": Uses one CSV file for each resource file.
For example, 10 timelines will result in 10 CSV files.
The \"Per File\" option utilises \"Output location\", in contrast, the \"Per Type\" will always use the Translation folder."
texture = SubResource("ImageTexture_xbph7")
hint_text = "Decides how many CSV files will be created.
• \"Per Type\": Uses one CSV file for each type of resource: Timelines, characters, and glossaries.
For example, 10 timelines will be combined into 1 CSV file.
• \"Per File\": Uses one CSV file for each resource file.
For example, 10 timelines will result in 10 CSV files.
The \"Per File\" option utilises \"Output location\", in contrast, the \"Per Type\" will always use the Translation folder."
[node name="TransMode" type="OptionButton" parent="TranslationSettings/VBoxContainer/Grid"]
unique_name_in_owner = true
layout_mode = 2
item_count = 2
selected = 0
popup/item_0/text = "Per Type"
popup/item_0/id = 0
popup/item_1/text = "Per File"
popup/item_1/id = 1
[node name="OutputLocation" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"]
layout_mode = 2
[node name="OutputLocationLabel" type="Label" parent="TranslationSettings/VBoxContainer/Grid/OutputLocation"]
layout_mode = 2
text = "Output location"
[node name="OutputLocationTooltip" parent="TranslationSettings/VBoxContainer/Grid/OutputLocation" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Decides where to save the generated CSV files.
• \"Inside Translation Folder\": Uses the \"Translation folder\".
• \"Next To Timeline\": Places them in the resource type's folder.
This button requires the \"Per File\" Output mode.
A resource type can be: Timelines, characters, and glossaries."
texture = SubResource("ImageTexture_xbph7")
hint_text = "Decides where to save the generated CSV files.
• \"Inside Translation Folder\": Uses the \"Translation folder\".
• \"Next To Timeline\": Places them in the resource type's folder.
This button requires the \"Per File\" Output mode.
A resource type can be: Timelines, characters, and glossaries."
[node name="SaveLocationMode" type="OptionButton" parent="TranslationSettings/VBoxContainer/Grid"]
unique_name_in_owner = true
layout_mode = 2
disabled = true
item_count = 2
selected = 0
popup/item_0/text = "Inside Translation Folder"
popup/item_0/id = 0
popup/item_1/text = "Next to File"
popup/item_1/id = 1
[node name="Control" type="Control" parent="TranslationSettings/VBoxContainer/Grid"]
visible = false
layout_mode = 2
[node name="AddSeparatorHBox" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"]
layout_mode = 2
[node name="AddSeparatorLabel" type="Label" parent="TranslationSettings/VBoxContainer/Grid/AddSeparatorHBox"]
layout_mode = 2
text = "Add Separator Lines"
[node name="HintAddSeparatorEnabled" parent="TranslationSettings/VBoxContainer/Grid/AddSeparatorHBox" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Adds an empty line into per-project CSVs to differentiate between sections.
For example, when a new glossary item or timeline starts, an empty line will be added."
texture = SubResource("ImageTexture_xbph7")
hint_text = "Adds an empty line into per-project CSVs to differentiate between sections.
For example, when a new glossary item or timeline starts, an empty line will be added."
[node name="AddSeparatorEnabled" type="CheckBox" parent="TranslationSettings/VBoxContainer/Grid"]
unique_name_in_owner = true
layout_mode = 2
[node name="HSeparator6" type="VSeparator" parent="TranslationSettings"]
layout_mode = 2
[node name="VBoxContainer2" type="VBoxContainer" parent="TranslationSettings"]
layout_mode = 2
size_flags_horizontal = 3
[node name="HBoxContainer" type="HBoxContainer" parent="TranslationSettings/VBoxContainer2"]
layout_mode = 2
[node name="Title3" type="Label" parent="TranslationSettings/VBoxContainer2/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"DialogicSettingsSection"
text = "Actions"
[node name="Actions" type="GridContainer" parent="TranslationSettings/VBoxContainer2"]
layout_mode = 2
columns = 2
[node name="UpdateCsvFiles" type="Button" parent="TranslationSettings/VBoxContainer2/Actions"]
unique_name_in_owner = true
layout_mode = 2
disabled = true
text = "Update CSV files"
icon = SubResource("ImageTexture_xbph7")
[node name="HintTooltip5" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "This button will scan all timelines and generate or update their CSV files.
A Dialogic CSV file will be prefixed with \"dialogic_\".
This action will be disabled if the \"Translation folder\" is missing or has an invalid path."
texture = SubResource("ImageTexture_xbph7")
hint_text = "This button will scan all timelines and generate or update their CSV files.
A Dialogic CSV file will be prefixed with \"dialogic_\".
This action will be disabled if the \"Translation folder\" is missing or has an invalid path."
[node name="CollectTranslations" type="Button" parent="TranslationSettings/VBoxContainer2/Actions"]
unique_name_in_owner = true
layout_mode = 2
text = "Collect translations"
icon = SubResource("ImageTexture_xbph7")
[node name="HintTooltip6" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Godot imports CSV files as \".translation\" files.
This buttons adds them to \"Project Settings -> Localization\".
"
texture = SubResource("ImageTexture_xbph7")
hint_text = "Godot imports CSV files as \".translation\" files.
This buttons adds them to \"Project Settings -> Localization\".
"
[node name="AspectRatioContainer2" type="AspectRatioContainer" parent="TranslationSettings/VBoxContainer2/Actions"]
custom_minimum_size = Vector2(0, 31)
layout_mode = 2
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="TranslationSettings/VBoxContainer2/Actions"]
custom_minimum_size = Vector2(0, 31)
layout_mode = 2
[node name="TransRemove" type="Button" parent="TranslationSettings/VBoxContainer2/Actions"]
unique_name_in_owner = true
layout_mode = 2
text = "Remove translations"
icon = SubResource("ImageTexture_xbph7")
[node name="HintTooltip7" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")]
layout_mode = 2
tooltip_text = "Be very careful with this button!
It will try to delete any \".csv\" and \".translation\" files that are related to Dialogic.
CSV and translation files prefixed with \"dialogic_\" are treated as Dialogic-related.
Removes translation IDs (eg. #id:33) from timelines and characters."
texture = SubResource("ImageTexture_xbph7")
hint_text = "Be very careful with this button!
It will try to delete any \".csv\" and \".translation\" files that are related to Dialogic.
CSV and translation files prefixed with \"dialogic_\" are treated as Dialogic-related.
Removes translation IDs (eg. #id:33) from timelines and characters."
[node name="StatusMessage" type="Label" parent="TranslationSettings/VBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
text = "⛔ Requires valid translation folder to translate character names and the project CSV file."
autowrap_mode = 3
[node name="UpdateConfirmationDialog" type="ConfirmationDialog" parent="."]
unique_name_in_owner = true
title = "Please Decide..."
size = Vector2i(490, 200)
ok_button_text = "Delete old & Generate new"
dialog_text = "You have previously generated CSVs and translation files with different Translation Settings!
Please consider to delete the old CSVs and then generate new changes."
dialog_autowrap = true
[node name="EraseConfirmationDialog" type="ConfirmationDialog" parent="."]
unique_name_in_owner = true
position = Vector2i(0, 36)
size = Vector2i(500, 280)
min_size = Vector2i(300, 70)
ok_button_text = "DELETE ALL"
dialog_text = "You are about to:
- Delete all CSVs prefixed with \"dialogic_\".
- Delete the related CSV import files.
- Delete the related translation files.
- Remove translation IDs from timelines and characters.
- Remove all \"dialogic\" prefixed translations from \"Project Settings -> Localization\".
- Remove the \"_translation_keys\" and \"entries\" starting with \"Glossary/\"."
dialog_autowrap = true
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="."]
custom_minimum_size = Vector2(0, 31)
layout_mode = 2
[connection signal="confirmed" from="UpdateConfirmationDialog" to="." method="_delete_and_update"]
[connection signal="confirmed" from="EraseConfirmationDialog" to="." method="erase_translations"]