diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..8ad74f78d9c9b9b8f3d68772c6d5aa0cb3fe47e9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c8c2ba8d01466b6da3a16b1fca91f22e76ac25ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json + +# System/tool-specific ignores +.directory +*~ + +# Project-specific ignores +/visuals +/audio +/godot/debug/save +.godot/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..2e26215cb86694c6efc8646911353dc88e7b47de --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +## v0.1.0 Project Demo 🏃 2023-05-19 + +### New + +The initial demo is to verify the direction of the 4.0 rewrite. The codebase is rewritten from scratch, being refactored to include Godot 4 updates as well as GDQuest best practices. + +This first demo tackles player movement throughout the gameworld. This is accomplished primarily via Godot's built-in physics engine, which is used to govern "where everything is and may be". + +Gamepiece movement is grid-based (as in the original Final Fantasy and similar games) and concurrent (gamepieces may move simultaneously). The player may move via gamepad, touch screen, or mouse + keyboard. The source code is documented and should provide a robust starting point for any RPG or Roguelike. + +Please turn on 'Debug/Visible Collision Shapes' in the editor to better see where everything is located on the grid. + +### Changes + +- Changed game assets to use Kenney's Tiny Town set. The art is upscaled 5x in-game. +- Updated the changelog and reset the versioning, as this is essentially a new project. +- Added a credits file providing attribution to project assets. diff --git a/CREDITS.md b/CREDITS.md new file mode 100644 index 0000000000000000000000000000000000000000..c4987a8f8d95e402340701b7d662fb442553d661 --- /dev/null +++ b/CREDITS.md @@ -0,0 +1,18 @@ +# Credits for external assets. + +Insect Factory +Author: Zane Little Music +URL: https://opengameart.org/content/insect-factory-wii-style-music (accessed 2023-06-06) +License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) + +Tiny Series (Tiny Town & Tiny Dungeon) +Author: Kenney +URL: https://kenney.nl/assets/tiny-town & https://kenney.nl/assets/tiny-dungeon (accessed 2023-02-22) +License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) + +Tiny Town - Animated Characters +Author: food_please; derived from Tiny Town (Kenney) & DawnLike (DragonDePlatino) +URL: N/A +License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) + +Please see the [license](LICENSE). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..92ed087debe5f89034ad50246a31b6f44d182b84 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 GDquest + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d206b2b99ed29b77067bd7daba49deb5950f24b2 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Godot 4 Open RPG + +![Godot Open RPG banner](media/Open-RPG.png) + +OpenRPG is a tool and a demo to create Role Playing Games with turn-based combat. + +➡ Follow us on [Twitter](https://twitter.com/NathanGDQuest) and [YouTube](https://www.youtube.com/c/gdquest/) for free game creation tutorials, tips, and news! Get one of our [Godot game creation courses](https://gdquest.mavenseed.com/courses) to support our work on Free Software. + +## Project Goal + +The goal of this project is to provide the gamedev community with a demo that shows how game systems work together in a complete 2D Godot game demo. + +The demo draws inspiration from RPG Maker and older JRPGs like Final Fantasy or Dragon Quest as well as the Godot 3 OpenRPG. + +Throughout the project, heavy emphasis is to be placed on code that is... + +- Updated to take advantage of what GDScript 4 has to offer. +- Simple, to best serve as a starting point for those diving into the project. +- Made to conform to GDQuest's [GDScript guidelines](https://gdquest.gitbook.io/gdquests-guidelines/godot-gdscript-guidelines). + +## Our Mission + +Together, we're creating a codebase and tools to show you some of the best practices to create: + +- Turn-based games +- A combat system +- An inventory system +- Character progression +- User interface with multiple menus + +And more! Do you want to contribute and improve your programming skills with Godot? Check out the open issues, suggest improvements and report bugs by opening new ones, and be sure to check the contributing guidelines below. + +## Contributing Guidelines + +All contributors are welcome 🙂. To ensure a smooth and a productive experience for everyone working together, we came up with some guidelines we all follow here. + +Check our [Contributors Guide](https://gdquest.gitbook.io/gdquests-guidelines/contributing-to-gdquest-projects/) for more information 😄 diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd new file mode 100644 index 0000000000000000000000000000000000000000..106d2ceffeebd927bf6a5c03275e3ac93624fc8e --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd @@ -0,0 +1,101 @@ +@tool +extends DialogicCharacterEditorPortraitSection + +## Tab that allows setting values of exported scene variables +## for custom portrait scenes + +var current_portrait_data := {} + +func _ready() -> void: + add_theme_stylebox_override('panel', get_theme_stylebox("Background", "EditorStyles")) + + $Label.add_theme_color_override("font_color", get_theme_color("readonly_color", "Editor")) + + +func _load_portrait_data(data:Dictionary) -> void: + _recheck(data) + +func _recheck(data:Dictionary): + if data.get('scene', '').is_empty(): + hide() + get_parent().get_child(get_index()-1).hide() + get_parent().get_child(get_index()+1).hide() + else: + get_parent().get_child(get_index()-1).show() + + current_portrait_data = data + load_portrait_scene_export_variables() + + +func load_portrait_scene_export_variables(): + var scene = null + if !current_portrait_data.get('scene', '').is_empty(): + scene = load(current_portrait_data.get('scene')) + + if !scene: + return + + for child in $Grid.get_children(): + child.queue_free() + + scene = scene.instantiate() + for i in scene.script.get_script_property_list(): + if i['usage'] & PROPERTY_USAGE_EDITOR: + var label = Label.new() + label.text = i['name'] + label.add_theme_stylebox_override('normal', get_theme_stylebox("CanvasItemInfoOverlay", "EditorStyles")) + $Grid.add_child(label) + + var current_value :Variant = scene.get(i['name']) + if current_portrait_data.has('export_overrides') and current_portrait_data['export_overrides'].has(i['name']): + current_value = str_to_var(current_portrait_data['export_overrides'][i['name']]) + + var input :Node = DialogicUtil.setup_script_property_edit_node( + i, current_value, + {'bool':_on_export_bool_submitted, 'color':_on_export_color_submitted, 'enum':_on_export_int_enum_submitted, + 'int':_on_export_number_submitted, 'float':_on_export_number_submitted, 'file':_on_export_file_submitted, + 'string':_on_export_input_text_submitted, "string_enum": _on_export_string_enum_submitted}) + + input.size_flags_horizontal = SIZE_EXPAND_FILL + $Grid.add_child(input) + if i['usage'] & PROPERTY_USAGE_GROUP: + var title := Label.new() + title.text = i['name'] + title.add_theme_stylebox_override('normal', get_theme_stylebox("ContextualToolbar", "EditorStyles")) + $Grid.add_child(title) + $Grid.add_child(Control.new()) + + $Label.visible = $Grid.get_child_count() == 0 + + +func set_export_override(property_name:String, value:String = "") -> void: + var data:Dictionary = selected_item.get_metadata(0) + if !data.has('export_overrides'): + data['export_overrides'] = {} + if !value.is_empty(): + data['export_overrides'][property_name] = value + else: + data['export_overrides'].erase(property_name) + changed.emit() + update_preview.emit() + +func _on_export_input_text_submitted(text:String, property_name:String) -> void: + set_export_override(property_name, var_to_str(text)) + +func _on_export_bool_submitted(value:bool, property_name:String) -> void: + set_export_override(property_name, var_to_str(value)) + +func _on_export_color_submitted(color:Color, property_name:String) -> void: + set_export_override(property_name, var_to_str(color)) + +func _on_export_int_enum_submitted(item:int, property_name:String) -> void: + set_export_override(property_name, var_to_str(item)) + +func _on_export_number_submitted(value:float, property_name:String) -> void: + set_export_override(property_name, var_to_str(value)) + +func _on_export_file_submitted(property_name:String, value:String) -> void: + set_export_override(property_name, var_to_str(value)) + +func _on_export_string_enum_submitted(value:int, property_name:String, list:PackedStringArray): + set_export_override(property_name, list[value]) diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn new file mode 100644 index 0000000000000000000000000000000000000000..e42b92d70b1e3a42a42c151e74db7c9b5acb7df8 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd" id="1_isys8"] + +[node name="Exports" type="VBoxContainer"] +custom_minimum_size = Vector2(0, 35) +offset_right = 367.0 +offset_bottom = 82.0 +script = ExtResource("1_isys8") + +[node name="Label" type="Label" parent="."] +layout_mode = 2 +text = "There are no exported variables to override. Add @export properties to the root script of your scene and make sure it's in @tool mode." +autowrap_mode = 3 + +[node name="Grid" type="GridContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/h_separation = 10 +columns = 2 diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd new file mode 100644 index 0000000000000000000000000000000000000000..f2547c1534c41a402d4d026790a217ba5f36f197 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd @@ -0,0 +1,40 @@ +@tool +extends DialogicCharacterEditorPortraitSection + +## Tab that allows setting size, offset and mirror of a portrait. + + +func _load_portrait_data(data:Dictionary) -> void: + %IgnoreScale.button_pressed = data.get('ignore_char_scale', false) + + %PortraitScale.value = data.get('scale', 1.0)*100 + %PortraitOffset.set_value(data.get('offset', Vector2())) + %PortraitMirror.button_pressed = data.get('mirror', false) + + +func _on_portrait_scale_value_changed(value) -> void: + var data:Dictionary = selected_item.get_metadata(0) + data['scale'] = value/100.0 + update_preview.emit() + changed.emit() + + +func _on_portrait_mirror_toggled(button_pressed:bool)-> void: + var data:Dictionary = selected_item.get_metadata(0) + data['mirror'] = button_pressed + update_preview.emit() + changed.emit() + + +func _on_ignore_scale_toggled(button_pressed:bool) -> void: + var data:Dictionary = selected_item.get_metadata(0) + data['ignore_char_scale'] = button_pressed + update_preview.emit() + changed.emit() + + +func _on_portrait_offset_value_changed(property:String, value:Vector2) -> void: + var data:Dictionary = selected_item.get_metadata(0) + data['offset'] = value + update_preview.emit() + changed.emit() diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn new file mode 100644 index 0000000000000000000000000000000000000000..797de881a2d3b129f073f9f8f4c274a1beb1cf4b --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn @@ -0,0 +1,65 @@ +[gd_scene load_steps=3 format=3 uid="uid://crke8suvv52c6"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd" id="1_76vf2"] +[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/Vector2.tscn" id="2_c8kyi"] + +[node name="Layout" type="HFlowContainer"] +offset_right = 428.0 +offset_bottom = 128.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_76vf2") + +[node name="Label3" type="Label" parent="."] +layout_mode = 2 +text = "Ignore Main Scale: " + +[node name="IgnoreScale" type="CheckBox" parent="."] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "This portrait will ignore the main scale." + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Label" type="Label" parent="HBoxContainer"] +layout_mode = 2 +text = "Scale:" + +[node name="PortraitScale" type="SpinBox" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "A scale to be applied on top of the main scale +(unless ignore main scale is pressed)." +value = 100.0 +allow_greater = true +suffix = "%" + +[node name="HBoxContainer2" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Label2" type="Label" parent="HBoxContainer2"] +layout_mode = 2 +text = "Offset:" + +[node name="PortraitOffset" parent="HBoxContainer2" instance=ExtResource("2_c8kyi")] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Offset that is applied on top of the main portrait offset." + +[node name="MirrorOption" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Label" type="Label" parent="MirrorOption"] +layout_mode = 2 +text = "Mirror:" + +[node name="PortraitMirror" type="CheckBox" parent="MirrorOption"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Mirroring that is applied on top of the main portrait mirror." + +[connection signal="toggled" from="IgnoreScale" to="." method="_on_ignore_scale_toggled"] +[connection signal="value_changed" from="HBoxContainer/PortraitScale" to="." method="_on_portrait_scale_value_changed"] +[connection signal="value_changed" from="HBoxContainer2/PortraitOffset" to="." method="_on_portrait_offset_value_changed"] +[connection signal="toggled" from="MirrorOption/PortraitMirror" to="." method="_on_portrait_mirror_toggled"] diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd new file mode 100644 index 0000000000000000000000000000000000000000..ebc14cd2991572c253c34e7078a31aa569168187 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd @@ -0,0 +1,39 @@ +@tool +extends DialogicCharacterEditorPortraitSection + +## Tab that allows setting an image file on a portrait. + + +func _ready() -> void: + %ImagePicker.file_filter = "*.png, *.svg" + %ImagePicker.resource_icon = get_theme_icon('Image', 'EditorIcons') + + %ScenePicker.file_filter = "*.tscn, *.scn; Scenes" + %ScenePicker.resource_icon = get_theme_icon('PackedScene', 'EditorIcons') + %ScenePicker.placeholder = 'Default scene' + + +func _load_portrait_data(data:Dictionary) -> void: + %ScenePicker.set_value(data.get('scene', '')) + %ImagePicker.set_value(data.get('image', '')) + update_image_picker_visibility(data['scene'].is_empty()) + + +func _on_image_picker_value_changed(prop_name:String, value:String): + var data:Dictionary = selected_item.get_metadata(0) + data['image'] = value + changed.emit() + update_preview.emit() + + +func _on_scene_picker_value_changed(prop_name:String, value:String) -> void: + var data:Dictionary = selected_item.get_metadata(0) + data['scene'] = value + update_image_picker_visibility(data['scene'].is_empty()) + update_preview.emit() + changed.emit() + + +func update_image_picker_visibility(show= true) -> void: + %ImagePicker.visible = show + %ImageLabel.visible = show diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn new file mode 100644 index 0000000000000000000000000000000000000000..1778366a161d212dba8d71d0ffe90d512499177d --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=7 format=3 uid="uid://djq4aasoihexj"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd" id="1_ht8lu"] +[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="2_k8xs0"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rimrq"] +content_margin_left = 4.0 +content_margin_top = 0.0 +content_margin_right = 4.0 +content_margin_bottom = 0.0 +bg_color = Color(0.1, 0.1, 0.1, 0.6) +border_width_bottom = 2 +border_color = Color(0, 0, 0, 0.6) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 + +[sub_resource type="Image" id="Image_4x7i8"] +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_y34ar"] +image = SubResource("Image_4x7i8") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bvmrg"] +content_margin_left = 4.0 +content_margin_top = 0.0 +content_margin_right = 4.0 +content_margin_bottom = 0.0 +bg_color = Color(0.1, 0.1, 0.1, 0.6) +border_width_bottom = 2 +border_color = Color(0, 0, 0, 0.6) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 + +[node name="General" type="GridContainer"] +offset_right = 298.0 +offset_bottom = 86.0 +size_flags_horizontal = 3 +columns = 2 +script = ExtResource("1_ht8lu") + +[node name="Label2" type="Label" parent="."] +layout_mode = 2 +text = "Scene: " + +[node name="ScenePicker" parent="." instance=ExtResource("2_k8xs0")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_rimrq") +file_filter = "*.tscn, *.scn; Scenes" +placeholder = "Default scene" +resource_icon = SubResource("ImageTexture_y34ar") + +[node name="ImageLabel" type="Label" parent="."] +unique_name_in_owner = true +layout_mode = 2 +text = "Image: " + +[node name="ImagePicker" parent="." instance=ExtResource("2_k8xs0")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_bvmrg") +file_filter = "*.png, *.svg" +resource_icon = SubResource("ImageTexture_y34ar") + +[connection signal="value_changed" from="ScenePicker" to="." method="_on_scene_picker_value_changed"] +[connection signal="value_changed" from="ImagePicker" to="." method="_on_image_picker_value_changed"] diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd new file mode 100644 index 0000000000000000000000000000000000000000..a9dc834c4d97d0b6d9c38e558fcd53317cc703c4 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd @@ -0,0 +1,36 @@ +@tool +extends DialogicCharacterEditorMainSection + +## The general character settings tab + + +func _ready() -> void: + # Connecting all necessary signals + %ColorPickerButton.color_changed.connect(character_editor.something_changed) + %DisplayNameLineEdit.text_changed.connect(character_editor.something_changed) + %NicknameLineEdit.text_changed.connect(character_editor.something_changed) + %DescriptionTextEdit.text_changed.connect(character_editor.something_changed) + + +func _load_character(resource:DialogicCharacter) -> void: + %DisplayNameLineEdit.text = resource.display_name + %ColorPickerButton.color = resource.color + + %NicknameLineEdit.text = "" + for nickname in resource.nicknames: + %NicknameLineEdit.text += nickname +", " + %NicknameLineEdit.text = %NicknameLineEdit.text.trim_suffix(', ') + + %DescriptionTextEdit.text = resource.description + + +func _save_changes(resource:DialogicCharacter) -> DialogicCharacter: + resource.display_name = %DisplayNameLineEdit.text + resource.color = %ColorPickerButton.color + var nicknames := [] + for n_name in %NicknameLineEdit.text.split(','): + nicknames.append(n_name.strip_edges()) + resource.nicknames = nicknames + resource.description = %DescriptionTextEdit.text + + return resource diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn new file mode 100644 index 0000000000000000000000000000000000000000..598428682a9b10cb013c991a18a41c3fdc3de88a --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn @@ -0,0 +1,69 @@ +[gd_scene load_steps=2 format=3 uid="uid://bnkck3hocbkk5"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd" id="1_3e1i1"] + +[node name="General" type="GridContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 7.5 +offset_top = 38.5 +offset_right = -7.5 +offset_bottom = -7.5 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/h_separation = 1 +theme_override_constants/v_separation = 6 +columns = 2 +script = ExtResource("1_3e1i1") + +[node name="Label2" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Display Name: " + +[node name="DisplayName" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="DisplayNameLineEdit" type="LineEdit" parent="DisplayName"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "This name will be displayed on the name label. You can use a dialogic variable. E.g. :{Player.name}" +expand_to_text_length = true +caret_blink = true +caret_blink_interval = 0.5 + +[node name="ColorPickerButton" type="ColorPickerButton" parent="DisplayName"] +unique_name_in_owner = true +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 +tooltip_text = "This color can be used on the name label and for occurences of the characters name in text (autocolor names)." +color = Color(1, 1, 1, 1) +edit_alpha = false + +[node name="Label3" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Nicknames:" + +[node name="NicknameLineEdit" type="LineEdit" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "If autocolor names is enabled, these will be colored in the characters color as well." +caret_blink = true +caret_blink_interval = 0.5 + +[node name="Label4" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Description:" + +[node name="DescriptionTextEdit" type="TextEdit" parent="."] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 65) +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "No effect, just for you." +wrap_mode = 1 diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd new file mode 100644 index 0000000000000000000000000000000000000000..0905ed91196eede79d670519b9d4b71d448c22c7 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd @@ -0,0 +1,66 @@ +@tool +extends DialogicCharacterEditorMainSection + +## The general portrait settings section +var loading := false + +func _ready() -> void: + # Connecting all necessary signals + %DefaultPortraitPicker.value_changed.connect(default_portrait_changed) + %MainScale.value_changed.connect(main_portrait_settings_update) + %MainOffset.value_changed.connect(main_portrait_settings_update) + %MainMirror.toggled.connect(main_portrait_settings_update) + + # Setting up Default Portrait Picker + %DefaultPortraitPicker.resource_icon = load("res://addons/dialogic/Editor/Images/Resources/portrait.svg") + %DefaultPortraitPicker.get_suggestions_func = suggest_portraits + + + +# Make sure preview get's updated when portrait settings change +func main_portrait_settings_update(_something=null, _value=null) -> void: + if loading: + return + character_editor.current_resource.scale = %MainScale.value/100.0 + character_editor.current_resource.offset = %MainOffset.current_value + character_editor.current_resource.mirror = %MainMirror.button_pressed + character_editor.update_preview() + character_editor.something_changed() + + +func default_portrait_changed(property:String, value:String) -> void: + character_editor.current_resource.default_portrait = value + character_editor.update_default_portrait_star(value) + + +func _load_character(resource:DialogicCharacter) -> void: + loading = true + %DefaultPortraitPicker.set_value(resource.default_portrait) + + %MainScale.value = 100*resource.scale + %MainOffset.set_value(resource.offset) + %MainMirror.button_pressed = resource.mirror + loading = false + +func _save_changes(resource:DialogicCharacter) -> DialogicCharacter: + # Portrait settings + if %DefaultPortraitPicker.current_value in resource.portraits.keys(): + resource.default_portrait = %DefaultPortraitPicker.current_value + elif !resource.portraits.is_empty(): + resource.default_portrait = resource.portraits.keys()[0] + else: + resource.default_portrait = "" + + resource.scale = %MainScale.value/100.0 + resource.offset = %MainOffset.current_value + resource.mirror = %MainMirror.button_pressed + return resource + + +# Get suggestions for DefaultPortraitPicker +func suggest_portraits(search:String) -> Dictionary: + var suggestions := {} + for portrait in character_editor.get_updated_portrait_dict().keys(): + suggestions[portrait] = {'value':portrait} + return suggestions + diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn new file mode 100644 index 0000000000000000000000000000000000000000..a899bc1900e2225c5808c3fc144152758d932a22 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn @@ -0,0 +1,59 @@ +[gd_scene load_steps=4 format=3] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd" id="1_6sxsl"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="2_birla"] +[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/Vector2.tscn" id="3_vcvin"] + +[node name="Portraits" type="GridContainer"] +offset_right = 453.0 +offset_bottom = 141.0 +theme_override_constants/h_separation = 1 +theme_override_constants/v_separation = 6 +columns = 2 +script = ExtResource("1_6sxsl") + +[node name="Label5" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Default:" + +[node name="DefaultPortraitPicker" parent="." instance=ExtResource("2_birla")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Select Default Portrait" +fit_text_length = false + +[node name="Label" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Main Scale:" + +[node name="MainScale" type="SpinBox" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 8 +value = 100.0 +allow_greater = true +alignment = 1 +suffix = "%" + +[node name="Label2" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Main Offset:" + +[node name="MainOffset" parent="." instance=ExtResource("3_vcvin")] +unique_name_in_owner = true +layout_mode = 2 +alignment = 2 + +[node name="Label3" type="Label" parent="."] +layout_mode = 2 +size_flags_vertical = 0 +text = "Main Mirror:" + +[node name="MainMirror" type="CheckBox" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 8 diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor.gd b/addons/dialogic/Editor/CharacterEditor/character_editor.gd new file mode 100644 index 0000000000000000000000000000000000000000..9c81893e3a23b5daac8e3027f9d8efe746594189 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/character_editor.gd @@ -0,0 +1,560 @@ +@tool +extends DialogicEditor + +## Editor for editing character resources. + +signal character_loaded(resource_path:String) +signal portrait_selected() + + +# Current state +var loading := false +var current_previewed_scene = null + +# References +var selected_item: TreeItem + +var def_portrait_path :String= DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn') + +############################################################################## +## RESOURCE LOGIC +############################################################################## + +# Method is called once editors manager is ready to accept registers. +func _register() -> void: + # Makes the editor open this when a .dch file is selected. + # Then _open_resource() is called. + editors_manager.register_resource_editor("dch", self) + # Add an "add character" button + var add_character_button = editors_manager.add_icon_button( + load("res://addons/dialogic/Editor/Images/Toolbar/add-character.svg"), + 'Add Character', + self) + add_character_button.pressed.connect(_on_create_character_button_pressed) + add_character_button.shortcut = Shortcut.new() + add_character_button.shortcut.events.append(InputEventKey.new()) + add_character_button.shortcut.events[0].keycode = KEY_2 + add_character_button.shortcut.events[0].ctrl_pressed = true + $NoCharacterScreen.show() + + +func _get_title() -> String: + return "Character" + + +func _get_icon() -> Texture: + return load("res://addons/dialogic/Editor/Images/Resources/character.svg") + + +# Called when a character is opened somehow +func _open_resource(resource:Resource) -> void: + # update resource + current_resource = (resource as DialogicCharacter) + + # make sure changes in the ui won't trigger saving + loading = true + + ## Load other main tabs + for child in %MainSettingsSections.get_children(): + if child is DialogicCharacterEditorMainSection: + child._load_character(current_resource) + + + # Portrait section + %PortraitSearch.text = "" + load_portrait_tree() + + loading = false + character_loaded.emit(resource.resource_path) + + for character in editors_manager.resource_helper.character_directory.values(): + if character.resource == resource: + %CharacterName.text = character.unique_short_path + + $NoCharacterScreen.hide() + %PortraitChangeInfo.hide() + + +func _open(extra_info:Variant="") -> void: + %PortraitChangeInfo.hide() + + +func _save() -> void: + if ! visible or not current_resource: + return + + # Portrait list + current_resource.portraits = get_updated_portrait_dict() + + # Main tabs + for child in %MainSettingsSections.get_children(): + if child is DialogicCharacterEditorMainSection: + current_resource = child._save_changes(current_resource) + + ResourceSaver.save(current_resource, current_resource.resource_path) + current_resource_state = ResourceStates.SAVED + editors_manager.resource_helper.rebuild_character_directory() + + +# Saves a new empty character to the given path +func new_character(path: String) -> void: + var resource := DialogicCharacter.new() + resource.resource_path = path + resource.display_name = path.get_file().trim_suffix("."+path.get_extension()) + resource.color = Color(1,1,1,1) + resource.default_portrait = "" + resource.custom_info = {} + ResourceSaver.save(resource, path) + editors_manager.resource_helper.rebuild_character_directory() + editors_manager.edit_resource(resource) + + +############################################################################## +## INTERFACE +############################################################################## + +func _ready() -> void: + + $NoCharacterScreen.color = get_theme_color("dark_color_2", "Editor") + $NoCharacterScreen.show() + setup_portrait_list_tab() + + _on_fit_preview_toggle_toggled(DialogicUtil.get_editor_setting('character_preview_fit', true)) + %PreviewLabel.add_theme_color_override("font_color", get_theme_color("readonly_color", "Editor")) + + %PortraitChangeWarning.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")) + + %RealPreviewPivot.texture = get_theme_icon("EditorPivot", "EditorIcons") + + %MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons") + + await find_parent('EditorView').ready + + # Add general tabs + add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn").instantiate(), %MainSettingsSections) + add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn").instantiate(), %MainSettingsSections) + + + add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn").instantiate(), %PortraitSettingsSection) + add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn").instantiate(), %PortraitSettingsSection) + add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn").instantiate(), %PortraitSettingsSection) + + # Load custom sections from modules + for indexer in DialogicUtil.get_indexers(): + for path in indexer._get_character_editor_sections(): + var scene :Control = load(path).instantiate() + if scene is DialogicCharacterEditorMainSection: + add_settings_section(scene, %MainSettingsSections) + elif scene is DialogicCharacterEditorPortraitSection: + add_settings_section(scene, %PortraitSettingsSection) + + +func add_settings_section(edit:Control, parent:Node) -> void: + edit.changed.connect(something_changed) + edit.character_editor = self + if edit.has_signal('update_preview'): + edit.update_preview.connect(update_preview) + + var button := Button.new() + button.flat = true + button.theme_type_variation = "DialogicSection" + button.alignment = HORIZONTAL_ALIGNMENT_LEFT + button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + button.text = edit.name + button.icon_alignment = HORIZONTAL_ALIGNMENT_RIGHT + button.pressed.connect(_on_section_button_pressed.bind(button)) + button.focus_mode = Control.FOCUS_NONE + button.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") + button.add_theme_color_override('icon_normal_color', get_theme_color("font_color", "DialogicSection")) + parent.add_child(button) + parent.add_child(edit) + parent.add_child(HSeparator.new()) + if !edit.name == "General": + _on_section_button_pressed(button) + + +func get_settings_section_by_name(name:String, main:=true) -> Node: + if main: + return %MainSettingsSections.get_node(name) + else: + return %PortraitSettingsSection.get_node(name) + + +func _on_section_button_pressed(button:Button) -> void: + if button.get_parent().get_child(button.get_index()+1).visible: + button.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") + button.get_parent().get_child(button.get_index()+1).visible = false + else: + button.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") + button.get_parent().get_child(button.get_index()+1).visible = true + + if button.get_parent().get_child_count() > button.get_index()+2 and button.get_parent().get_child(button.get_index()+2) is Separator: + button.get_parent().get_child(button.get_index()+2).visible = button.get_parent().get_child(button.get_index()+1).visible + + +func something_changed(fake_argument = "", fake_arg2 = null) -> void: + if not loading: + current_resource_state = ResourceStates.UNSAVED + + + +############################################################################## +## PORTRAIT SECTION +############################################################################## + +func setup_portrait_list_tab() -> void: + %PortraitTree.editor = self + + ## Portrait section styling/connections + %AddPortraitButton.icon = get_theme_icon("Add", "EditorIcons") + %AddPortraitButton.pressed.connect(add_portrait) + %AddPortraitGroupButton.icon = load("res://addons/dialogic/Editor/Images/Pieces/add-folder.svg") + %AddPortraitGroupButton.pressed.connect(add_portrait_group) + %ImportPortraitsButton.icon = get_theme_icon("Load", "EditorIcons") + %ImportPortraitsButton.pressed.connect(open_portrait_folder_select) + %PortraitSearch.right_icon = get_theme_icon("Search", "EditorIcons") + %PortraitSearch.text_changed.connect(filter_portrait_list) + + %PortraitTree.item_selected.connect(load_selected_portrait) + %PortraitTree.item_edited.connect(_on_item_edited) + %PortraitTree.item_activated.connect(_on_item_activated) + + +func open_portrait_folder_select() -> void: + find_parent("EditorView").godot_file_dialog( + import_portraits_from_folder, "*", + EditorFileDialog.FILE_MODE_OPEN_DIR) + + +func import_portraits_from_folder(path:String) -> void: + var parent: TreeItem = %PortraitTree.get_root() + if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'): + parent = %PortraitTree.get_selected() + + var dir := DirAccess.open(path) + dir.list_dir_begin() + var file_name :String = dir.get_next() + while file_name != "": + if not dir.current_is_dir(): + var file_lower = file_name.to_lower() + if '.svg' in file_lower or '.png' in file_lower: + if not '.import' in file_lower: + var final_name: String= path+ "/" + file_name + %PortraitTree.add_portrait_item(file_name.trim_suffix('.'+file_name.get_extension()), + {'scene':"",'image':final_name, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent) + file_name = dir.get_next() + something_changed() + + +func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary={'scene':"", 'image':'', 'scale':1, 'offset':Vector2(), 'mirror':false}) -> void: + var parent: TreeItem = %PortraitTree.get_root() + if %PortraitTree.get_selected(): + if %PortraitTree.get_selected().get_metadata(0).has('group'): + parent = %PortraitTree.get_selected() + else: + parent = %PortraitTree.get_selected().get_parent() + var item :TreeItem = %PortraitTree.add_portrait_item(portrait_name, portrait_data, parent) + item.set_meta('new', true) + item.set_editable(0, true) + item.select(0) + %PortraitTree.call_deferred('edit_selected') + something_changed() + + +func add_portrait_group() -> void: + var parent_item :TreeItem = %PortraitTree.get_root() + if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'): + parent_item = %PortraitTree.get_selected() + var item :TreeItem = %PortraitTree.add_portrait_group("Group", parent_item) + item.set_meta('new', true) + item.set_editable(0, true) + item.select(0) + %PortraitTree.call_deferred('edit_selected') + + +func load_portrait_tree() -> void: + %PortraitTree.clear_tree() + var root:TreeItem = %PortraitTree.create_item() + + for portrait in current_resource.portraits.keys(): + var portrait_label = portrait + var parent = %PortraitTree.get_root() + if '/' in portrait: + parent = %PortraitTree.create_necessary_group_items(portrait) + portrait_label = portrait.split('/')[-1] + + %PortraitTree.add_portrait_item(portrait_label, current_resource.portraits[portrait], parent) + + update_default_portrait_star(current_resource.default_portrait) + + if root.get_child_count(): + root.get_first_child().select(0) + while %PortraitTree.get_selected().get_child_count(): + %PortraitTree.get_selected().get_child(0).select(0) + else: + # Call anyways to clear preview and hide portrait settings section + load_selected_portrait() + + +func filter_portrait_list(filter_term:String = '') -> void: + filter_branch(%PortraitTree.get_root(), filter_term) + + +func filter_branch(parent:TreeItem, filter_term:String) -> bool: + var anything_visible := false + for item in parent.get_children(): + if item.get_metadata(0).has('group'): + item.visible = filter_branch(item, filter_term) + anything_visible = item.visible + elif filter_term.is_empty() or filter_term.to_lower() in item.get_text(0).to_lower(): + item.visible = true + anything_visible = true + else: + item.visible = false + return anything_visible + + +# this is used to save the portrait data +func get_updated_portrait_dict() -> Dictionary: + return list_portraits(%PortraitTree.get_root().get_children()) + + +func list_portraits(tree_items:Array[TreeItem], dict:Dictionary = {}, path_prefix = "") -> Dictionary: + for item in tree_items: + if item.get_metadata(0).has('group'): + dict = list_portraits(item.get_children(), dict, path_prefix+item.get_text(0)+"/") + else: + dict[path_prefix +item.get_text(0)] = item.get_metadata(0) + return dict + + +func load_selected_portrait(): + if selected_item and is_instance_valid(selected_item): + selected_item.set_editable(0, false) + + selected_item = %PortraitTree.get_selected() + + + if selected_item and selected_item.get_metadata(0) != null and !selected_item.get_metadata(0).has('group'): + %PortraitSettingsSection.show() + var current_portrait_data :Dictionary = selected_item.get_metadata(0) + portrait_selected.emit(%PortraitTree.get_full_item_name(selected_item), current_portrait_data) + + update_preview() + + for child in %PortraitSettingsSection.get_children(): + if child is DialogicCharacterEditorPortraitSection: + child.selected_item = selected_item + child._load_portrait_data(current_portrait_data) + + else: + %PortraitSettingsSection.hide() + update_preview() + +# if selected_item: +# await get_tree().create_timer(0.01).timeout +# selected_item.set_editable(0, true) + + +func delete_portrait_item(item:TreeItem) -> void: + if item.get_next_visible(true): + item.get_next_visible(true).select(0) + item.free() + something_changed() + + +func duplicate_item(item:TreeItem) -> void: + %PortraitTree.add_portrait_item(item.get_text(0)+'_duplicated', item.get_metadata(0).duplicate(true), item.get_parent()).select(0) + + +func _input(event:InputEvent) -> void: + if !is_visible_in_tree() or (get_viewport().gui_get_focus_owner()!= null and !name+'/' in str(get_viewport().gui_get_focus_owner().get_path())): + return + if event is InputEventKey and event.pressed: + if event.keycode == KEY_F2 and %PortraitTree.get_selected(): + %PortraitTree.get_selected().set_editable(0, true) + %PortraitTree.edit_selected() + get_viewport().set_input_as_handled() + elif event.keycode == KEY_DELETE and get_viewport().gui_get_focus_owner() is Tree and %PortraitTree.get_selected(): + delete_portrait_item(%PortraitTree.get_selected()) + get_viewport().set_input_as_handled() + +func _on_portrait_right_click_menu_index_pressed(id:int) -> void: + # RENAME BUTTON + if id == 0: + _on_item_activated() + # DELETE BUTTON + if id == 2: + delete_portrait_item(%PortraitTree.get_selected()) + # DUPLICATE ITEM + elif id == 1: + duplicate_item(%PortraitTree.get_selected()) + + +# this removes/and adds the DEFAULT star on the portrait list +func update_default_portrait_star(default_portrait_name:String) -> void: + var item_list : Array = %PortraitTree.get_root().get_children() + if item_list.is_empty() == false: + while true: + var item = item_list.pop_back() + if item.get_button_by_id(0, 2) != -1: + item.erase_button(0, item.get_button_by_id(0, 2)) + if %PortraitTree.get_full_item_name(item) == default_portrait_name: + item.add_button(0, get_theme_icon('Favorites', 'EditorIcons'), 2, true, 'Default') + item_list.append_array(item.get_children()) + if item_list.is_empty(): + break + + +func _on_item_edited(): + selected_item = %PortraitTree.get_selected() + something_changed() + if selected_item: + if %PreviewLabel.text.trim_prefix('Preview of "').trim_suffix('"') == current_resource.default_portrait: + current_resource.default_portrait = %PortraitTree.get_full_item_name(selected_item) + selected_item.set_editable(0, false) + + if !selected_item.has_meta('new') and %PortraitTree.get_full_item_name(selected_item) != selected_item.get_meta('previous_name'): + report_name_change(selected_item) + %PortraitChangeInfo.show() + update_preview() + + +func _on_item_activated(): + if %PortraitTree.get_selected() == null: + return + %PortraitTree.get_selected().set_editable(0, true) + %PortraitTree.edit_selected() + + +func report_name_change(item:TreeItem) -> void: + if item.get_metadata(0).has('group'): + for s_item in item.get_children(): + if s_item.get_metadata(0).has('group') or !s_item.has_meta('new'): + report_name_change(s_item) + else: + if item.get_meta('previous_name') == %PortraitTree.get_full_item_name(item): + return + editors_manager.reference_manager.add_portrait_ref_change( + item.get_meta('previous_name'), + %PortraitTree.get_full_item_name(item), + [editors_manager.resource_helper.get_character_short_path(current_resource)]) + item.set_meta('previous_name', %PortraitTree.get_full_item_name(item)) + %PortraitChangeInfo.show() + + +############################################################################## +## PREVIEW +############################################################################## + +func update_preview() -> void: + %ScenePreviewWarning.hide() + if selected_item and is_instance_valid(selected_item) and selected_item.get_metadata(0) != null and !selected_item.get_metadata(0).has('group'): + %PreviewLabel.text = 'Preview of "'+%PortraitTree.get_full_item_name(selected_item)+'"' + + var current_portrait_data: Dictionary = selected_item.get_metadata(0) + var mirror:bool = current_portrait_data.get('mirror', false) != current_resource.mirror + var scale:float = current_portrait_data.get('scale', 1) * current_resource.scale + if current_portrait_data.get('ignore_char_scale', false): + scale = current_portrait_data.get('scale', 1) + var offset:Vector2 =current_portrait_data.get('offset', Vector2()) + current_resource.offset + + if current_previewed_scene != null \ + and current_previewed_scene.get_meta('path', '') == current_portrait_data.get('scene') \ + and current_previewed_scene.has_method('_should_do_portrait_update') \ + and is_instance_valid(current_previewed_scene.get_script()) \ + and current_previewed_scene._should_do_portrait_update(current_resource, selected_item.get_text(0)): + pass # we keep the same scene + else: + for node in %RealPreviewPivot.get_children(): + node.queue_free() + current_previewed_scene = null + if current_portrait_data.get('scene', '').is_empty(): + if FileAccess.file_exists(def_portrait_path): + current_previewed_scene = load(def_portrait_path).instantiate() + current_previewed_scene.set_meta('path', '') + else: + if FileAccess.file_exists(current_portrait_data.get('scene')): + current_previewed_scene = load(current_portrait_data.get('scene')).instantiate() + current_previewed_scene.set_meta('path', current_portrait_data.get('scene')) + if current_previewed_scene: + %RealPreviewPivot.add_child(current_previewed_scene) + + if current_previewed_scene != null: + var scene = current_previewed_scene + scene.show_behind_parent = true + + for prop in current_portrait_data.get('export_overrides', {}).keys(): + scene.set(prop, str_to_var(current_portrait_data['export_overrides'][prop])) + + if is_instance_valid(scene.get_script()) and scene.script.is_tool(): + if scene.has_method('_update_portrait'): + scene._update_portrait(current_resource, %PortraitTree.get_full_item_name(selected_item)) + if scene.has_method('_set_mirror'): + scene._set_mirror(mirror) + if !%FitPreview_Toggle.button_pressed: + scene.position = Vector2() + offset + scene.scale = Vector2(1,1)*scale + else: + if is_instance_valid(scene.get_script()) and scene.script.is_tool() and scene.has_method('_get_covered_rect'): + var rect :Rect2= scene._get_covered_rect() + var available_rect:Rect2 = %FullPreviewAvailableRect.get_rect() + scene.scale = Vector2(1,1) * min(available_rect.size.x/rect.size.x, available_rect.size.y/rect.size.y) + %RealPreviewPivot.position = (rect.position)*-1*scene.scale + %RealPreviewPivot.position.x = %FullPreviewAvailableRect.size.x/2 + scene.position = Vector2() + else: + %ScenePreviewWarning.show() + else: + %PreviewLabel.text = 'Nothing to preview' + for child in %PortraitSettingsSection.get_children(): + if child is DialogicCharacterEditorPortraitSection: + child._recheck(current_portrait_data) + else: + %PreviewLabel.text = 'No portrait to preview.' + for node in %RealPreviewPivot.get_children(): + node.queue_free() + current_previewed_scene = null + + + +func _on_full_preview_available_rect_resized(): + if %FitPreview_Toggle.button_pressed: + update_preview() + + +func _on_create_character_button_pressed(): + editors_manager.show_add_resource_dialog( + new_character, + '*.dch; DialogicCharacter', + 'Create new character', + 'character', + ) + + +func _on_fit_preview_toggle_toggled(button_pressed): + %FitPreview_Toggle.set_pressed_no_signal(button_pressed) + if button_pressed: + %FitPreview_Toggle.icon = get_theme_icon("ScrollContainer", "EditorIcons") + %FitPreview_Toggle.tooltip_text = "Real scale" + else: + %FitPreview_Toggle.tooltip_text = "Fit into preview" + %FitPreview_Toggle.icon = get_theme_icon("CenterContainer", "EditorIcons") + DialogicUtil.set_editor_setting('character_preview_fit', button_pressed) + update_preview() + + +func _on_reference_manger_button_pressed(): + editors_manager.reference_manager.open() + + +func _on_main_settings_collapse_toggled(button_pressed): + %MainSettingsTitle.visible = !button_pressed + %MainSettingsScroll.visible = !button_pressed +# %MainHSplit.collapsed = button_pressed + if button_pressed: + %MainSettings.hide() + %MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityHidden", "EditorIcons") + else: + %MainSettings.show() + %MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons") diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor.tscn b/addons/dialogic/Editor/CharacterEditor/character_editor.tscn new file mode 100644 index 0000000000000000000000000000000000000000..4663c2e2cda11e751106886c9fbb3b9d3526ec81 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/character_editor.tscn @@ -0,0 +1,416 @@ +[gd_scene load_steps=9 format=3 uid="uid://dlskc36c5hrwv"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor.gd" id="2"] +[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_uhhqs"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd" id="2_vad0i"] +[ext_resource type="Texture2D" uid="uid://babwe22dqjta" path="res://addons/dialogic/Editor/Images/Pieces/add-folder.svg" id="3_v1qnr"] + +[sub_resource type="Image" id="Image_0tb86"] +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_6kogb"] +image = SubResource("Image_0tb86") + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_es2rd"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4xgdx"] + +[node name="CharacterEditor" type="Control"] +self_modulate = Color(0, 0, 0, 1) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.3 +theme_override_constants/separation = 0 + +[node name="TopSection" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="NameContainer" type="HBoxContainer" parent="VBoxContainer/TopSection"] +layout_mode = 2 + +[node name="CharacterName" type="Label" parent="VBoxContainer/TopSection/NameContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicTitle" +text = "My Character" + +[node name="NameTooltip" parent="VBoxContainer/TopSection/NameContainer" instance=ExtResource("2_uhhqs")] +layout_mode = 2 +tooltip_text = "This name is determined from the file name. Use this name in timelines to reference this character." +texture = SubResource("ImageTexture_6kogb") +hint_text = "This name is determined from the file name. Use this name in timelines to reference this character." + +[node name="MainSettingsCollapse" type="Button" parent="VBoxContainer/TopSection"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 10 +size_flags_vertical = 4 +toggle_mode = true +text = "Main Settings" +icon = SubResource("ImageTexture_6kogb") + +[node name="MainHSplit" type="HSplitContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="MainSettings" type="VBoxContainer" parent="VBoxContainer/MainHSplit"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.2 + +[node name="MainSettingsTitle" type="Label" parent="VBoxContainer/MainHSplit/MainSettings"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"DialogicSubTitle" +text = "Main Settings" + +[node name="MainSettingsScroll" type="ScrollContainer" parent="VBoxContainer/MainHSplit/MainSettings"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxEmpty_es2rd") +horizontal_scroll_mode = 0 + +[node name="MainSettingsSections" type="VBoxContainer" parent="VBoxContainer/MainHSplit/MainSettings/MainSettingsScroll"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Split" type="HSplitContainer" parent="VBoxContainer/MainHSplit"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 0 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.2 +theme_override_constants/separation = 0 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.2 +theme_override_constants/margin_bottom = 10 + +[node name="PortraitListSection" type="PanelContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"DialogicPanelA" + +[node name="Portraits" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection"] +layout_mode = 2 + +[node name="PortraitsTitle" type="Label" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +layout_mode = 2 +theme_type_variation = &"DialogicSubTitle" +text = "Portraits" + +[node name="PortraitListTools" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +layout_mode = 2 + +[node name="AddPortraitButton" type="Button" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Add portrait" +icon = SubResource("ImageTexture_6kogb") + +[node name="AddPortraitGroupButton" type="Button" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Add Group" +icon = ExtResource("3_v1qnr") + +[node name="ImportPortraitsButton" type="Button" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Import images from folder" +icon = SubResource("ImageTexture_6kogb") + +[node name="PortraitSearch" type="LineEdit" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +placeholder_text = "Search" +expand_to_text_length = true +clear_button_enabled = true +right_icon = SubResource("ImageTexture_6kogb") +caret_blink = true +caret_blink_interval = 0.5 + +[node name="PortraitTreePanel" type="PanelContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxEmpty_4xgdx") + +[node name="PortraitTree" type="Tree" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel"] +unique_name_in_owner = true +layout_mode = 2 +allow_rmb_select = true +hide_root = true +drop_mode_flags = 3 +script = ExtResource("2_vad0i") + +[node name="PortraitRightClickMenu" type="PopupMenu" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree"] +size = Vector2i(118, 100) +item_count = 3 +item_0/text = "Rename" +item_0/icon = SubResource("ImageTexture_6kogb") +item_0/id = 2 +item_1/text = "Duplicate" +item_1/icon = SubResource("ImageTexture_6kogb") +item_1/id = 0 +item_2/text = "Delete" +item_2/icon = SubResource("ImageTexture_6kogb") +item_2/id = 1 + +[node name="PortraitChangeInfo" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="PortraitChangeWarning" type="Label" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitChangeInfo"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "Some portraits were renamed. Make sure no references broke!" +autowrap_mode = 3 + +[node name="ReferenceMangerButton" type="Button" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitChangeInfo"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +text = "Reference +Manager" + +[node name="RightSection" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.5 + +[node name="Spacer" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection"] +custom_minimum_size = Vector2(0, 30) +layout_mode = 2 + +[node name="RightSection" type="VSplitContainer" parent="VBoxContainer/MainHSplit/Split/RightSection"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.5 + +[node name="PortraitPreviewSection" type="Panel" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection"] +unique_name_in_owner = true +show_behind_parent = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_type_variation = &"DialogicPanelB" + +[node name="ClipRect" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection"] +clip_contents = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Node2D" type="Node2D" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection/ClipRect"] +position = Vector2(13, 17) + +[node name="RealPreviewPivot" type="Sprite2D" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection/ClipRect/Node2D"] +unique_name_in_owner = true +position = Vector2(330, 405) +texture = SubResource("ImageTexture_6kogb") + +[node name="ScenePreviewWarning" type="Label" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -143.0 +offset_top = -44.5 +offset_right = 143.0 +offset_bottom = 85.5 +grow_horizontal = 2 +grow_vertical = 2 +text = "Custom scenes can only be viewed in \"Full mode\" if they are in @tool mode and override _get_covered_rect" +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 3 +metadata/_edit_layout_mode = 1 + +[node name="PreviewReal" type="CenterContainer" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -302.0 +offset_top = -80.0 +offset_right = 302.0 +grow_horizontal = 2 +grow_vertical = 0 +metadata/_edit_layout_mode = 1 + +[node name="Control" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection/PreviewReal"] +layout_mode = 2 + +[node name="RealSizeRemotePivotTransform" type="RemoteTransform2D" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection/PreviewReal/Control"] +unique_name_in_owner = true +remote_path = NodePath("../../../ClipRect/Node2D/RealPreviewPivot") +update_position = false +update_rotation = false +update_scale = false + +[node name="FullPreviewAvailableRect" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 10.0 +offset_top = 13.0 +offset_right = -10.0 +offset_bottom = -16.0 +grow_horizontal = 2 +grow_vertical = 2 +metadata/_edit_layout_mode = 1 + +[node name="PreviewLabel" type="Label" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection"] +unique_name_in_owner = true +show_behind_parent = true +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_left = -1.0 +offset_top = -23.0 +offset_right = -1.0 +offset_bottom = 3.0 +grow_horizontal = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "No portrait to preview." +horizontal_alignment = 2 +clip_text = true +text_overrun_behavior = 1 + +[node name="FitPreview_Toggle" type="Button" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -38.0 +offset_top = 8.0 +offset_right = -7.0 +offset_bottom = 39.0 +grow_horizontal = 0 +tooltip_text = "Real scale" +focus_mode = 0 +toggle_mode = true +button_pressed = true +icon = SubResource("ImageTexture_6kogb") +flat = true +metadata/_edit_layout_mode = 1 + +[node name="VBox" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.4 + +[node name="Hbox" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/VBox"] +layout_mode = 2 + +[node name="PortraitSettingsTitle" type="Label" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/VBox/Hbox"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicSubTitle" +text = "Portrait Settings" + +[node name="Scroll" type="ScrollContainer" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/VBox"] +layout_mode = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.4 + +[node name="PortraitSettingsSection" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection/RightSection/VBox/Scroll"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.3 + +[node name="NoCharacterScreen" type="ColorRect" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +color = Color(0, 0, 0, 1) + +[node name="CenterContainer" type="CenterContainer" parent="NoCharacterScreen"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="NoCharacterScreen/CenterContainer"] +custom_minimum_size = Vector2(250, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="NoCharacterScreen/CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "No character opened. +Create a character or double-click one in the file system dock." +horizontal_alignment = 1 +autowrap_mode = 3 + +[node name="CreateCharacterButton" type="Button" parent="NoCharacterScreen/CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "Create New Character" + +[connection signal="toggled" from="VBoxContainer/TopSection/MainSettingsCollapse" to="." method="_on_main_settings_collapse_toggled"] +[connection signal="item_mouse_selected" from="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree" to="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree" method="_on_item_mouse_selected"] +[connection signal="index_pressed" from="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree/PortraitRightClickMenu" to="." method="_on_portrait_right_click_menu_index_pressed"] +[connection signal="pressed" from="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitChangeInfo/ReferenceMangerButton" to="." method="_on_reference_manger_button_pressed"] +[connection signal="resized" from="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection/FullPreviewAvailableRect" to="." method="_on_full_preview_available_rect_resized"] +[connection signal="toggled" from="VBoxContainer/MainHSplit/Split/RightSection/RightSection/PortraitPreviewSection/FitPreview_Toggle" to="." method="_on_fit_preview_toggle_toggled"] +[connection signal="pressed" from="NoCharacterScreen/CenterContainer/VBoxContainer/CreateCharacterButton" to="." method="_on_create_character_button_pressed"] diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor_main_settings_section.gd b/addons/dialogic/Editor/CharacterEditor/character_editor_main_settings_section.gd new file mode 100644 index 0000000000000000000000000000000000000000..238eedb9a15008ac51acd2449233d0b8bf041fc3 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/character_editor_main_settings_section.gd @@ -0,0 +1,20 @@ +@tool +class_name DialogicCharacterEditorMainSection +extends Control + +## Base class for all character editor main tabs. Methods should be overriden. + + +# Emit this, if something changed +signal changed + + +var character_editor:Control + + +func _load_character(resource:DialogicCharacter) -> void: + pass + + +func _save_changes(resource:DialogicCharacter) -> DialogicCharacter: + return resource diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd new file mode 100644 index 0000000000000000000000000000000000000000..1e485bb805339c411ce02be04607ac71c671be0e --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd @@ -0,0 +1,19 @@ +@tool +class_name DialogicCharacterEditorPortraitSection +extends Control + +## Base class for all portrait settings tabs. Methods should be overriden. + +# Emit this, if something changed +signal changed +signal update_preview + +var character_editor:Control + +var selected_item :TreeItem = null + +func _load_portrait_data(data:Dictionary) -> void: + pass + +func _recheck(data:Dictionary) -> void: + pass diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd new file mode 100644 index 0000000000000000000000000000000000000000..d9469d13753b82e8fc47a141be50e0e60c965ad2 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd @@ -0,0 +1,138 @@ +@tool +extends Tree + +## Tree that displays the portrait list as a hirarchy + +var editor = find_parent('Character Editor') +var current_group_nodes := {} + + +func _ready() -> void: + $PortraitRightClickMenu.set_item_icon(0, get_theme_icon('Rename', 'EditorIcons')) + $PortraitRightClickMenu.set_item_icon(1, get_theme_icon('Duplicate', 'EditorIcons')) + $PortraitRightClickMenu.set_item_icon(2, get_theme_icon('Remove', 'EditorIcons')) + + +func clear_tree() -> void: + clear() + current_group_nodes = {} + + +func add_portrait_item(portrait_name:String, portrait_data:Dictionary, parent_item:TreeItem, previous_name:String = "") -> TreeItem: + var item :TreeItem = %PortraitTree.create_item(parent_item) + item.set_text(0, portrait_name) + item.set_metadata(0, portrait_data) + if previous_name.is_empty(): + item.set_meta('previous_name', get_full_item_name(item)) + else: + item.set_meta('previous_name', previous_name) + if portrait_name == editor.current_resource.default_portrait: + item.add_button(0, get_theme_icon('Favorites', 'EditorIcons'), 2, true, 'Default') + return item + + +func add_portrait_group(goup_name:String = "Group", parent_item:TreeItem = get_root(), previous_name:String = "") -> TreeItem: + var item :TreeItem = %PortraitTree.create_item(parent_item) + item.set_icon(0, get_theme_icon("Folder", "EditorIcons")) + item.set_text(0, goup_name) + item.set_metadata(0, {'group':true}) + if previous_name.is_empty(): + item.set_meta('previous_name', get_full_item_name(item)) + else: + item.set_meta('previous_name', previous_name) + return item + + +func get_full_item_name(item:TreeItem) -> String: + var item_name := item.get_text(0) + while item.get_parent() != get_root() and item != get_root(): + item_name = item.get_parent().get_text(0)+"/"+item_name + item = item.get_parent() + return item_name + + +# Will create all not yet existing folders in the given path. +# Returns the last folder (the parent of the portrait item of this path). +func create_necessary_group_items(path:String) -> TreeItem: + var last_item := get_root() + var item_path := "" + + for i in Array(path.split('/')).slice(0, -1): + item_path += "/"+i + item_path = item_path.trim_prefix('/') + if current_group_nodes.has(item_path+"/"+i): + last_item = current_group_nodes[item_path+"/"+i] + else: + var new_item:TreeItem = add_portrait_group(i, last_item) + current_group_nodes[item_path+"/"+i] = new_item + last_item = new_item + return last_item + + +func _on_item_mouse_selected(pos:Vector2, mouse_button_index:int) -> void: + if mouse_button_index == MOUSE_BUTTON_RIGHT: + $PortraitRightClickMenu.set_item_disabled(1, get_selected().get_metadata(0).has('group')) + $PortraitRightClickMenu.popup_on_parent(Rect2(get_global_mouse_position(),Vector2())) + + +################################################################################ +## DRAG AND DROP +################################################################################ + +func _get_drag_data(position:Vector2) -> Variant: + drop_mode_flags = DROP_MODE_INBETWEEN + var preview := Label.new() + preview.text = " "+get_selected().get_text(0) + preview.add_theme_stylebox_override('normal', get_theme_stylebox("Background", "EditorStyles")) + set_drag_preview(preview) + + return get_selected() + + +func _can_drop_data(position:Vector2, data:Variant) -> bool: + return data is TreeItem + + +func _drop_data(position:Vector2, item:Variant) -> void: + var to_item := get_item_at_position(position) + if to_item: + var test_item:= to_item + while true: + if test_item == item: + return + test_item = test_item.get_parent() + if test_item == get_root(): + break + + var drop_section := get_drop_section_at_position(position) + var parent := get_root() + if to_item: + parent = to_item.get_parent() + + if to_item and to_item.get_metadata(0).has('group') and drop_section == 1: + parent = to_item + + var new_item := copy_branch_or_item(item, parent) + + if to_item and !to_item.get_metadata(0).has('group') and drop_section == 1: + new_item.move_after(to_item) + + if drop_section == -1: + new_item.move_before(to_item) + + editor.report_name_change(new_item) + + item.free() + + +func copy_branch_or_item(item:TreeItem, new_parent:TreeItem) -> TreeItem: + var new_item :TreeItem = null + if item.get_metadata(0).has('group'): + new_item = add_portrait_group(item.get_text(0), new_parent, item.get_meta('previous_name')) + else: + new_item = add_portrait_item(item.get_text(0), item.get_metadata(0), new_parent, item.get_meta('previous_name')) + + for child in item.get_children(): + copy_branch_or_item(child, new_item) + return new_item + diff --git a/addons/dialogic/Editor/Common/DCSS.gd b/addons/dialogic/Editor/Common/DCSS.gd new file mode 100644 index 0000000000000000000000000000000000000000..93e60f3c9ca1a6ef162d312389ccad13b638ffa1 --- /dev/null +++ b/addons/dialogic/Editor/Common/DCSS.gd @@ -0,0 +1,60 @@ +@tool +class_name DCSS + +static func get_editor_scale() -> float: + return DialogicUtil.get_editor_scale() + +static func inline(style:Dictionary) -> StyleBoxFlat: + var scale:float = get_editor_scale() + var s := StyleBoxFlat.new() + for property in style.keys(): + match property: + 'border-left': + s.set('border_width_left', style[property] * scale) + 'border-radius': + var radius:float = style[property] * scale + s.set('corner_radius_top_left', radius) + s.set('corner_radius_top_right', radius) + s.set('corner_radius_bottom_left', radius) + s.set('corner_radius_bottom_right', radius) + 'background': + s.set('bg_color', style[property]) + 'border': + var width:float = style[property] * scale + s.set('border_width_left', width) + s.set('border_width_right', width) + s.set('border_width_top', width) + s.set('border_width_bottom', width) + 'border-color': + s.set('border_color', style[property]) + 'padding': + var value_v: float = 0.0 + var value_h: float = 0.0 + if style[property] is int: + value_v = style[property] * scale + value_h = value_v + else: + value_v = style[property][0] * scale + value_h = style[property][1] * scale + s.set('content_margin_top', value_v) + s.set('content_margin_bottom', value_v) + s.set('content_margin_left', value_h) + s.set('content_margin_right', value_h) + 'padding-right': + s.set('content_margin_right', style[property] * scale) + 'padding-left': + s.set('content_margin_left', style[property] * scale) + return s + +static func style(node, style:Dictionary) -> StyleBoxFlat: + var scale:float = get_editor_scale() + var s:StyleBoxFlat = inline(style) + + node.set('theme_override_styles/normal', s) + node.set('theme_override_styles/focus', s) + node.set('theme_override_styles/read_only', s) + node.set('theme_override_styles/hover', s) + node.set('theme_override_styles/pressed', s) + node.set('theme_override_styles/disabled', s) + node.set('theme_override_styles/panel', s) + return s diff --git a/addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd b/addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd new file mode 100644 index 0000000000000000000000000000000000000000..f01da512f50108d91cd84c807d10eb99262e570a --- /dev/null +++ b/addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd @@ -0,0 +1,137 @@ +@tool +extends PanelContainer + + +enum Modes {EDIT, ADD} + +var mode := Modes.EDIT +var item :TreeItem = null + + +func _ready() -> void: + hide() + get_parent().icon = get_theme_icon("Add", "EditorIcons") + get_parent().pressed.connect(_on_add_pressed) + var stl := get_theme_stylebox("PanelForeground", "EditorStyles").duplicate() + stl.set_content_margin_all(5) + stl.set_border_width_all(1) + stl.set_border_color(get_theme_color("accent_color", "Editor")) + add_theme_stylebox_override('panel',stl) + + %Character.resource_icon = load("res://addons/dialogic/Editor/Images/Resources/character.svg") + %Character.get_suggestions_func = get_character_suggestions + + +func _on_add_pressed() -> void: + if visible: + if mode == Modes.ADD: + hide() + return + elif mode == Modes.EDIT: + save() + + %AddButton.text = "Add" + mode = Modes.ADD + show() + %Type.selected = 0 + _on_type_item_selected(0) + %Where.selected = 2 + _on_where_item_selected(2) + %Old.text = "" + %New.text = "" + + _on_resized() + + +func open_existing(_item:TreeItem, info:Dictionary): + mode = Modes.EDIT + item = _item + show() + %AddButton.text = "Update" + %Type.selected = info.type + _on_type_item_selected(info.type) + if !info.character_names.is_empty(): + %Where.selected = 1 + %Character.set_value(info.character_names[0]) + else: + %Where.selected = 0 + _on_where_item_selected(%Where.selected) + + %Old.text = info.what + %New.text = info.forwhat + + _on_resized() + + +func _on_resized() -> void: + if !visible: + return + size = Vector2() + position = get_parent().get_global_transform().get_origin()-Vector2(1,0)*size.x+Vector2(0,1) *get_parent().size.y + + +func _on_type_item_selected(index:int) -> void: + match index: + 0: + %Where.select(0) + %Where.set_item_disabled(0, false) + %Where.set_item_disabled(1, false) + %Where.set_item_disabled(2, true) + 1: + %Where.select(0) + %Where.set_item_disabled(0, false) + %Where.set_item_disabled(1, false) + %Where.set_item_disabled(2, true) + 2: + %Where.select(1) + %Where.set_item_disabled(0, true) + %Where.set_item_disabled(1, false) + %Where.set_item_disabled(2, true) + 3,4: + %Where.select(0) + %Where.set_item_disabled(0, false) + %Where.set_item_disabled(1, true) + %Where.set_item_disabled(2, true) + %PureTextFlags.visible = index == 0 + _on_where_item_selected(%Where.selected) + + +func _on_where_item_selected(index:int) -> void: + %Character.visible = index == 1 + + +func get_character_suggestions(search_text:String) -> Dictionary: + var suggestions := {} + + #override the previous _character_directory with the meta, specifically for searching otherwise new nodes wont work + var _character_directory = Engine.get_main_loop().get_meta('dialogic_character_directory') + + var icon := load("res://addons/dialogic/Editor/Images/Resources/character.svg") + suggestions['(No one)'] = {'value':null, 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + + for resource in _character_directory.keys(): + suggestions[resource] = { + 'value' : resource, + 'tooltip' : _character_directory[resource]['full_path'], + 'icon' : icon.duplicate()} + return suggestions + + +func save(): + if %Old.text.is_empty() or %New.text.is_empty(): + return + if %Where.selected == 1 and %Character.current_value == null: + return + + var previous := {} + if mode == Modes.EDIT: + previous = item.get_metadata(0) + item.get_parent() + item.free() + + var ref_manager := find_parent('ReferenceManager') + var character_names := [] + if %Character.current_value != null: + character_names = [%Character.current_value] + ref_manager.add_ref_change(%Old.text, %New.text, %Type.selected, %Where.selected, character_names, %WholeWords.button_pressed, %MatchCase.button_pressed, previous) + hide() diff --git a/addons/dialogic/Editor/Common/TitleBgStylebox.tres b/addons/dialogic/Editor/Common/TitleBgStylebox.tres new file mode 100644 index 0000000000000000000000000000000000000000..f08bb2c8820928700f5c25d28c35ac5bcfda13cf --- /dev/null +++ b/addons/dialogic/Editor/Common/TitleBgStylebox.tres @@ -0,0 +1,8 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://dmsjhgv22dns8"] + +[resource] +content_margin_left = 5.0 +content_margin_top = 5.0 +content_margin_right = 5.0 +content_margin_bottom = 5.0 +bg_color = Color(0.545098, 0.545098, 0.545098, 0.211765) diff --git a/addons/dialogic/Editor/Common/broken_reference_manager.tscn b/addons/dialogic/Editor/Common/broken_reference_manager.tscn new file mode 100644 index 0000000000000000000000000000000000000000..efd22c57fbe6b93ccfe44b0d42a52f3754532f2b --- /dev/null +++ b/addons/dialogic/Editor/Common/broken_reference_manager.tscn @@ -0,0 +1,289 @@ +[gd_scene load_steps=8 format=3 uid="uid://c7lmt5cp7bxcm"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/reference_manager.gd" id="1_3tomk"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd" id="2_vtwcs"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3_x5bo4"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hkp0w"] +content_margin_left = 10.0 +content_margin_top = 10.0 +content_margin_right = 10.0 +content_margin_bottom = 10.0 +bg_color = Color(1, 1, 1, 1) + +[sub_resource type="Image" id="Image_bryma"] +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_bryma") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_02ogm"] +content_margin_left = 5.0 +content_margin_top = 5.0 +content_margin_right = 5.0 +content_margin_bottom = 5.0 +bg_color = Color(1, 0.365, 0.365, 1) +draw_center = false +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0, 0, 0, 1) +corner_detail = 1 + +[node name="ReferenceManager" type="PanelContainer"] +self_modulate = Color(0, 0, 0, 1) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_hkp0w") +script = ExtResource("1_3tomk") + +[node name="VBoxContainer" type="VSplitContainer" parent="."] +layout_mode = 2 + +[node name="ChangeList" type="VBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HBox2" type="HBoxContainer" parent="VBoxContainer/ChangeList"] +layout_mode = 2 + +[node name="Title" type="Label" parent="VBoxContainer/ChangeList/HBox2"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_font_sizes/font_size = 16 +text = "Broken Reference Manager" + +[node name="TitleTooltip" type="TextureRect" parent="VBoxContainer/ChangeList/HBox2"] +unique_name_in_owner = true +modulate = Color(0, 0, 0, 1) +layout_mode = 2 +tooltip_text = "Because dialogics timelines are pure text files, it can often happen that +references (like variables, portraits, character names, etc.) break. + +This manager allows you to fix broken references by replacing them. + +Dialogic will automatically add items to this list, when you rename already +existing things. After such changes you should check if there are any +broken references." +texture = SubResource("ImageTexture_lce2m") +stretch_mode = 3 + +[node name="ChangesSection" type="PanelContainer" parent="VBoxContainer/ChangeList"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_type_variation = &"DialogicPanelA" + +[node name="VBox" type="VBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox"] +layout_mode = 2 + +[node name="SectionTitle" type="Label" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Recent renames" + +[node name="AddButton" type="Button" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 10 +tooltip_text = "Add custom rename" + +[node name="ReplacementPanel" type="PanelContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton"] +unique_name_in_owner = true +visible = false +top_level = true +custom_minimum_size = Vector2(400, 0) +layout_mode = 0 +offset_left = -400.0 +offset_top = 24.0 +offset_bottom = 244.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_02ogm") +script = ExtResource("2_vtwcs") + +[node name="VBox" type="VBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel"] +layout_mode = 2 + +[node name="Title" type="Label" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"HeaderSmall" +text = "Custom Replacement" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox"] +layout_mode = 2 + +[node name="Old" type="LineEdit" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Old" + +[node name="Label2" type="Label" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer"] +layout_mode = 2 +text = "->" + +[node name="New" type="LineEdit" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "New" + +[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer3"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Type: " +horizontal_alignment = 2 + +[node name="Type" type="OptionButton" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer3"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "This decides the regexes for searching. Pure text allows you to enter your own regex into into \"Old\". " +item_count = 5 +selected = 0 +fit_to_longest_item = false +popup/item_0/text = "Pure Text" +popup/item_0/id = 0 +popup/item_1/text = "Variable" +popup/item_1/id = 1 +popup/item_2/text = "Portrait" +popup/item_2/id = 2 +popup/item_3/text = "Character (Ref)" +popup/item_3/id = 3 +popup/item_4/text = "Timeline (Ref)" +popup/item_4/id = 4 + +[node name="PureTextFlags" type="HBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox"] +unique_name_in_owner = true +layout_mode = 2 +alignment = 2 + +[node name="MatchCase" type="CheckBox" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/PureTextFlags"] +unique_name_in_owner = true +layout_mode = 2 +button_pressed = true +text = "Match Case" + +[node name="WholeWords" type="CheckBox" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/PureTextFlags"] +unique_name_in_owner = true +layout_mode = 2 +text = "Whole Words" + +[node name="HBoxContainer4" type="HBoxContainer" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox"] +layout_mode = 2 + +[node name="Label4" type="Label" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer4"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Where: " +horizontal_alignment = 2 + +[node name="Where" type="OptionButton" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer4"] +unique_name_in_owner = true +layout_mode = 2 +item_count = 3 +selected = 0 +fit_to_longest_item = false +popup/item_0/text = "Everywhere" +popup/item_0/id = 0 +popup/item_1/text = "Only for Character" +popup/item_1/id = 1 +popup/item_2/text = "Texts only" +popup/item_2/id = 2 + +[node name="Character" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer4" instance=ExtResource("3_x5bo4")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="AddButton" type="Button" parent="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox"] +unique_name_in_owner = true +layout_mode = 2 +text = "Add/Save" + +[node name="ChangeTree" type="Tree" parent="VBoxContainer/ChangeList/ChangesSection/VBox"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/draw_relationship_lines = 1 +columns = 3 +hide_root = true + +[node name="CheckButton" type="Button" parent="VBoxContainer/ChangeList/ChangesSection/VBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +tooltip_text = "Search timelines for occurences of these renames" +text = "Check Selected" +icon = SubResource("ImageTexture_lce2m") + +[node name="ReplacementSection" type="PanelContainer" parent="VBoxContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +theme_type_variation = &"DialogicPanelA" + +[node name="FindList" type="VBoxContainer" parent="VBoxContainer/ReplacementSection"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HBox" type="HBoxContainer" parent="VBoxContainer/ReplacementSection/FindList"] +layout_mode = 2 + +[node name="SectionTitle2" type="Label" parent="VBoxContainer/ReplacementSection/FindList/HBox"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "Found references" + +[node name="State" type="Label" parent="VBoxContainer/ReplacementSection/FindList/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 8 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "State" + +[node name="ReferenceTree" type="Tree" parent="VBoxContainer/ReplacementSection/FindList"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/draw_relationship_lines = 1 +columns = 2 +hide_root = true + +[node name="Replace" type="Button" parent="VBoxContainer/ReplacementSection/FindList"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +tooltip_text = "Replace all selected findings (Careful, no undo!)" +text = "Replace Selected" +icon = SubResource("ImageTexture_lce2m") + +[connection signal="resized" from="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel" to="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel" method="_on_resized"] +[connection signal="item_selected" from="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer3/Type" to="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel" method="_on_type_item_selected"] +[connection signal="item_selected" from="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/HBoxContainer4/Where" to="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel" method="_on_where_item_selected"] +[connection signal="pressed" from="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel/VBox/AddButton" to="VBoxContainer/ChangeList/ChangesSection/VBox/HBoxContainer/AddButton/ReplacementPanel" method="save"] +[connection signal="button_clicked" from="VBoxContainer/ChangeList/ChangesSection/VBox/ChangeTree" to="." method="_on_change_tree_button_clicked"] +[connection signal="item_edited" from="VBoxContainer/ChangeList/ChangesSection/VBox/ChangeTree" to="." method="_on_change_tree_item_edited"] +[connection signal="pressed" from="VBoxContainer/ChangeList/ChangesSection/VBox/CheckButton" to="." method="_on_check_button_pressed"] +[connection signal="pressed" from="VBoxContainer/ReplacementSection/FindList/Replace" to="." method="_on_replace_pressed"] diff --git a/addons/dialogic/Editor/Common/hint_tooltip_icon.gd b/addons/dialogic/Editor/Common/hint_tooltip_icon.gd new file mode 100644 index 0000000000000000000000000000000000000000..940d2bd43ec897bed646060ea1054391d242b6f4 --- /dev/null +++ b/addons/dialogic/Editor/Common/hint_tooltip_icon.gd @@ -0,0 +1,9 @@ +@tool +extends TextureRect + +@export_multiline var hint_text = "" + +func _ready(): + texture = get_theme_icon("NodeInfo", "EditorIcons") + modulate = get_theme_color("readonly_color", "Editor") + tooltip_text = hint_text diff --git a/addons/dialogic/Editor/Common/hint_tooltip_icon.tscn b/addons/dialogic/Editor/Common/hint_tooltip_icon.tscn new file mode 100644 index 0000000000000000000000000000000000000000..3ee10fb41b165fc15ff73aee1a1767faeb4b26ef --- /dev/null +++ b/addons/dialogic/Editor/Common/hint_tooltip_icon.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=4 format=3 uid="uid://dbpkta2tjsqim"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.gd" id="1_x8t45"] + +[sub_resource type="Image" id="Image_eiyxd"] +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_lseut"] +image = SubResource("Image_eiyxd") + +[node name="HintTooltip" type="TextureRect"] +modulate = Color(0, 0, 0, 1) +texture = SubResource("ImageTexture_lseut") +stretch_mode = 3 +script = ExtResource("1_x8t45") diff --git a/addons/dialogic/Editor/Common/reference_manager.gd b/addons/dialogic/Editor/Common/reference_manager.gd new file mode 100644 index 0000000000000000000000000000000000000000..49ae3dcf31ecf0bbe9c9a5fadb908d98aec6a23f --- /dev/null +++ b/addons/dialogic/Editor/Common/reference_manager.gd @@ -0,0 +1,299 @@ +@tool +extends PanelContainer + +## This manager shows a list of changed references and allows searching for them and replacing them. + +var reference_changes :Array[Dictionary] = [] + + +func _ready() -> void: + if owner.get_parent() is SubViewport: + return + %ReplacementSection.hide() + + %CheckButton.icon = get_theme_icon("Search", "EditorIcons") + %Replace.icon = get_theme_icon("ArrowRight", "EditorIcons") + %TitleTooltip.texture = get_theme_icon("NodeInfo", "EditorIcons") + %TitleTooltip.modulate = get_theme_color("readonly_color", "Editor") + + %State.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")) + + self_modulate = get_theme_color("background", "Editor") + + + %Title.add_theme_font_override("font", get_theme_font("title", "EditorFonts")) + %Title.add_theme_color_override("font_color", get_theme_color("accent_color", "Editor")) + %Title.add_theme_font_size_override("font_size", get_theme_font_size("doc_size", "EditorFonts")) + + + %SectionTitle.add_theme_font_override("font", get_theme_font("title", "EditorFonts")) + %SectionTitle.add_theme_font_size_override("font_size", get_theme_font_size("doc_size", "EditorFonts")) + + %SectionTitle2.add_theme_font_override("font", get_theme_font("title", "EditorFonts")) + %SectionTitle2.add_theme_font_size_override("font_size", get_theme_font_size("doc_size", "EditorFonts")) + + +func open() -> void: + show() + %ReplacementPanel.hide() + %ReplacementSection.hide() + %ChangeTree.clear() + %ChangeTree.create_item() + %ChangeTree.set_column_expand(0, false) + %ChangeTree.set_column_expand(2, false) + var categories := {null:%ChangeTree.get_root()} + for i in reference_changes: + var parent : TreeItem = null + if !i.get('category', null) in categories: + parent = %ChangeTree.create_item() + parent.set_text(1, i.category) + parent.set_custom_color(1, get_theme_color("disabled_font_color", "Editor")) + categories[i.category] = parent + else: + parent = categories[i.get('category')] + + var item :TreeItem = %ChangeTree.create_item(parent) + item.set_text(1, i.what+" -> "+i.forwhat) + item.add_button(1, get_theme_icon("Edit", "EditorIcons"), 1, false, 'Edit') + item.add_button(1, get_theme_icon("Remove", "EditorIcons"), 0, false, 'Remove Change from List') + item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK) + item.set_checked(0, true) + item.set_editable(0, true) + item.set_metadata(0, i) + %CheckButton.disabled = reference_changes.is_empty() + + +func _on_change_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_index:int) -> void: + if id == 0: + reference_changes.erase(item.get_metadata(0)) + if item.get_parent().get_child_count() == 1: + item.get_parent().free() + else: + item.free() + + %CheckButton.disabled = reference_changes.is_empty() + + if id == 1: + %ReplacementPanel.open_existing(item, item.get_metadata(0)) + + +func _on_change_tree_item_edited() -> void: + if !%ChangeTree.get_selected(): + return + %CheckButton.disabled = false + + +func _on_check_button_pressed() -> void: + var to_be_checked :Array[Dictionary]= [] + var item :TreeItem = %ChangeTree.get_root() + while item.get_next_visible(): + item = item.get_next_visible() + + if item.get_child_count(): + continue + + if item.is_checked(0): + to_be_checked.append(item.get_metadata(0)) + to_be_checked[-1]['item'] = item + to_be_checked[-1]['count'] = 0 + + open_finder(to_be_checked) + %CheckButton.disabled = true + + +func open_finder(replacements:Array[Dictionary]) -> void: + %ReplacementSection.show() + var regexes : Array[Array] = [] + + for i in replacements: + if i.has('character_names') and !i.character_names.is_empty(): + i['character_regex'] = RegEx.create_from_string("(?m)^(Join|Update|Leave)?\\s*("+str(i.character_names).replace('"', '').replace(', ', '|').trim_suffix(']').trim_prefix('[').replace('/', '\\/')+")(?(1).*|.*:)") + + for regex_string in i.regex: + var regex := RegEx.create_from_string(regex_string) + regexes.append([regex, i]) + + var finds : Array[Dictionary] = [] + + for timeline_path in DialogicUtil.list_resources_of_type('.dtl'): + %State.text = "Loading '"+timeline_path+"'" + + var timeline_file := FileAccess.open(timeline_path, FileAccess.READ) + var timeline_text :String = timeline_file.get_as_text() + var timeline_events : PackedStringArray = timeline_text.split('\n') + timeline_file.close() + + for regex_info in regexes: + %State.text = "Searching '"+timeline_path+"' for "+regex_info[1].what+' -> '+regex_info[1].forwhat + for i in regex_info[0].search_all(timeline_text): + if regex_info[1].has('character_regex'): + if regex_info[1].character_regex.search(get_line(timeline_text, i.get_start()+1)) == null: + continue + + var line_number := timeline_text.count('\n', 0, i.get_start()+1)+1 + var line := timeline_text.get_slice('\n', line_number-1) + finds.append({ + 'match':i, + 'timeline':timeline_path, + 'info': regex_info[1], + 'line_number': line_number, + 'line': line, + 'line_start': timeline_text.rfind('\n', i.get_start()) + }) + regex_info[1]['count'] += 1 + + + for regex_info in regexes: + regex_info[1]['item'].set_text(2, str(regex_info[1]['count'])) + update_count_coloring() + + %State.text = str(len(finds))+ " occurrences found" + + %ReferenceTree.clear() + %ReferenceTree.set_column_expand(0, false) + %ReferenceTree.create_item() + + var timelines := {} + var height := 0 + for i in finds: + var parent : TreeItem = null + if !i.timeline in timelines: + parent = %ReferenceTree.create_item() + parent.set_text(1, i.timeline) + parent.set_custom_color(1, get_theme_color("disabled_font_color", "Editor")) + timelines[i.timeline] = parent + height += %ReferenceTree.get_item_area_rect(parent).size.y+10 + else: + parent = timelines[i.timeline] + + var item :TreeItem = %ReferenceTree.create_item(parent) + item.set_text(1, 'Line '+str(i.line_number)+': '+i.line) + item.set_tooltip_text(1, i.info.what+' -> '+i.info.forwhat) + item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK) + item.set_checked(0, true) + item.set_editable(0, true) + item.set_metadata(0, i) + height += %ReferenceTree.get_item_area_rect(item).size.y+10 + var change_item :TreeItem = i.info.item + change_item.set_meta('found_items', change_item.get_meta('found_items', [])+[item]) + + + + %ReferenceTree.custom_minimum_size.y = min(height, 200) + + %ReferenceTree.visible = !finds.is_empty() + %Replace.disabled = finds.is_empty() + if finds.is_empty(): + %State.text = "Nothing found" + else: + %Replace.grab_focus() + + +func get_line(string:String, at_index:int) -> String: + return string.substr(max(string.rfind('\n', at_index), 0), string.find('\n', at_index)-string.rfind('\n', at_index)) + + +func update_count_coloring() -> void: + var item :TreeItem = %ChangeTree.get_root() + while item.get_next_visible(): + item = item.get_next_visible() + + if item.get_child_count(): + continue + if int(item.get_text(2)) > 0: + item.set_custom_bg_color(1, get_theme_color("warning_color", "Editor").darkened(0.8)) + item.set_custom_color(1, get_theme_color("warning_color", "Editor")) + item.set_custom_color(2, get_theme_color("warning_color", "Editor")) + else: + item.set_custom_color(2, get_theme_color("success_color", "Editor")) + item.set_custom_color(1, get_theme_color("readonly_font_color", "Editor")) + if item.get_button_count(1): + item.erase_button(1, 1) + item.add_button(1, get_theme_icon("Eraser", "EditorIcons"), -1, true, "This reference was not found anywhere and will be removed from this list.") + + +func _on_replace_pressed() -> void: + var to_be_replaced :Array[Dictionary]= [] + var item :TreeItem = %ReferenceTree.get_root() + var affected_timelines :Array[String]= [] + + while item.get_next_visible(): + item = item.get_next_visible() + + if item.get_child_count(): + continue + + if item.is_checked(0): + to_be_replaced.append(item.get_metadata(0)) + to_be_replaced[-1]['f_item'] = item + if !item.get_metadata(0).timeline in affected_timelines: + affected_timelines.append(item.get_metadata(0).timeline) + replace(affected_timelines, to_be_replaced) + + +func replace(timelines:Array[String], replacement_info:Array[Dictionary]) -> void: + var reopen_timeline := "" + var timeline_editor :DialogicEditor = find_parent('EditorView').editors_manager.editors['Timeline'].node + if timeline_editor.current_resource != null and timeline_editor.current_resource.resource_path in timelines: + reopen_timeline = timeline_editor.current_resource.resource_path + find_parent('EditorView').editors_manager.clear_editor(timeline_editor) + + replacement_info.sort_custom(func(a,b): return a.match.get_start() < b.match.get_start()) + + for timeline_path in timelines: + %State.text = "Loading '"+timeline_path+"'" + + var timeline_file := FileAccess.open(timeline_path, FileAccess.READ_WRITE) + var timeline_text :String = timeline_file.get_as_text() + var timeline_events := timeline_text.split('\n') + timeline_file.close() + + var idx := 1 + var offset_correction := 0 + for replacement in replacement_info: + if replacement.timeline != timeline_path: + continue + + %State.text = "Replacing in '"+timeline_path + "' ("+str(idx)+"/"+str(len(replacement_info))+")" + var group := 'replace' + if not 'replace' in replacement.match.names: + group = '' + + + timeline_text = timeline_text.substr(0, replacement.match.get_start(group) + offset_correction) + \ + replacement.info.regex_replacement + \ + timeline_text.substr(replacement.match.get_end(group) + offset_correction) + offset_correction += len(replacement.info.regex_replacement)-len(replacement.match.get_string(group)) + + replacement.info.count -= 1 + replacement.info.item.set_text(2, str(replacement.info.count)) + replacement.f_item.set_custom_bg_color(1, get_theme_color("success_color", "Editor").darkened(0.8)) + + timeline_file = FileAccess.open(timeline_path, FileAccess.WRITE) + timeline_file.store_string(timeline_text.strip_edges(false, true)) + timeline_file.close() + + if ResourceLoader.has_cached(timeline_path): + var tml := load(timeline_path) + tml.from_text(timeline_text) + + if !reopen_timeline.is_empty(): + find_parent('EditorView').editors_manager.edit_resource(load(reopen_timeline), false, true) + + update_count_coloring() + + %Replace.disabled = true + %CheckButton.disabled = false + %State.text = "Done Replacing" + + +func close() -> void: + var item :TreeItem = %ChangeTree.get_root() + while item.get_next_visible(): + item = item.get_next_visible() + + if item.get_child_count(): + continue + if item.get_text(2) != "" and int(item.get_text(2)) == 0: + reference_changes.erase(item.get_metadata(0)) + diff --git a/addons/dialogic/Editor/Common/reference_manager_window.gd b/addons/dialogic/Editor/Common/reference_manager_window.gd new file mode 100644 index 0000000000000000000000000000000000000000..660d17f3fb467006a75f1500529add5ad733ab97 --- /dev/null +++ b/addons/dialogic/Editor/Common/reference_manager_window.gd @@ -0,0 +1,169 @@ +@tool +extends Window + +## This window manages communication with the replacement manager it contains. +## Other scripts can call the add_ref_change() method to register changes directly +## or use the helpers add_variable_ref_change() and add_portrait_ref_change() + +@onready var editors_manager := get_node("../Margin/EditorsManager") + +enum Where {EVERYWHERE, BY_CHARACTER, TEXTS_ONLY} +enum Types {TEXT, VARIABLE, PORTRAIT, CHARACTER_NAME, TIMELINE_NAME} + +var icon_button :Button = null + + +func _ready() -> void: + if owner.get_parent() is SubViewport: + return + icon_button = editors_manager.add_icon_button(get_theme_icon("Unlinked", "EditorIcons"), 'Manage Broken References') + icon_button.pressed.connect(open) + + var dot := Sprite2D.new() + dot.texture = get_theme_icon("GuiGraphNodePort", "EditorIcons") + dot.scale = Vector2(0.8, 0.8) + dot.z_index = 10 + dot.position = Vector2(icon_button.size.x*0.8, icon_button.size.x*0.2) + dot.modulate = get_theme_color("warning_color", "Editor").lightened(0.5) + + icon_button.add_child(dot) + + var old_changes :Array = DialogicUtil.get_editor_setting('reference_changes', []) + if !old_changes.is_empty(): + $Manager.reference_changes = old_changes + + update_indicator() + + hide() + + get_parent().plugin_reference.get_editor_interface().get_file_system_dock().files_moved.connect(_on_file_moved) + get_parent().get_node('ResourceRenameWarning').confirmed.connect(open) + + +func add_ref_change(old_name:String, new_name:String, type:Types, where:=Where.TEXTS_ONLY, character_names:=[], + whole_words:=false, case_sensitive:=false, previous:Dictionary = {}) -> void: + var regexes := [] + var category_name := "" + match type: + Types.TEXT: + category_name = "Texts" + regexes = ['(?'+old_name.replace('/', '\\/')+')'] + if !case_sensitive: + regexes[0] = '(?i)'+regexes[0] + if whole_words: + regexes = ['\\b'+regexes[0]+'\\b'] + + Types.VARIABLE: + regexes = ['{(?\\s*'+old_name.replace('/', '\\/')+'\\s*)}', 'var\\s*=\\s*"(?\\s*'+old_name.replace('/', '\\/')+'\\s*)"'] + category_name = "Variables" + + Types.PORTRAIT: + regexes = ['(?m)^[^:(]*\\((?'+old_name.replace('/', '\\/')+')\\)', '\\[\\s*portrait\\s*=(?\\s*'+old_name.replace('/', '\\/')+'\\s*)\\]'] + category_name = "Portraits by "+character_names[0] + + Types.CHARACTER_NAME: + # for reference: ((Join|Leave|Update) )?(?NAME)(?!\B)(?(1)|(?!([^:\n]|\\:)*(\n|$))) + regexes = ['((Join|Leave|Update) )?(?'+old_name+')(?!\\B)(?(1)|(?!([^:\\n]|\\\\:)*(\\n|$)))'] + category_name = "Renamed Character Files" + + Types.TIMELINE_NAME: + regexes = ['timeline ?= ?" ?(?'+old_name+') ?"'] + category_name = "Renamed Timeline Files" + + if where != Where.BY_CHARACTER: + character_names = [] + + # previous is only given when an existing item is edited + # in that case the old one is removed first + var idx := len($Manager.reference_changes) + if previous in $Manager.reference_changes: + idx = $Manager.reference_changes.find(previous) + $Manager.reference_changes.erase(previous) + + if _check_for_ref_change_cycle(old_name, new_name, category_name): + update_indicator() + return + + $Manager.reference_changes.insert(idx, + {'what':old_name, + 'forwhat':new_name, + 'regex': regexes, + 'regex_replacement':new_name, + 'category':category_name, + 'character_names':character_names, + 'texts_only':where == Where.TEXTS_ONLY, + 'type':type + }) + + update_indicator() + + if visible: + $Manager.open() + + +## Checks for reference cycles or chains. +## E.g. if you first rename a portrait from "happy" to "happy1" and then to "Happy/happy1" +## This will make sure only a change "happy" -> "Happy/happy1" is remembered +## This is very important for correct replacement +func _check_for_ref_change_cycle(old_name:String, new_name:String, category:String) -> bool: + for ref in $Manager.reference_changes: + if ref['forwhat'] == old_name and ref['category'] == category: + if new_name == ref['what']: + $Manager.reference_changes.erase(ref) + else: + $Manager.reference_changes[$Manager.reference_changes.find(ref)]['forwhat'] = new_name + $Manager.reference_changes[$Manager.reference_changes.find(ref)]['regex_replacement'] = new_name + return true + return false + + +## Helper for adding variable ref changes +func add_variable_ref_change(old_name:String, new_name:String) -> void: + add_ref_change(old_name, new_name, Types.VARIABLE, Where.EVERYWHERE) + + +## Helper for adding portrait ref changes +func add_portrait_ref_change(old_name:String, new_name:String, character_names:PackedStringArray) -> void: + add_ref_change(old_name, new_name, Types.PORTRAIT, Where.BY_CHARACTER, character_names) + + +## Helper for adding character name ref changes +func add_character_name_ref_change(old_name:String, new_name:String) -> void: + add_ref_change(old_name, new_name, Types.CHARACTER_NAME, Where.EVERYWHERE) + + +## Helper for adding timeline name ref changes +func add_timeline_name_ref_change(old_name:String, new_name:String) -> void: + add_ref_change(old_name, new_name, Types.TIMELINE_NAME, Where.EVERYWHERE) + + +func open() -> void: + popup_centered_ratio(0.5) + move_to_foreground() + grab_focus() + $Manager.open() + + +func _on_close_requested() -> void: + hide() + $Manager.close() + update_indicator() + + +func update_indicator() -> void: + icon_button.get_child(0).visible = !$Manager.reference_changes.is_empty() + for i in $Manager.reference_changes: + i.item = null + DialogicUtil.set_editor_setting('reference_changes', $Manager.reference_changes) + + +## FILE MOVEMENT: +func _on_file_moved(old_file:String, new_file:String) -> void: + if old_file.ends_with('.dch') and new_file.ends_with('.dch'): + if old_file.get_file() != new_file.get_file(): + add_character_name_ref_change(old_file.get_file().trim_suffix('.dch'), new_file.get_file().trim_suffix('.dch')) + get_parent().get_node('ResourceRenameWarning').popup_centered() + elif old_file.ends_with('.dtl') and new_file.ends_with('.dtl'): + if old_file.get_file() != new_file.get_file(): + add_timeline_name_ref_change(old_file.get_file().trim_suffix('.dtl'), new_file.get_file().trim_suffix('.dtl')) + get_parent().get_node('ResourceRenameWarning').popup_centered() diff --git a/addons/dialogic/Editor/Common/side_bar.tscn b/addons/dialogic/Editor/Common/side_bar.tscn new file mode 100644 index 0000000000000000000000000000000000000000..ac2934c253cccce2463f0fdb072613fc330a68dd --- /dev/null +++ b/addons/dialogic/Editor/Common/side_bar.tscn @@ -0,0 +1,91 @@ +[gd_scene load_steps=5 format=3 uid="uid://cwe3r2tbh2og1"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/sidebar.gd" id="1_jnq65"] + +[sub_resource type="Theme" id="Theme_pn0f4"] +VBoxContainer/constants/separation = 4 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_gxwm6"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_n8rql"] + +[node name="SideBar" type="VSplitContainer"] +custom_minimum_size = Vector2(100, 130) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = SubResource("Theme_pn0f4") +split_offset = 100 +script = ExtResource("1_jnq65") + +[node name="VBox" type="VBoxContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Margin" type="MarginContainer" parent="VBox"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_bottom = 5 + +[node name="VSplitContainer" type="VSplitContainer" parent="VBox/Margin"] +layout_mode = 2 + +[node name="VBox" type="VBoxContainer" parent="VBox/Margin/VSplitContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Logo" type="TextureRect" parent="VBox/Margin/VSplitContainer/VBox"] +unique_name_in_owner = true +modulate = Color(1, 1, 1, 0.623529) +texture_filter = 6 +custom_minimum_size = Vector2(0, 25) +layout_mode = 2 +expand_mode = 3 +stretch_mode = 4 + +[node name="CurrentResource" type="Label" parent="VBox/Margin/VSplitContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "No resource" +horizontal_alignment = 1 +vertical_alignment = 1 +text_overrun_behavior = 1 + +[node name="Search" type="LineEdit" parent="VBox/Margin/VSplitContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +placeholder_text = "Filter Resources" +caret_blink = true +caret_blink_interval = 0.5 + +[node name="ResourcesList" type="ItemList" parent="VBox/Margin/VSplitContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +same_column_width = true + +[node name="ContentListSection" type="VBoxContainer" parent="VBox/Margin/VSplitContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ContentList" type="ItemList" parent="VBox/Margin/VSplitContainer/ContentListSection"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_override_styles/selected = SubResource("StyleBoxEmpty_gxwm6") +theme_override_styles/selected_focus = SubResource("StyleBoxEmpty_n8rql") +allow_reselect = true +same_column_width = true + +[node name="CurrentVersion" type="Label" parent="VBox"] +unique_name_in_owner = true +layout_mode = 2 +clip_text = true + +[connection signal="gui_input" from="VBox/Margin/VSplitContainer/VBox/Logo" to="." method="_on_logo_gui_input"] +[connection signal="text_changed" from="VBox/Margin/VSplitContainer/VBox/Search" to="." method="_on_search_text_changed"] diff --git a/addons/dialogic/Editor/Common/sidebar.gd b/addons/dialogic/Editor/Common/sidebar.gd new file mode 100644 index 0000000000000000000000000000000000000000..eaa53859b962b3822f1486568796d9eec8d42b9f --- /dev/null +++ b/addons/dialogic/Editor/Common/sidebar.gd @@ -0,0 +1,154 @@ +@tool +extends Control + +## Script that handles the editor sidebar. + +signal file_activated(file_path) + +signal content_item_activated(item_name) + +@onready var editors_manager = get_parent().get_parent() + + +func _ready(): + if owner.get_parent() is SubViewport: + return + + ## CONNECTIONS + %ResourcesList.item_selected.connect(_on_resources_list_item_selected) + %ResourcesList.item_clicked.connect(_on_resources_list_item_clicked) + editors_manager.resource_opened.connect(_on_editors_resource_opened) + editors_manager.editor_changed.connect(_on_editors_editor_changed) + + %ContentList.item_selected.connect(func (idx:int): content_item_activated.emit(%ContentList.get_item_text(idx))) + + var editor_scale := DialogicUtil.get_editor_scale() + ## ICONS + %Logo.texture = load("res://addons/dialogic/Editor/Images/dialogic-logo.svg") + %Logo.custom_minimum_size.y = 30*editor_scale + %Search.right_icon = get_theme_icon("Search", "EditorIcons") + + %CurrentResource.add_theme_stylebox_override('normal', get_theme_stylebox('normal', 'LineEdit')) + + %ContentList.add_theme_color_override("font_hovered_color", get_theme_color("warning_color", "Editor")) + %ContentList.add_theme_color_override("font_selected_color", get_theme_color("property_color_z", "Editor")) + + ## MARGINS + $VBox/Margin.set("theme_override_constants/margin_left", 4 * editor_scale) + $VBox/Margin.set("theme_override_constants/margin_bottom", 4 * editor_scale) + + ## VERSION LABEL + var plugin_cfg := ConfigFile.new() + plugin_cfg.load("res://addons/dialogic/plugin.cfg") + %CurrentVersion.text = plugin_cfg.get_value('plugin', 'version', 'unknown version') + + + + +################################################################################ +## RESOURCE LIST +################################################################################ + +func _on_editors_resource_opened(resource:Resource) -> void: + update_resource_list() + + +func _on_editors_editor_changed(previous:DialogicEditor, current:DialogicEditor) -> void: + %ContentListSection.visible = current.current_resource is DialogicTimeline + update_resource_list() + + +func update_resource_list(resources_list:PackedStringArray = []) -> void: + var filter :String = %Search.text + var current_file := "" + if editors_manager.current_editor and editors_manager.current_editor.current_resource: + current_file = editors_manager.current_editor.current_resource.resource_path + + var character_directory: Dictionary = editors_manager.resource_helper.character_directory + var timeline_directory: Dictionary = editors_manager.resource_helper.timeline_directory + if resources_list.is_empty(): + resources_list = DialogicUtil.get_editor_setting('last_resources', []) + if !current_file in resources_list: + resources_list.append(current_file) + + %CurrentResource.text = "No Resource" + %CurrentResource.add_theme_color_override("font_color", get_theme_color("disabled_font_color", "Editor")) + + %ResourcesList.clear() + var idx := 0 + for character in character_directory.values(): + if character['full_path'] in resources_list: + if filter.is_empty() or filter.to_lower() in character['unique_short_path'].to_lower(): + %ResourcesList.add_item( + character['unique_short_path'], + load("res://addons/dialogic/Editor/Images/Resources/character.svg")) + %ResourcesList.set_item_metadata(idx, character['full_path']) + %ResourcesList.set_item_tooltip(idx, character['full_path']) + if character['full_path'] == current_file: + %ResourcesList.select(idx) + %ResourcesList.set_item_custom_fg_color(idx, get_theme_color("accent_color", "Editor")) + %CurrentResource.text = character['unique_short_path']+'.dch' + idx += 1 + for timeline_name in timeline_directory: + if timeline_directory[timeline_name] in resources_list: + if filter.is_empty() or filter.to_lower() in timeline_name.to_lower(): + %ResourcesList.add_item(timeline_name, get_theme_icon("TripleBar", "EditorIcons")) + %ResourcesList.set_item_metadata(idx, timeline_directory[timeline_name]) + if timeline_directory[timeline_name] == current_file: + %ResourcesList.select(idx) + %ResourcesList.set_item_custom_fg_color(idx, get_theme_color("accent_color", "Editor")) + %CurrentResource.text = timeline_name+'.dtl' + idx += 1 + if %CurrentResource.text != "No Resource": + %CurrentResource.add_theme_color_override("font_color", get_theme_color("font_color", "Editor")) + %ResourcesList.sort_items_by_text() + DialogicUtil.set_editor_setting('last_resources', resources_list) + + +func _on_resources_list_item_selected(index:int) -> void: + if %ResourcesList.get_item_metadata(index) == null: + return + editors_manager.edit_resource(load(%ResourcesList.get_item_metadata(index))) + + +func _on_resources_list_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void: + # If clicked with the middle mouse button, remove the item from the list + if mouse_button_index == 3: + var new_list := [] + for entry in DialogicUtil.get_editor_setting('last_resources', []): + if entry != %ResourcesList.get_item_metadata(index): + new_list.append(entry) + DialogicUtil.set_editor_setting('last_resources', new_list) + %ResourcesList.remove_item(index) + + +func _on_search_text_changed(new_text:String) -> void: + update_resource_list() + + +func set_unsaved_indicator(saved:bool = true) -> void: + if saved and %CurrentResource.text.ends_with('(*)'): + %CurrentResource.text = %CurrentResource.text.trim_suffix('(*)') + if not saved and not %CurrentResource.text.ends_with('(*)'): + %CurrentResource.text = %CurrentResource.text+"(*)" + + +func _on_logo_gui_input(event:InputEvent) -> void: + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + editors_manager.open_editor(editors_manager.editors['HomePage'].node) + + +func update_content_list(list:PackedStringArray) -> void: + %ContentList.clear() + %ContentList.add_item('~ Top') + for i in list: + if i.is_empty(): continue + %ContentList.add_item(i) + if list.is_empty(): + return + + for i in editors_manager.resource_helper.timeline_directory: + if editors_manager.resource_helper.timeline_directory[i] == editors_manager.get_current_editor().current_resource.resource_path: + editors_manager.resource_helper.label_directory[i] = list + editors_manager.resource_helper.label_directory[''] = list + DialogicUtil.set_editor_setting('label_ref', editors_manager.resource_helper.label_directory) diff --git a/addons/dialogic/Editor/Common/toolbar.gd b/addons/dialogic/Editor/Common/toolbar.gd new file mode 100644 index 0000000000000000000000000000000000000000..1921802b78d6f1e785f5333cc01e295e837228b2 --- /dev/null +++ b/addons/dialogic/Editor/Common/toolbar.gd @@ -0,0 +1,50 @@ +@tool +extends HBoxContainer + +# Dialogic Editor toolbar. Works together with editors_mangager. + +################################################################################ +## EDITOR BUTTONS/LABELS +################################################################################ +func _ready(): + if owner.get_parent() is SubViewport: + return + var editor_scale := DialogicUtil.get_editor_scale() + %CustomButtons.custom_minimum_size.y = 33*editor_scale + + for child in get_children(): + if child is Button: + child.queue_free() + + +func add_icon_button(icon: Texture, tooltip: String) -> Button: + var button := Button.new() + button.icon = icon + button.tooltip_text = tooltip + button.flat = true + button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + button.add_theme_color_override('icon_hover_color', get_theme_color('warning_color', 'Editor')) + button.add_theme_stylebox_override('focus', StyleBoxEmpty.new()) + add_child(button) + move_child(button, -2) + return button + + +func add_custom_button(label:String, icon:Texture) -> Button: + var button := Button.new() + button.text = label + button.icon = icon +# button.flat = true + + button.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + %CustomButtons.add_child(button) +# custom_minimum_size.y = button.size.y + return button + + +func hide_all_custom_buttons() -> void: + for button in %CustomButtons.get_children(): + button.hide() + + + diff --git a/addons/dialogic/Editor/Events/BranchEnd.gd b/addons/dialogic/Editor/Events/BranchEnd.gd new file mode 100644 index 0000000000000000000000000000000000000000..75ede9bc5561b13421e60674d149da415f3b2bbe --- /dev/null +++ b/addons/dialogic/Editor/Events/BranchEnd.gd @@ -0,0 +1,75 @@ +@tool +extends Control +## A scene shown at the end of events that contain other events + +var resource : DialogicEndBranchEvent + +# References +var parent_node : Control = null +var end_control :Control = null + +# Indent +var indent_size := 15 +var current_indent_level := 1 + +func _ready() -> void: + $Icon.icon = get_theme_icon("GuiSpinboxUpdown", "EditorIcons") + $Spacer.custom_minimum_size.x = 100*DialogicUtil.get_editor_scale() + visual_deselect() + parent_node_changed() + + +## Called by the visual timeline editor +func visual_select() -> void: + modulate = get_theme_color("highlighted_font_color", "Editor") + + +## Called by the visual timeline editor +func visual_deselect() -> void: + modulate = parent_node.resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.3) + + +## Called by the visual timeline editor +func highlight() -> void: + modulate = parent_node.resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.6) + + +## Called by the visual timeline editor +func unhighlight() -> void: + modulate = parent_node.resource.event_color + + +func update_hidden_events_indicator(hidden_events_count:int = 0) -> void: + $HiddenEventsLabel.visible = hidden_events_count > 0 + if hidden_events_count == 1: + $HiddenEventsLabel.text = "[1 event hidden]" + else: + $HiddenEventsLabel.text = "["+str(hidden_events_count)+ " events hidden]" + + +## Called by the visual timeline editor +func set_indent(indent: int) -> void: + $Indent.custom_minimum_size = Vector2(indent_size * indent, 0) + $Indent.visible = indent != 0 + current_indent_level = indent + queue_redraw() + + +## Called by the visual timeline editor if something was edited on the parent event block +func parent_node_changed() -> void: + if parent_node and end_control and end_control.has_method('refresh'): + end_control.refresh() + + +## Called on creation if the parent event provides an end control +func add_end_control(control:Control) -> void: + if !control: + return + add_child(control) + control.size_flags_vertical = SIZE_SHRINK_CENTER + if "parent_resource" in control: + control.parent_resource = parent_node.resource + if control.has_method('refresh'): + control.refresh() + end_control = control + diff --git a/addons/dialogic/Editor/Events/BranchEnd.tscn b/addons/dialogic/Editor/Events/BranchEnd.tscn new file mode 100644 index 0000000000000000000000000000000000000000..e90f8fc2963629573b39cc8f31397a89ec29d212 --- /dev/null +++ b/addons/dialogic/Editor/Events/BranchEnd.tscn @@ -0,0 +1,49 @@ +[gd_scene load_steps=4 format=3 uid="uid://de13fdeebrkcb"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/BranchEnd.gd" id="1"] + +[sub_resource type="Image" id="Image_8tlok"] +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_vm1jl"] +image = SubResource("Image_8tlok") + +[node name="EndBranch" type="HBoxContainer"] +custom_minimum_size = Vector2(0, 40) +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 40.0 +grow_horizontal = 2 +mouse_filter = 0 +script = ExtResource("1") + +[node name="Indent" type="Control" parent="."] +layout_mode = 2 +size_flags_vertical = 0 + +[node name="Spacer" type="Control" parent="."] +custom_minimum_size = Vector2(75, 0) +layout_mode = 2 +size_flags_vertical = 0 + +[node name="Icon" type="Button" parent="."] +unique_name_in_owner = true +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "Click and drag" +focus_mode = 0 +mouse_filter = 1 +icon = SubResource("ImageTexture_vm1jl") +flat = true + +[node name="HiddenEventsLabel" type="Label" parent="."] +visible = false +layout_mode = 2 +text = "XX Events hidden" diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.gd b/addons/dialogic/Editor/Events/EventBlock/event_block.gd new file mode 100644 index 0000000000000000000000000000000000000000..c6011ad68e032fc4be208094e16886fc0d117bc7 --- /dev/null +++ b/addons/dialogic/Editor/Events/EventBlock/event_block.gd @@ -0,0 +1,437 @@ +@tool +extends MarginContainer + +## Scene that represents an event in the visual timeline editor. + +signal option_action(action_name) +signal content_changed() + +# Resource +var resource : DialogicEvent + +var selected : bool = false + +### internal node eferences +@onready var warning := %Warning +@onready var icon_texture := %IconTexture +@onready var header_content_container := %HeaderContent +@onready var body_container := %Body +@onready var body_content_container := %BodyContent + +# is the body visible +var expanded := true + +# was the body content loaded +var body_was_build := false + +# does the body have elements? +var has_any_enabled_body_content := false + +# list that stores visibility conditions +var field_list := [] + +# for choice and condition +var end_node:Node = null: + get: + return end_node + set(node): + end_node = node + %CollapseButton.visible = true if end_node else false + +var collapsed := false + +### extarnal node references +var editor_reference + +### Icon size +var icon_size := 28 + +### the indent size +var indent_size := 22 +var current_indent_level := 1 + +# Setting this to true will ignore the event while saving +# Useful for making placeholder events in drag and drop +var ignore_save := false + + +## ***************************************************************************** +## PUBLIC METHODS +## ***************************************************************************** + +func visual_select() -> void: + $PanelContainer.add_theme_stylebox_override('panel', load("res://addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres")) + selected = true + %IconPanel.self_modulate = resource.event_color + %IconTexture.modulate = get_theme_color("icon_saturation", "Editor") +# %IconTexture.self_modulate.a = 1 + + +func visual_deselect() -> void: + $PanelContainer.add_theme_stylebox_override('panel', load("res://addons/dialogic/Editor/Events/styles/unselected_stylebox.tres")) + selected = false + %IconPanel.self_modulate = resource.event_color.lerp(Color.DARK_SLATE_GRAY, 0.1) + %IconTexture.modulate = get_theme_color('font_color', 'Label') +# %IconTexture.self_modulate.a = 0.7 + + +func is_selected() -> bool: + return selected + +# called by the timeline before adding it to the tree +func load_data(data:DialogicEvent) -> void: + resource = data + + +func set_warning(text:String= "") -> void: + if !text.is_empty(): + warning.show() + warning.tooltip_text = text + else: + warning.hide() + + +func set_indent(indent: int) -> void: + add_theme_constant_override("margin_left", indent_size*indent) + current_indent_level = indent + + +## ***************************************************************************** +## PRIVATE METHODS +## ***************************************************************************** + +func _set_event_icon(icon: Texture) -> void: + icon_texture.texture = icon + var _scale := DialogicUtil.get_editor_scale() + var ip := %IconPanel + var ipc := icon_texture + + # Resizing the icon acording to the scale + + ip.custom_minimum_size = Vector2(icon_size, icon_size) * _scale + ipc.custom_minimum_size = ip.custom_minimum_size + + # Updating the theme properties to scale + var custom_style :StyleBox = ip.get_theme_stylebox('panel') + custom_style.corner_radius_top_left = 5 * _scale + custom_style.corner_radius_top_right = 5 * _scale + custom_style.corner_radius_bottom_left = 5 * _scale + custom_style.corner_radius_bottom_right = 5 * _scale + + +# called to inform event parts, that a focus is wanted +func focus(): + pass + + +func toggle_collapse(toggled:bool) -> void: + collapsed = toggled + var timeline_editor = find_parent('VisualEditor') + if (timeline_editor != null): + # @todo select item and clear selection is marked as "private" in TimelineEditor.gd + # consider to make it "public" or add a public helper function + timeline_editor.indent_events() + + +func build_editor(build_header:bool = true, build_body:bool = false) -> void: + var current_body_container :HFlowContainer = null + + if build_body and body_was_build: build_body = false + if build_body: + if body_was_build: + return + current_body_container = HFlowContainer.new() + %BodyContent.add_child(current_body_container) + body_was_build = true + + for p in resource.get_event_editor_info(): + field_list.append({'node':null, 'location':p.location}) + if p.has('condition'): + field_list[-1]['condition'] = p.condition + + if !build_body and p.location == 1: + continue + elif !build_header and p.location == 0: + continue + + ### -------------------------------------------------------------------- + ### 1. CREATE A NODE OF THE CORRECT TYPE FOR THE PROPERTY + var editor_node : Control + + ### LINEBREAK + if p.name == "linebreak": + field_list.remove_at(field_list.size()-1) + if !current_body_container.get_child_count(): + current_body_container.queue_free() + current_body_container = HFlowContainer.new() + %BodyContent.add_child(current_body_container) + continue + + ### STRINGS + elif p.dialogic_type == resource.ValueType.MULTILINE_TEXT: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/MultilineText.tscn").instantiate() + elif p.dialogic_type == resource.ValueType.SINGLELINE_TEXT: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/SinglelineText.tscn").instantiate() + editor_node.placeholder = p.display_info.get('placeholder', '') + elif p.dialogic_type == resource.ValueType.BOOL: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/Bool.tscn").instantiate() + + elif p.dialogic_type == resource.ValueType.FILE: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn").instantiate() + editor_node.file_filter = p.display_info.get('file_filter', '') + editor_node.placeholder = p.display_info.get('placeholder', '') + editor_node.resource_icon = p.display_info.get('icon', null) + if editor_node.resource_icon == null and p.display_info.has('editor_icon'): + editor_node.resource_icon = callv('get_theme_icon', p.display_info.editor_icon) + + elif p.dialogic_type == resource.ValueType.CONDITION: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn").instantiate() + + ## Complex Picker + elif p.dialogic_type == resource.ValueType.COMPLEX_PICKER: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn").instantiate() + + editor_node.file_extension = p.display_info.get('file_extension', '') + editor_node.collapse_when_empty = p.display_info.get('collapse_when_empty', false) + editor_node.get_suggestions_func = p.display_info.get('suggestions_func', editor_node.get_suggestions_func) + editor_node.empty_text = p.display_info.get('empty_text', '') + editor_node.placeholder_text = p.display_info.get('placeholder', 'Select Resource') + editor_node.resource_icon = p.display_info.get('icon', null) + editor_node.enable_pretty_name = p.display_info.get('enable_pretty_name', false) + if editor_node.resource_icon == null and p.display_info.has('editor_icon'): + editor_node.resource_icon = callv('get_theme_icon', p.display_info.editor_icon) + + ## INTEGERS + elif p.dialogic_type == resource.ValueType.INTEGER: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/Number.tscn").instantiate() + editor_node.use_int_mode() + editor_node.max = p.display_info.get('max', 9999) + editor_node.min = p.display_info.get('min', -9999) + elif p.dialogic_type == resource.ValueType.FLOAT: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/Number.tscn").instantiate() + editor_node.use_float_mode() + editor_node.max = p.display_info.get('max', 9999) + editor_node.min = p.display_info.get('min', 0) + elif p.dialogic_type == resource.ValueType.DECIBEL: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/Number.tscn").instantiate() + editor_node.use_decibel_mode() + elif p.dialogic_type == resource.ValueType.FIXED_OPTION_SELECTOR: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/OptionSelector.tscn").instantiate() + editor_node.options = p.display_info.get('selector_options', []) + editor_node.disabled = p.display_info.get('disabled', false) + editor_node.symbol_only = p.display_info.get('symbol_only', false) + + elif p.dialogic_type == resource.ValueType.VECTOR2: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/Vector2.tscn").instantiate() + + elif p.dialogic_type == resource.ValueType.STRING_ARRAY: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/Array.tscn").instantiate() + + elif p.dialogic_type == resource.ValueType.LABEL: + editor_node = Label.new() + editor_node.text = p.display_info.text + editor_node.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + editor_node.set('custom_colors/font_color', Color("#7b7b7b")) + editor_node.add_theme_color_override('font_color', resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.8)) + elif p.dialogic_type == resource.ValueType.BUTTON: + editor_node = Button.new() + editor_node.text = p.display_info.text + if typeof(p.display_info.icon) == TYPE_ARRAY: + editor_node.icon = callv('get_theme_icon', p.display_info.icon) + else: + editor_node.icon = p.display_info.icon + editor_node.flat = true + editor_node.custom_minimum_size.x = 30*DialogicUtil.get_editor_scale() + editor_node.pressed.connect(p.display_info.callable) + ## CUSTOM + elif p.dialogic_type == resource.ValueType.CUSTOM: + if p.display_info.has('path'): + editor_node = load(p.display_info.path).instantiate() + + ## ELSE + else: + editor_node = Label.new() + editor_node.text = p.name + editor_node.add_theme_color_override('font_color', resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.8)) + + ### -------------------------------------------------------------------- + ### 2. ADD IT TO THE RIGHT PLACE (HEADER/BODY) + var location :Control = %HeaderContent + if p.location == 1: + location = current_body_container + location.add_child(editor_node) + + ### -------------------------------------------------------------------- + ### 3. FILL THE NEW NODE WITH INFORMATION AND LISTEN TO CHANGES + field_list[-1]['node'] = editor_node + if "event_resource" in editor_node: + editor_node.event_resource = resource + if 'property_name' in editor_node: + editor_node.property_name = p.name + field_list[-1]['property'] = p.name + if editor_node.has_method('set_value'): + editor_node.set_value(resource.get(p.name)) + if editor_node.has_signal('value_changed'): + editor_node.value_changed.connect(set_property) + editor_node.tooltip_text = p.display_info.get('tooltip', '') + var left_label :Label = null + var right_label :Label = null + if !p.get('left_text', '').is_empty(): + left_label = Label.new() + left_label.text = p.get('left_text') + left_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + left_label.add_theme_color_override('font_color', resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.8)) + location.add_child(left_label) + location.move_child(left_label, editor_node.get_index()) + if !p.get('right_text', '').is_empty(): + right_label = Label.new() + right_label.text = p.get('right_text') + right_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + right_label.add_theme_color_override('font_color', resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.8)) + location.add_child(right_label) + location.move_child(right_label, editor_node.get_index()+1) + + if p.has('condition'): + field_list[-1]['condition'] = p.condition + if left_label: + field_list.append({'node': left_label, 'condition':p.condition, 'location':p.location}) + if right_label: + field_list.append({'node': right_label, 'condition':p.condition, 'location':p.location}) + + ### -------------------------------------------------------------------- + ### 4. GETTING THE PATH OF THE FIELD WE WANT TO FOCUS (in case we want) + if resource.created_by_button and p.display_info.get('autofocus', false) and editor_node.has_method('take_autofocus'): + editor_node.call_deferred('take_autofocus') + + if build_body: +# has_body_content = true + if current_body_container.get_child_count() == 0: +# has_body_content = false + expanded = false + body_container.visible = false + + recalculate_field_visibility() + + +func recalculate_field_visibility() -> void: + has_any_enabled_body_content = false + for p in field_list: + if !p.has('condition') or p.condition.is_empty(): + if p.node != null: + p.node.show() + if p.location == 1: + has_any_enabled_body_content = true + else: + var expr := Expression.new() + expr.parse(p.condition) + if expr.execute([], resource): + if p.node != null: + p.node.show() + if p.location == 1: + has_any_enabled_body_content = true + else: + if p.node != null: + p.node.hide() + if expr.has_execute_failed(): + printerr("[Dialogic] Failed executing visibility condition for '",p.get('property', 'unnamed'),"': " + expr.get_error_text()) + %ExpandButton.visible = has_any_enabled_body_content + + +func set_property(property_name:String, value:Variant) -> void: + resource.set(property_name, value) + content_changed.emit() + if end_node: + end_node.parent_node_changed() + + +func _on_resource_ui_update_needed() -> void: + for node_info in field_list: + if node_info.node.has_method('set_value'): + node_info.node.set_value(resource.get(node_info.property)) + + +func _update_color() -> void: + if resource.dialogic_color_name != '': + %IconPanel.self_modulate = DialogicUtil.get_color(resource.dialogic_color_name) + + +######################## OVERRIDES ############################################# +################################################################################ + +func _ready(): + resized.connect(get_parent().get_parent().queue_redraw) + + ## DO SOME STYLING + var _scale := DialogicUtil.get_editor_scale() + $PanelContainer.self_modulate = get_theme_color("accent_color", "Editor") + warning.texture = get_theme_icon("NodeWarning", "EditorIcons") + warning.size = Vector2(16 * _scale, 16 * _scale) + warning.position = Vector2(-5 * _scale, -10 * _scale) + + indent_size = indent_size * DialogicUtil.get_editor_scale() + + %ExpandButton.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") + %ExpandButton.modulate = get_theme_color("readonly_color", "Editor") + + if resource: + if resource.event_name: + %TitleLabel.add_theme_color_override("font_color", resource.event_color.lightened(0.4)) + %TitleLabel.add_theme_font_override("font", get_theme_font("title", "EditorFonts")) + %TitleLabel.text = resource.event_name + %IconPanel.tooltip_text = resource.event_name + if resource._get_icon() != null: + _set_event_icon(resource._get_icon()) + resource.ui_update_needed.connect(_on_resource_ui_update_needed) + resource.ui_update_warning.connect(set_warning) + + %IconPanel.self_modulate = resource.event_color + + _on_ExpandButton_toggled(resource.expand_by_default) + + set_focus_mode(1) # Allowing this node to grab focus + + # signals + # TODO godot4 react to changes of the colors, the signal was removed + #ProjectSettings.project_settings_changed.connect(_update_color) + + # Separation on the header + %Header.add_theme_constant_override("custom_constants/separation", 5 * _scale) + + content_changed.connect(recalculate_field_visibility) + +# _on_Indent_visibility_changed() + %CollapseButton.toggled.connect(toggle_collapse) + %CollapseButton.icon = get_theme_icon("Collapse", "EditorIcons") + %CollapseButton.hide() + visual_deselect() + + +func _on_ExpandButton_toggled(button_pressed:bool) -> void: + if button_pressed and !body_was_build: + build_editor(false, true) + %ExpandButton.set_pressed_no_signal(button_pressed) + if button_pressed: + %ExpandButton.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") + else: + %ExpandButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") + expanded = button_pressed + body_container.visible = button_pressed + body_container.add_theme_constant_override("margin_left", icon_size*DialogicUtil.get_editor_scale()) + + +func _on_EventNode_gui_input(event:InputEvent) -> void: + if event is InputEventMouseButton and event.is_pressed() and event.button_index == 1: + grab_focus() # Grab focus to avoid copy pasting text or events + if event.double_click: + if has_any_enabled_body_content: + _on_ExpandButton_toggled(!expanded) + # For opening the context menu + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: + var popup :PopupMenu = get_parent().get_parent().get_node('EventPopupMenu') + popup.current_event = self + popup.popup_on_parent(Rect2(get_global_mouse_position(),Vector2())) + if resource.help_page_path == "": + popup.set_item_disabled(0, true) + else: + popup.set_item_disabled(0, false) diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.tscn b/addons/dialogic/Editor/Events/EventBlock/event_block.tscn new file mode 100644 index 0000000000000000000000000000000000000000..ae19f0c7e816a3607e18af5c0040a106205da305 --- /dev/null +++ b/addons/dialogic/Editor/Events/EventBlock/event_block.tscn @@ -0,0 +1,140 @@ +[gd_scene load_steps=8 format=3 uid="uid://bwaxj1n401fp4"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/EventBlock/event_block.gd" id="1"] +[ext_resource type="StyleBox" uid="uid://cl75ikyq2is7c" path="res://addons/dialogic/Editor/Events/styles/unselected_stylebox.tres" id="2_axj84"] +[ext_resource type="Texture2D" uid="uid://dybg3l5pwetne" path="res://addons/dialogic/Editor/Images/plugin-icon.svg" id="6"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_otutu"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 + +[sub_resource type="Image" id="Image_tmsys"] +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_wps7w"] +image = SubResource("Image_tmsys") + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ee4ub"] + +[node name="EventNode" type="MarginContainer"] +anchors_preset = 10 +anchor_right = 1.0 +grow_horizontal = 2 +size_flags_horizontal = 3 +size_flags_vertical = 9 +focus_mode = 1 +script = ExtResource("1") + +[node name="PanelContainer" type="PanelContainer" parent="."] +self_modulate = Color(0, 0, 0, 1) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 +theme_override_styles/panel = ExtResource("2_axj84") + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Header" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="IconPanel" type="Panel" parent="PanelContainer/VBoxContainer/Header"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +mouse_filter = 1 +mouse_default_cursor_shape = 6 +theme_override_styles/panel = SubResource("StyleBoxFlat_otutu") + +[node name="IconTexture" type="TextureRect" parent="PanelContainer/VBoxContainer/Header/IconPanel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 0 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +texture = ExtResource("6") +expand_mode = 1 +stretch_mode = 5 + +[node name="Warning" type="TextureRect" parent="PanelContainer/VBoxContainer/Header/IconPanel"] +unique_name_in_owner = true +visible = false +layout_mode = 0 +offset_left = -5.0 +offset_top = -10.0 +offset_right = 11.0 +offset_bottom = 6.0 +texture = SubResource("ImageTexture_wps7w") +stretch_mode = 5 + +[node name="TitleLabel" type="Label" parent="PanelContainer/VBoxContainer/Header"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +vertical_alignment = 1 + +[node name="VSeparator" type="VSeparator" parent="PanelContainer/VBoxContainer/Header"] +visible = false +custom_minimum_size = Vector2(0, 20) +layout_mode = 2 +size_flags_vertical = 4 + +[node name="HeaderContent" type="HBoxContainer" parent="PanelContainer/VBoxContainer/Header"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="ExpandButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] +unique_name_in_owner = true +modulate = Color(0, 0, 0, 1) +layout_mode = 2 +size_flags_horizontal = 0 +tooltip_text = "Fold/Unfold Settings" +theme_override_styles/focus = SubResource("StyleBoxEmpty_ee4ub") +toggle_mode = true +icon = SubResource("ImageTexture_wps7w") +flat = true + +[node name="CollapseButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 10 +tooltip_text = "Collapse Contained Events" +toggle_mode = true +icon = SubResource("ImageTexture_wps7w") +flat = true + +[node name="Body" type="MarginContainer" parent="PanelContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 4 + +[node name="BodyContent" type="VBoxContainer" parent="PanelContainer/VBoxContainer/Body"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 + +[connection signal="gui_input" from="." to="." method="_on_EventNode_gui_input"] +[connection signal="toggled" from="PanelContainer/VBoxContainer/Header/ExpandButton" to="." method="_on_ExpandButton_toggled"] diff --git a/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd b/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd new file mode 100644 index 0000000000000000000000000000000000000000..120c88041fd799fef6f915b31a6dbf360e9d3200 --- /dev/null +++ b/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd @@ -0,0 +1,19 @@ +@tool +extends PopupMenu + +var current_event : Node = null + +func _ready(): + clear() + add_icon_item(get_theme_icon("Help", "EditorIcons"), "Documentation") + add_separator() + add_icon_item(get_theme_icon("ArrowUp", "EditorIcons"), "Move up") + add_icon_item(get_theme_icon("ArrowDown", "EditorIcons"), "Move down") + add_separator() + add_icon_item(get_theme_icon("Remove", "EditorIcons"), "Delete") + + var menu_background := StyleBoxFlat.new() + menu_background.bg_color = get_parent().get_theme_color("base_color", "Editor") + add_theme_stylebox_override('panel', menu_background) + add_theme_stylebox_override('hover', get_theme_stylebox("FocusViewport", "EditorStyles")) + add_theme_color_override('font_color_hover', get_parent().get_theme_color("accent_color", "Editor")) diff --git a/addons/dialogic/Editor/Events/Fields/Array.gd b/addons/dialogic/Editor/Events/Fields/Array.gd new file mode 100644 index 0000000000000000000000000000000000000000..57f5131c84ea97e41d2b4d7c4851f3d19f195302 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Array.gd @@ -0,0 +1,49 @@ +@tool +extends VBoxContainer + +## Event block field for editing arrays. + +signal value_changed +var property_name : String + +const ArrayValue = "res://addons/dialogic/Editor/Events/Fields/ArrayValue.tscn" + +func _ready(): + %Add.icon = get_theme_icon("Add", "EditorIcons") + +func set_value(value:Array) -> void: + for child in %Values.get_children(): + child.queue_free() + + for item in value: + var x = load(ArrayValue).instantiate() + %Values.add_child(x) + x.set_value(item) + x.value_changed.connect(recalculate_values) + + +func _on_value_changed(value:Variant) -> void: + emit_signal("value_changed", property_name, value) + + +func recalculate_values() -> void: + var arr := [] + for child in %Values.get_children(): + if !child.is_queued_for_deletion(): + arr.append(child.get_value()) + _on_value_changed(arr) + + +func _on_AddButton_pressed() -> void: + var x :Control = load(ArrayValue).instantiate() + %Values.add_child(x) + x.set_value("") + x.value_changed.connect(recalculate_values) + recalculate_values() + + +## Overridable +func set_left_text(value:String) -> void: + %LeftText.text = str(value) + %LeftText.visible = value.is_empty() + diff --git a/addons/dialogic/Editor/Events/Fields/Array.tscn b/addons/dialogic/Editor/Events/Fields/Array.tscn new file mode 100644 index 0000000000000000000000000000000000000000..930184e152b288d788a88670b44de8607a486cec --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Array.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=3 format=3 uid="uid://btmy7ageqpyq1"] + +[ext_resource type="PackedScene" uid="uid://ch4j2lesn1sis" path="res://addons/dialogic/Editor/Events/Fields/ArrayValue.tscn" id="1"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Array.gd" id="2"] + +[node name="Array" type="VBoxContainer"] +script = ExtResource("2") + +[node name="Editing" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +alignment = 2 + +[node name="LeftText" type="Label" parent="Editing"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Add" type="Button" parent="Editing"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Values" type="VBoxContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value" parent="Values" instance=ExtResource("1")] +layout_mode = 2 + +[node name="Value2" parent="Values" instance=ExtResource("1")] +layout_mode = 2 + +[connection signal="pressed" from="Editing/Add" to="." method="_on_AddButton_pressed"] diff --git a/addons/dialogic/Editor/Events/Fields/ArrayValue.gd b/addons/dialogic/Editor/Events/Fields/ArrayValue.gd new file mode 100644 index 0000000000000000000000000000000000000000..c8b35092d2402221a6a54c728c25bd0a633fd063 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/ArrayValue.gd @@ -0,0 +1,27 @@ +@tool +extends HBoxContainer + +## Event block field part for the Array field. + +signal value_changed() + + +func set_value(value:String): + $Value.text = str(value) + + +func get_value() -> String: + return $Value.text + + +func _ready() -> void: + $Delete.icon = get_theme_icon("Remove", "EditorIcons") + + +func _on_Delete_pressed() -> void: + queue_free() + value_changed.emit() + + +func _on_Value_text_changed(new_text:String) -> void: + value_changed.emit() diff --git a/addons/dialogic/Editor/Events/Fields/ArrayValue.tscn b/addons/dialogic/Editor/Events/Fields/ArrayValue.tscn new file mode 100644 index 0000000000000000000000000000000000000000..bf2b2215ff7ef2e30e8143c458f0c21f546b79ce --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/ArrayValue.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=5 format=3 uid="uid://ch4j2lesn1sis"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/ArrayValue.gd" id="1"] +[ext_resource type="Theme" uid="uid://d3g4i4dshtdpu" path="res://addons/dialogic/Editor/Events/styles/InputFieldsStyle.tres" id="2"] + +[sub_resource type="Image" id="Image_ov00m"] +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_oe0il"] +image = SubResource("Image_ov00m") + +[node name="Value" type="HBoxContainer"] +theme = ExtResource("2") +script = ExtResource("1") + +[node name="Value" type="LineEdit" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +expand_to_text_length = true + +[node name="Delete" type="Button" parent="."] +layout_mode = 2 +icon = SubResource("ImageTexture_oe0il") + +[connection signal="text_changed" from="Value" to="." method="_on_Value_text_changed"] +[connection signal="pressed" from="Delete" to="." method="_on_Delete_pressed"] diff --git a/addons/dialogic/Editor/Events/Fields/Bool.gd b/addons/dialogic/Editor/Events/Fields/Bool.gd new file mode 100644 index 0000000000000000000000000000000000000000..8f352f6e01edfdeff86bed9e5f036963fb8f9ad1 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Bool.gd @@ -0,0 +1,19 @@ +@tool +extends CheckButton + +## Event block field for boolean values. + +signal value_changed +var property_name : String + + +func _ready() -> void: + toggled.connect(_on_value_changed) + + +func set_value(value:bool) -> void: + button_pressed = value + + +func _on_value_changed(value:bool) -> void: + value_changed.emit(property_name, value) diff --git a/addons/dialogic/Editor/Events/Fields/Bool.tscn b/addons/dialogic/Editor/Events/Fields/Bool.tscn new file mode 100644 index 0000000000000000000000000000000000000000..93201b92513e1c849f6612964a0035b09cbae2b7 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Bool.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dm5hxmhyyxgq"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Bool.gd" id="1"] + +[node name="Bool" type="CheckButton"] +script = ExtResource("1") diff --git a/addons/dialogic/Editor/Events/Fields/ComplexPicker.gd b/addons/dialogic/Editor/Events/Fields/ComplexPicker.gd new file mode 100644 index 0000000000000000000000000000000000000000..c920b7d815095d1fc00ea8e2c0b50267cdd46082 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/ComplexPicker.gd @@ -0,0 +1,264 @@ +@tool +extends Control + +## Event block field for resources/options. + +# this signal is on all event parts and informs the event that a change happened. +signal value_changed(property_name, value) +var property_name : String +var event_resource : DialogicEvent = null + +### SETTINGS FOR THE RESOURCE PICKER +@export var placeholder_text : String = "Select Resource" +var collapse_when_empty := false +var file_extension : String = "" +var get_suggestions_func : Callable = get_default_suggestions +var empty_text : String = "" +@export var enable_pretty_name : bool = false +@export var fit_text_length : bool = true + +var resource_icon : Texture = null: + get: + return resource_icon + set(new_icon): + resource_icon = new_icon + %Icon.texture = new_icon + +## STORING VALUE AND REFERENCE TO RESOURCE +var current_value :Variant # Dynamic +var editor_reference + +var current_selected = 0 + +################################################################################ +## BASIC EVENT PART FUNCTIONS +################################################################################ + +func set_value(value:Variant, text : String = '') -> void: + %Search.show() + if value == null or value.is_empty(): + %Search.text = empty_text + if collapse_when_empty: + %Search.hide() + elif file_extension != "" and file_extension != ".dch" and file_extension != ".dtl": + %Search.text = value.resource_path + %Search.tooltip_text = value.resource_path + elif value: + if enable_pretty_name: + %Search.text = DialogicUtil.pretty_name(value) + else: + %Search.text = value + else: + %Search.text = empty_text + if text: + %Search.text = text + + current_value = value + + +func changed_to_empty() -> void: + if file_extension != "" && file_extension != ".dch": + emit_signal("value_changed", property_name, null) + else: + emit_signal("value_changed", property_name, "") + + +################################################################################ +## BASIC +################################################################################ +func _ready(): + %Focus.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) + %Search.text_changed.connect(_on_Search_text_changed) + %Search.text_submitted.connect(_on_Search_text_entered) + %SelectButton.icon = get_theme_icon("Collapse", "EditorIcons") + %Search.placeholder_text = placeholder_text + %Search.expand_to_text_length = fit_text_length + %Suggestions.add_theme_stylebox_override('bg', load("res://addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres")) + %Suggestions.hide() + %Suggestions.item_selected.connect(suggestion_selected) + %Suggestions.item_clicked.connect(suggestion_selected) + if resource_icon == null: + self.resource_icon = null + + editor_reference = find_parent('EditorView') + + +func _exit_tree(): + # Explicitly free any open cache resources on close, so we don't get leaked resource errors on shutdown + event_resource = null + + +func take_autofocus(): + %Search.grab_focus() + +################################################################################ +## SEARCH & SUGGESTION POPUP +################################################################################ +func _on_Search_text_entered(new_text:String) -> void: + if %Suggestions.get_item_count(): + if %Suggestions.is_anything_selected(): + suggestion_selected(%Suggestions.get_selected_items()[0]) + else: + suggestion_selected(0) + else: + changed_to_empty() + + +func _on_Search_text_changed(new_text:String, just_update:bool = false) -> void: + %Suggestions.clear() + + if new_text == "" and !just_update: + changed_to_empty() + else: + %Search.show() + + var suggestions :Dictionary = get_suggestions_func.call(new_text) + + var line_length:int = 0 + var idx:int = 0 + for element in suggestions: + if new_text.is_empty() or new_text.to_lower() in element.to_lower() or new_text.to_lower() in str(suggestions[element].value).to_lower() or new_text.to_lower() in suggestions[element].get('tooltip', '').to_lower(): + line_length = max(get_theme_font('font', 'Label').get_string_size(element, HORIZONTAL_ALIGNMENT_LEFT, -1, get_theme_font_size("font_size", 'Label')).x+80, line_length) + %Suggestions.add_item(element) + if suggestions[element].has('icon'): + %Suggestions.set_item_icon(idx, suggestions[element].icon) + elif suggestions[element].has('editor_icon'): + %Suggestions.set_item_icon(idx, get_theme_icon(suggestions[element].editor_icon[0],suggestions[element].editor_icon[1])) + + %Suggestions.set_item_tooltip(idx, suggestions[element].get('tooltip', '')) + %Suggestions.set_item_metadata(idx, suggestions[element].value) + idx += 1 + + if not %Suggestions.visible: + %Suggestions.show() + %Suggestions.global_position = $PanelContainer.global_position+Vector2(0,1)*$PanelContainer.size.y + #%Suggestions.position = Vector2() + %Suggestions.size.x = max(%Search.size.x, line_length) + %Suggestions.size.y = min(%Suggestions.get_item_count()*35*DialogicUtil.get_editor_scale(), 200*DialogicUtil.get_editor_scale()) + if %Suggestions.get_item_count(): + %Suggestions.select(0) + current_selected = 0 + else: + current_selected = -1 + %Search.grab_focus() + + +func get_default_suggestions(input:String) -> Dictionary: + if file_extension.is_empty(): return {'Nothing found!':{'value':''}} + var suggestions: Dictionary = {} + if file_extension == ".dch": + suggestions['(No one)'] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + + for resource in editor_reference.character_directory.keys(): + suggestions[resource] = {'value': resource, 'tooltip': editor_reference.character_directory[resource]['full_path']} + else: + var resources: Array = DialogicUtil.list_resources_of_type(file_extension) + + for resource in resources: + suggestions[resource] = {'value':resource, 'tooltip':resource} + + return suggestions + + +func suggestion_selected(index : int, position:=Vector2(), button_index:=MOUSE_BUTTON_LEFT) -> void: + if button_index != MOUSE_BUTTON_LEFT: + return + if %Suggestions.is_item_disabled(index): + return + + %Search.text = %Suggestions.get_item_text(index) + + if %Suggestions.get_item_metadata(index) == null: + current_value = null + + # if this is a resource, then load it instead of assigning the string: + elif file_extension != "" and file_extension != ".dch" and file_extension != ".dtl": + var file = load(%Suggestions.get_item_metadata(index)) + current_value = file + else: + current_value = %Suggestions.get_item_metadata(index) + + hide_suggestions() + + %Search.grab_focus() + emit_signal("value_changed", property_name, current_value) + +func _input(event:InputEvent): + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + if %Suggestions.visible: + if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()) and \ + !%SelectButton.get_global_rect().has_point(get_global_mouse_position()): + hide_suggestions() + + +func hide_suggestions() -> void: + %SelectButton.set_pressed_no_signal(false) + %Suggestions.hide() + if !current_value and collapse_when_empty: + %Search.hide() + + +func _on_SelectButton_toggled(button_pressed:bool) -> void: + if button_pressed: + _on_Search_text_changed('', true) + else: + hide_suggestions() + +func _on_focus_entered(): + %Search.grab_focus() + + +func _on_search_gui_input(event): + if event is InputEventKey and (event.keycode == KEY_DOWN or event.keycode == KEY_UP) and event.pressed: + if !%Suggestions.visible: + _on_Search_text_changed('', true) + current_selected = -1 + if event.keycode == KEY_DOWN: + current_selected = wrapi(current_selected+1, 0, %Suggestions.item_count) + if event.keycode == KEY_UP: + current_selected = wrapi(current_selected-1, 0, %Suggestions.item_count) + %Suggestions.select(current_selected) + %Suggestions.ensure_current_is_visible() + + +func _on_search_focus_entered(): + if %Search.text == "" or current_value == null or (typeof(current_value) == TYPE_STRING and current_value.is_empty()): + _on_Search_text_changed("") + %Search.call_deferred('select_all') + %Focus.show() + + +func _on_search_focus_exited(): + %Focus.hide() + if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()): + hide_suggestions() + +################################################################################ +## DRAG AND DROP +################################################################################ + +func _can_drop_data(position, data) -> bool: + if typeof(data) == TYPE_DICTIONARY and data.has('files') and len(data.files) == 1: + if file_extension: + if data.files[0].ends_with(file_extension): + return true + else: + return false + return false + +func _drop_data(position, data) -> void: + if data.files[0].ends_with('dch'): + for character in editor_reference.character_directory.keys(): + if editor_reference.character_directory[character]["full_path"] == data.files[0]: + set_value(character) + break + elif data.files[0].ends_with('dtl'): + for timeline in editor_reference.timeline_directory.keys(): + if editor_reference.timeline_directory[timeline] == data.files[0]: + set_value(timeline) + break + else: + var file = load(data.files[0]) + set_value(file) + emit_signal("value_changed", property_name, file) + diff --git a/addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn b/addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn new file mode 100644 index 0000000000000000000000000000000000000000..c05473d4ae8f65f996989e3353e0d5db52f01822 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn @@ -0,0 +1,134 @@ +[gd_scene load_steps=7 format=3 uid="uid://dpwhshre1n4t6"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.gd" id="1"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tmt5n"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_vennf"] + +[sub_resource type="Image" id="Image_tmsys"] +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_wps7w"] +image = SubResource("Image_tmsys") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_j1gic"] +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="ComplexPicker" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -2.0 +offset_top = -2.0 +offset_right = -1005.0 +offset_bottom = -622.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_mode = 2 +script = ExtResource("1") +placeholder_text = "" + +[node name="PanelContainer" type="MarginContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 0 +theme_override_constants/margin_top = 0 +theme_override_constants/margin_right = 0 +theme_override_constants/margin_bottom = 0 + +[node name="BG" type="Panel" parent="PanelContainer"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 2 +theme_type_variation = &"DialogicEventEdit" +metadata/_edit_use_anchors_ = true + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 2 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer"] +layout_mode = 2 + +[node name="Icon" type="TextureRect" parent="PanelContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 4 +mouse_filter = 2 +stretch_mode = 5 + +[node name="Search" type="LineEdit" parent="PanelContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +focus_neighbor_bottom = NodePath("Suggestions") +focus_mode = 1 +mouse_filter = 1 +theme_override_styles/normal = SubResource("StyleBoxEmpty_tmt5n") +theme_override_styles/focus = SubResource("StyleBoxEmpty_vennf") +expand_to_text_length = true +flat = true +caret_blink = true + +[node name="Suggestions" type="ItemList" parent="PanelContainer/MarginContainer/HBoxContainer/Search"] +unique_name_in_owner = true +visible = false +top_level = true +custom_minimum_size = Vector2(-1086, 0) +layout_mode = 0 +offset_left = -5.0 +offset_top = 36.0 +offset_right = 195.0 +offset_bottom = 71.0 +size_flags_vertical = 0 +auto_translate = false +focus_neighbor_top = NodePath("..") +max_text_lines = 3 +item_count = 1 +item_0/text = "Hello" + +[node name="SelectButton" type="Button" parent="PanelContainer/MarginContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +toggle_mode = true +shortcut_in_tooltip = false +icon = SubResource("ImageTexture_wps7w") +flat = true + +[node name="Focus" type="Panel" parent="PanelContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_j1gic") +metadata/_edit_use_anchors_ = true + +[connection signal="focus_entered" from="." to="." method="_on_focus_entered"] +[connection signal="focus_entered" from="PanelContainer/MarginContainer/HBoxContainer/Search" to="." method="_on_search_focus_entered"] +[connection signal="focus_exited" from="PanelContainer/MarginContainer/HBoxContainer/Search" to="." method="_on_search_focus_exited"] +[connection signal="gui_input" from="PanelContainer/MarginContainer/HBoxContainer/Search" to="." method="_on_search_gui_input"] +[connection signal="gui_input" from="PanelContainer/MarginContainer/HBoxContainer/Search/Suggestions" to="." method="_on_suggestions_gui_input"] +[connection signal="toggled" from="PanelContainer/MarginContainer/HBoxContainer/SelectButton" to="." method="_on_SelectButton_toggled"] diff --git a/addons/dialogic/Editor/Events/Fields/ConditionPicker.gd b/addons/dialogic/Editor/Events/Fields/ConditionPicker.gd new file mode 100644 index 0000000000000000000000000000000000000000..9f989a68b54ba7e1919ba94c476f6c8fafce0cdf --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/ConditionPicker.gd @@ -0,0 +1,227 @@ +@tool +extends Control + +## Event block field for displaying conditions in either a simple or complex way. + +signal value_changed +var property_name : String +var event_resource : DialogicEvent = null + +var _current_value1 :Variant = "" +var _current_value2 :Variant = "" + +func _ready() -> void: + + for i in [%Value1Type, %Value2Type]: + i.options = [{ + 'label': 'String', + 'icon': ["String", "EditorIcons"], + 'value': 0 + },{ + 'label': 'Number', + 'icon': ["float", "EditorIcons"], + 'value': 1 + },{ + 'label': 'Variable', + 'icon': load("res://addons/dialogic/Editor/Images/Pieces/variable.svg"), + 'value': 2 + },{ + 'label': 'Expression', + 'icon': ["Variant", "EditorIcons"], + 'value': 3 + } +# ,{ +# 'label': 'Random Number', +# 'icon': ["RandomNumberGenerator", "EditorIcons"], +# 'value': 4 +# } + ] + i.symbol_only = true + i.value_changed.connect(value_type_changed.bind(i.name)) + i.value_changed.connect(something_changed) + i.tooltip_text = "Change type" + + + for i in [%Value1Variable, %Value2Variable]: + i.get_suggestions_func = get_variable_suggestions + i.value_changed.connect(something_changed) + + %Value1Number.value_changed.connect(something_changed) + %Value2Number.value_changed.connect(something_changed) + %Value1Text.value_changed.connect(something_changed) + %Value2Text.value_changed.connect(something_changed) + + %ToggleComplex.icon = get_theme_icon("Enum", "EditorIcons") + + %Operator.value_changed.connect(something_changed) + %Operator.options = [ + {'label': '==', 'value': '=='}, + {'label': '>', 'value': '>'}, + {'label': '<', 'value': '<'}, + {'label': '<=', 'value': '<='}, + {'label': '>=', 'value': '>='}, + {'label': '!=', 'value': '!='} + ] + + +func set_value(value:String) -> void: + var too_complex := is_too_complex(value) + %ToggleComplex.disabled = too_complex + %ToggleComplex.button_pressed = too_complex + %ComplexEditor.visible = too_complex + %SimpleEditor.visible = !too_complex + %ComplexEditor.text = value + if not too_complex: + load_simple_editor(value) + + +func load_simple_editor(condition_string:String) -> void: + var data := complex2simple(condition_string) + %Value1Type.set_value(get_value_type(data[0], 2)) + _current_value1 = data[0] + value_type_changed('', get_value_type(data[0], 2), 'Value1') + %Operator.set_value(data[1].strip_edges()) + %Value2Type.set_value(get_value_type(data[2], 0)) + _current_value2 = data[2] + value_type_changed('', get_value_type(data[2], 0), 'Value2') + + +func value_type_changed(property:String, value_type:int, value_name:String) -> void: + value_name = value_name.trim_suffix('Type') + get_node('%'+value_name+'Variable').hide() + get_node('%'+value_name+'Text').hide() + get_node('%'+value_name+'Number').hide() + var current_val :Variant = "" + if '1' in value_name: + current_val = _current_value1 + else: + current_val = _current_value2 + match value_type: + 0: + get_node('%'+value_name+'Text').show() + get_node('%'+value_name+'Text').set_value(trim_value(current_val, value_type)) + 1: + get_node('%'+value_name+'Number').show() + get_node('%'+value_name+'Number').set_value(float(current_val.strip_edges())) + 2: + get_node('%'+value_name+'Variable').show() + get_node('%'+value_name+'Variable').set_value(trim_value(current_val, value_type)) + 3: + get_node('%'+value_name+'Text').show() + get_node('%'+value_name+'Text').set_value(str(current_val)) + + + +func get_value_type(value:String, default:int) -> int: + value = value.strip_edges() + if value.begins_with('"') and value.ends_with('"') and value.count('"')-value.count('\\"') == 2: + return 0 + elif value.begins_with('{') and value.ends_with('}') and value.count('{') == 1: + return 2 + else: + if value.is_empty(): + return default + if value.is_valid_float(): + return 1 + else: + return 3 + + +func prep_value(value:Variant, value_type:int) -> String: + if value != null: value = str(value) + else: value = "" + value = value.strip_edges() + match value_type: + 0: return '"'+value.replace('"', '\\"')+'"' + 2: return '{'+value+'}' + _: return value + + +func trim_value(value:Variant, value_type:int) -> String: + value = value.strip_edges() + match value_type: + 0: return value.trim_prefix('"').trim_suffix('"').replace('\\"', '"') + 2: return value.trim_prefix('{').trim_suffix('}') + _: return value + + +func something_changed(fake_arg1=null, fake_arg2 = null): + if %ComplexEditor.visible: + value_changed.emit(property_name, %ComplexEditor.text) + + else: + match %Value1Type.current_value: + 0: _current_value1 = prep_value(%Value1Text.text, %Value1Type.current_value) + 1: _current_value1 = str(%Value1Number.get_value()) + 2: _current_value1 = prep_value(%Value1Variable.current_value, %Value1Type.current_value) + _: _current_value1 = prep_value(%Value1Text.text, %Value1Type.current_value) + + match %Value2Type.current_value: + 0: _current_value2 = prep_value(%Value2Text.text, %Value2Type.current_value) + 1: _current_value2 = str(%Value2Number.get_value()) + 2: _current_value2 = prep_value(%Value2Variable.current_value, %Value2Type.current_value) + _: _current_value2 = prep_value(%Value2Text.text, %Value2Type.current_value) + + if event_resource: + if not %Operator.text in ['==', '!='] and get_value_type(_current_value2, 0) == 0: + event_resource.ui_update_warning.emit("This operator doesn't work with strings.") + else: + event_resource.ui_update_warning.emit("") + + value_changed.emit(property_name, get_simple_condition()) + + +func is_too_complex(condition:String) -> bool: + return !(condition.is_empty() + or ' and ' in condition + or ' or ' in condition + or ' not ' in condition + or condition.count('==') != 1 + or condition.count('>') != 1 + or condition.count('<') != 1 + or condition.count('<=') != 1 + or condition.count('>=') != 1 + or condition.count('!=') != 1) + + +## Combines the info from the simple editor fields into a string condition +func get_simple_condition() -> String: + return _current_value1 +" "+ %Operator.text +" "+ _current_value2 + + +func complex2simple(condition:String) -> Array: + if is_too_complex(condition) or condition.strip_edges().is_empty(): + return ['', '==',''] + + for i in ['==', '!=', '<=', '<', '>', '>=']: + if i in condition: + var cond_split := Array(condition.split(i, false)) + return [cond_split[0], i, cond_split[1]] + + return ['', '==',''] + + +func _on_toggle_complex_toggled(button_pressed:bool) -> void: + if button_pressed: + %ComplexEditor.show() + %SimpleEditor.hide() + %ComplexEditor.text = get_simple_condition() + else: + if !is_too_complex(%ComplexEditor.text): + %ComplexEditor.hide() + %SimpleEditor.show() + load_simple_editor(%ComplexEditor.text) + + +func _on_complex_editor_text_changed(new_text:String) -> void: + %ToggleComplex.disabled = is_too_complex(%ComplexEditor.text) + something_changed() + + +func get_variable_suggestions(filter:String) -> Dictionary: + var suggestions := {} + var vars :Dictionary= ProjectSettings.get_setting('dialogic/variables', {}) + for var_path in DialogicUtil.list_variables(vars): + suggestions[var_path] = {'value':var_path, 'editor_icon':["ClassList", "EditorIcons"]} + return suggestions + diff --git a/addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn b/addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn new file mode 100644 index 0000000000000000000000000000000000000000..da4a21d5b7f1f52d5f0bf847f4e63a385f9bba5e --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn @@ -0,0 +1,91 @@ +[gd_scene load_steps=8 format=3 uid="uid://ir6334lqtuwt"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/ConditionPicker.gd" id="1_q5p62"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="1_rr7mq"] +[ext_resource type="PackedScene" uid="uid://d3bhehatwoio" path="res://addons/dialogic/Editor/Events/Fields/OptionSelector.tscn" id="4_27ir8"] +[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/Number.tscn" id="4_al48y"] +[ext_resource type="PackedScene" uid="uid://c0vkcehgjsjy" path="res://addons/dialogic/Editor/Events/Fields/SinglelineText.tscn" id="4_b5vlr"] + +[sub_resource type="Image" id="Image_tmsys"] +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_wps7w"] +image = SubResource("Image_tmsys") + +[node name="ConditionPicker" type="HBoxContainer"] +offset_right = 77.0 +offset_bottom = 31.0 +script = ExtResource("1_q5p62") + +[node name="SimpleEditor" type="HBoxContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value1Type" parent="SimpleEditor" instance=ExtResource("4_27ir8")] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Change type" +text = "" + +[node name="Value1Text" parent="SimpleEditor" instance=ExtResource("4_b5vlr")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value1Number" parent="SimpleEditor" instance=ExtResource("4_al48y")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value1Variable" parent="SimpleEditor" instance=ExtResource("1_rr7mq")] +unique_name_in_owner = true +layout_mode = 2 +placeholder_text = "Variable" + +[node name="Operator" parent="SimpleEditor" instance=ExtResource("4_27ir8")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value2Type" parent="SimpleEditor" instance=ExtResource("4_27ir8")] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Change type" +text = "" + +[node name="Value2Text" parent="SimpleEditor" instance=ExtResource("4_b5vlr")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value2Number" parent="SimpleEditor" instance=ExtResource("4_al48y")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value2Variable" parent="SimpleEditor" instance=ExtResource("1_rr7mq")] +unique_name_in_owner = true +layout_mode = 2 +placeholder_text = "Variable" + +[node name="ComplexEditor" type="LineEdit" parent="."] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(150, 0) +layout_mode = 2 +mouse_filter = 1 +theme_type_variation = &"DialogicEventEdit" +text = "VAR.Player.Health > 20 and VAR.Counter < 3 and randi()%3 == 2" +placeholder_text = "Enter condition" +expand_to_text_length = true + +[node name="ToggleComplex" type="Button" parent="."] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Use complex expression" +toggle_mode = true +icon = SubResource("ImageTexture_wps7w") + +[connection signal="text_changed" from="ComplexEditor" to="." method="_on_complex_editor_text_changed"] +[connection signal="toggled" from="ToggleComplex" to="." method="_on_toggle_complex_toggled"] diff --git a/addons/dialogic/Editor/Events/Fields/FilePicker.gd b/addons/dialogic/Editor/Events/Fields/FilePicker.gd new file mode 100644 index 0000000000000000000000000000000000000000..de2776d3a260eb49fb6a8b148bed2aa7ace30ac0 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/FilePicker.gd @@ -0,0 +1,84 @@ +@tool +extends Control + +## Event block field for selecting a file or directory. + +signal value_changed(property_name:String, value:String) +var property_name : String + +@export var file_filter := "" +@export var placeholder := "" +@export var file_mode : EditorFileDialog.FileMode = EditorFileDialog.FILE_MODE_OPEN_FILE +@export var resource_icon:Texture = null: + get: + return resource_icon + set(new_icon): + resource_icon = new_icon + %Icon.texture = new_icon + if new_icon == null: + %Field.theme_type_variation = "" + else: + %Field.theme_type_variation = "LineEditWithIcon" + +var max_text_length := 16 +var current_value : String +var hide_reset:bool = false + +func _ready() -> void: + $FocusStyle.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) + %OpenButton.icon = get_theme_icon("Folder", "EditorIcons") + %ClearButton.icon = get_theme_icon("Reload", "EditorIcons") + %OpenButton.button_down.connect(_on_OpenButton_pressed) + %ClearButton.button_up.connect(clear_path) + %ClearButton.visible = !hide_reset + %Field.set_drag_forwarding(Callable(), self._can_drop_data_fw, self._drop_data_fw) + %Field.placeholder_text = placeholder + + +func set_value(value:String) -> void: + current_value = value + if file_mode != EditorFileDialog.FILE_MODE_OPEN_DIR: + %Field.text = value.get_file() + if len(value.get_file()) > max_text_length: + %Field.custom_minimum_size.x = get_theme_font('font', 'Label').get_string_size(value.get_file()).x + %Field.expand_to_text_length = false + %Field.size.x = 0 + else: + %Field.custom_minimum_size.x = 0 + %Field.expand_to_text_length = true + %Field.tooltip_text = value + %ClearButton.visible = !value.is_empty() and !hide_reset + else: + %Field.text = value + + +func _on_OpenButton_pressed() -> void: + find_parent('EditorView').godot_file_dialog(_on_file_dialog_selected, file_filter, file_mode, "Open "+ property_name) + + +func _on_file_dialog_selected(path:String) -> void: + set_value(path) + emit_signal("value_changed", property_name, path) + + +func clear_path() -> void: + set_value("") + emit_signal("value_changed", property_name, "") + +func _can_drop_data_fw(at_position: Vector2, data: Variant) -> bool: + if typeof(data) == TYPE_DICTIONARY and data.has('files') and len(data.files) == 1: + if file_filter: + if '*.'+data.files[0].get_extension() in file_filter: + return true + else: return true + return false + +func _drop_data_fw(at_position: Vector2, data: Variant) -> void: + _on_file_dialog_selected(data.files[0]) + + +func _on_field_focus_entered(): + $FocusStyle.show() + +func _on_field_focus_exited(): + $FocusStyle.hide() diff --git a/addons/dialogic/Editor/Events/Fields/FilePicker.tscn b/addons/dialogic/Editor/Events/Fields/FilePicker.tscn new file mode 100644 index 0000000000000000000000000000000000000000..80c73aedcc4d0741b973ce69d818ef5faede4f91 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/FilePicker.tscn @@ -0,0 +1,87 @@ +[gd_scene load_steps=8 format=3 uid="uid://7mvxuaulctcq"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.gd" id="1_838yp"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tr837"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wq6bt"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6b7on"] + +[sub_resource type="Image" id="Image_pqmjp"] +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_fyskv"] +image = SubResource("Image_pqmjp") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ias3t"] +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="FilePicker" type="MarginContainer"] +offset_right = 160.0 +offset_bottom = 40.0 +theme_type_variation = &"DialogicEventEdit" +script = ExtResource("1_838yp") + +[node name="BG" type="PanelContainer" parent="."] +layout_mode = 2 +theme_type_variation = &"DialogicEventEdit" + +[node name="HBox" type="HBoxContainer" parent="BG"] +layout_mode = 2 +alignment = 2 + +[node name="Icon" type="TextureRect" parent="BG/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 2 + +[node name="Field" type="LineEdit" parent="BG/HBox"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 1 +theme_override_styles/normal = SubResource("StyleBoxEmpty_tr837") +theme_override_styles/focus = SubResource("StyleBoxEmpty_wq6bt") +theme_override_styles/read_only = SubResource("StyleBoxEmpty_6b7on") +expand_to_text_length = true + +[node name="OpenButton" type="Button" parent="BG/HBox"] +unique_name_in_owner = true +layout_mode = 2 +icon = SubResource("ImageTexture_fyskv") +flat = true + +[node name="ClearButton" type="Button" parent="BG/HBox"] +unique_name_in_owner = true +layout_mode = 2 +icon = SubResource("ImageTexture_fyskv") +flat = true + +[node name="FocusStyle" type="Panel" parent="."] +visible = false +layout_mode = 2 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ias3t") + +[connection signal="focus_entered" from="BG/HBox/Field" to="." method="_on_field_focus_entered"] +[connection signal="focus_exited" from="BG/HBox/Field" to="." method="_on_field_focus_exited"] +[connection signal="text_submitted" from="BG/HBox/Field" to="." method="_on_file_dialog_selected"] diff --git a/addons/dialogic/Editor/Events/Fields/Label.gd b/addons/dialogic/Editor/Events/Fields/Label.gd new file mode 100644 index 0000000000000000000000000000000000000000..b1625f1d220252b2f6200b14d493c5a10e199d1f --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Label.gd @@ -0,0 +1,11 @@ +@tool +extends Control + + +@export var text: String = "Hello World" + + +func _ready(): + $Label.text = text + $Label.set('custom_colors/font_color', Color("#7b7b7b")) + diff --git a/addons/dialogic/Editor/Events/Fields/Label.tscn b/addons/dialogic/Editor/Events/Fields/Label.tscn new file mode 100644 index 0000000000000000000000000000000000000000..6eae67bcc760798742701133fe5d857c48394a27 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Label.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/dialogic/Editor/Events/Fields/Label.gd" type="Script" id=1] + +[node name="Control" type="CenterContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 1 +script = ExtResource( 1 ) + +[node name="Label" type="Label" parent="."] +margin_left = 474.0 +margin_top = 293.0 +margin_right = 550.0 +margin_bottom = 307.0 +size_flags_horizontal = 3 +custom_colors/font_color = Color( 0.482353, 0.482353, 0.482353, 1 ) +text = "Hello World" diff --git a/addons/dialogic/Editor/Events/Fields/MultilineText.gd b/addons/dialogic/Editor/Events/Fields/MultilineText.gd new file mode 100644 index 0000000000000000000000000000000000000000..25c313c491e657076d9b33a836458a7b457bdc91 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/MultilineText.gd @@ -0,0 +1,60 @@ +@tool +extends CodeEdit + +## Event block field that allows entering multiline text (mainly text event). + +var property_name : String +signal value_changed + +func _ready() -> void: + text_changed.connect(_on_text_changed) + syntax_highlighter = load('res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd').new() + syntax_highlighter.mode = syntax_highlighter.Modes.TEXT_EVENT_ONLY + + +func _on_text_changed(value := "") -> void: + emit_signal("value_changed", property_name, text) + request_code_completion(true) + + +func set_value(value:Variant) -> void: + text = str(value) + + +func take_autofocus() -> void: + grab_focus() + + +################################################################################ +## AUTO COMPLETION +################################################################################ + +# Called if something was typed +func _request_code_completion(force:bool): + $CodeCompletionHelper.request_code_completion(force, self) + + +# Filters the list of all possible options, depending on what was typed +# Purpose of the different Kinds is explained in [_request_code_completion] +func _filter_code_completion_candidates(candidates:Array) -> Array: + return $CodeCompletionHelper.filter_code_completion_candidates(candidates, self) + + +# Called when code completion was activated +# Inserts the selected item +func _confirm_code_completion(replace:bool) -> void: + $CodeCompletionHelper.confirm_code_completion(replace, self) + + +################################################################################ +## SYMBOL CLICKING +################################################################################ + +# Performs an action (like opening a link) when a valid symbol was clicked +func _on_symbol_lookup(symbol, line, column): + $CodeCompletionHelper.symbol_lookup(symbol, line, column) + + +# Called to test if a symbol can be clicked +func _on_symbol_validate(symbol:String) -> void: + $CodeCompletionHelper.symbol_validate(symbol, self) diff --git a/addons/dialogic/Editor/Events/Fields/MultilineText.tscn b/addons/dialogic/Editor/Events/Fields/MultilineText.tscn new file mode 100644 index 0000000000000000000000000000000000000000..120afbe5e5038f16fdfd7dc6a1d4dee19b61cade --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/MultilineText.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=6 format=3 uid="uid://dyp7m2nvab1aj"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/MultilineText.gd" id="1"] +[ext_resource type="StyleBox" path="res://addons/dialogic/Editor/Events/styles/TextBackground.tres" id="1_dkxlh"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd" id="1_wj4ha"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd" id="3_mmga0"] + +[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_42qr1"] +script = ExtResource("1_wj4ha") + +[node name="MultilineText" type="CodeEdit"] +offset_right = 1152.0 +offset_bottom = 648.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_styles/normal = ExtResource("1_dkxlh") +wrap_mode = 1 +syntax_highlighter = SubResource("SyntaxHighlighter_42qr1") +scroll_fit_content_height = true +symbol_lookup_on_click = true +delimiter_strings = Array[String]([]) +code_completion_enabled = true +code_completion_prefixes = Array[String](["[", "{"]) +indent_automatic_prefixes = Array[String]([":", "{", "[", ")"]) +auto_brace_completion_enabled = true +auto_brace_completion_pairs = { +"[": "]", +"{": "}" +} +script = ExtResource("1") + +[node name="CodeCompletionHelper" type="Node" parent="."] +script = ExtResource("3_mmga0") +mode = 0 diff --git a/addons/dialogic/Editor/Events/Fields/Number.gd b/addons/dialogic/Editor/Events/Fields/Number.gd new file mode 100644 index 0000000000000000000000000000000000000000..3ebc70d89565ffe9f32b14518f274ac6c85c6ad6 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Number.gd @@ -0,0 +1,81 @@ +@tool +extends Control + +## Event block field for integers and floats. Improved version of the native spinbox. + +signal value_changed +var property_name : String + +@export var allow_string :bool = false +@export var step:float = 0.1 +@export var enforce_step:bool = true +@export var min:float = 0 +@export var max:float= 999 +@export var value = 0 +@export var suffix := "" + +func _ready(): + if $Value.text.is_empty(): + set_value(value) + $Spin.icon = get_theme_icon("updown", "SpinBox") + +func set_value(new_value) -> void: + _on_value_text_submitted(str(new_value)) + $Value.tooltip_text = tooltip_text + +func get_value(): + return value + +func use_timestamp_mode(): + step = 0.1 + suffix = ' sec' + max = 9999 #2.7 hours. Enough, or is more needed? + +func use_float_mode(): + step = 0.1 + suffix = "" + enforce_step = false + +func use_int_mode(): + step = 1 + suffix = "" + +func use_decibel_mode(): + max = 6 + suffix = "dB" + min = -80 + +func set_max_value(value): + max = value + +func set_min_value(value): + min = value + +func _on_spin_gui_input(event): + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + if event.position.y < size.y/2.0: + _on_value_text_submitted(str(value+step)) + else: + _on_value_text_submitted(str(value-step)) + + +func _on_value_text_submitted(new_text): + new_text = new_text.trim_suffix(suffix) + if new_text.is_valid_float(): + var temp:float = min(max(new_text.to_float(), min), max) + if !enforce_step or is_equal_approx(temp/step, round(temp/step)): + value = temp + else: + value = snapped(temp, step) + elif allow_string: + value = new_text + $Value.text = str(value)+suffix + value_changed.emit(property_name, value) + + +func _on_value_focus_exited(): + _on_value_text_submitted($Value.text) + + +func take_autofocus(): + $Value.grab_focus() diff --git a/addons/dialogic/Editor/Events/Fields/Number.tscn b/addons/dialogic/Editor/Events/Fields/Number.tscn new file mode 100644 index 0000000000000000000000000000000000000000..e41eceb85b1773fb1388a0d4a6aa61273889b89f --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Number.tscn @@ -0,0 +1,45 @@ +[gd_scene load_steps=4 format=3 uid="uid://kdpp3mibml33"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Number.gd" id="1"] + +[sub_resource type="Image" id="Image_yitwe"] +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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 76, 255, 255, 255, 75, 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, 99, 255, 255, 255, 191, 255, 255, 255, 191, 255, 255, 255, 99, 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, 4, 255, 255, 255, 122, 255, 255, 255, 191, 255, 255, 255, 188, 255, 255, 255, 188, 255, 255, 255, 191, 255, 255, 255, 121, 255, 255, 255, 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, 12, 255, 255, 255, 142, 255, 255, 255, 191, 255, 255, 255, 181, 255, 255, 255, 53, 255, 255, 255, 54, 255, 255, 255, 181, 255, 255, 255, 191, 255, 255, 255, 142, 255, 255, 255, 12, 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, 71, 255, 255, 255, 191, 255, 255, 255, 171, 255, 255, 255, 36, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 36, 255, 255, 255, 171, 255, 255, 255, 191, 255, 255, 255, 71, 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, 86, 255, 255, 255, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 22, 255, 255, 255, 86, 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, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 86, 255, 255, 255, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 22, 255, 255, 255, 86, 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, 71, 255, 255, 255, 191, 255, 255, 255, 171, 255, 255, 255, 36, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 36, 255, 255, 255, 171, 255, 255, 255, 191, 255, 255, 255, 71, 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, 12, 255, 255, 255, 142, 255, 255, 255, 191, 255, 255, 255, 181, 255, 255, 255, 54, 255, 255, 255, 54, 255, 255, 255, 182, 255, 255, 255, 191, 255, 255, 255, 142, 255, 255, 255, 12, 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, 4, 255, 255, 255, 121, 255, 255, 255, 191, 255, 255, 255, 188, 255, 255, 255, 188, 255, 255, 255, 191, 255, 255, 255, 121, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 98, 255, 255, 255, 191, 255, 255, 255, 191, 255, 255, 255, 98, 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, 75, 255, 255, 255, 75, 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, 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_uwu06"] +image = SubResource("Image_yitwe") + +[node name="NumberValue" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1036.0 +offset_bottom = -615.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 0 +script = ExtResource("1") +value = 0.0 + +[node name="Value" type="LineEdit" parent="."] +layout_mode = 2 +theme_type_variation = &"DialogicEventEdit" +theme_override_constants/minimum_character_width = 0 +text = "0" +expand_to_text_length = true + +[node name="Spin" type="Button" parent="."] +layout_mode = 2 +size_flags_vertical = 4 +focus_mode = 0 +icon = SubResource("ImageTexture_uwu06") +flat = true + +[connection signal="focus_exited" from="Value" to="." method="_on_value_focus_exited"] +[connection signal="text_submitted" from="Value" to="." method="_on_value_text_submitted"] +[connection signal="gui_input" from="Spin" to="." method="_on_spin_gui_input"] diff --git a/addons/dialogic/Editor/Events/Fields/OptionSelector.gd b/addons/dialogic/Editor/Events/Fields/OptionSelector.gd new file mode 100644 index 0000000000000000000000000000000000000000..3f2195539738b6b18638c7059f8047849e6ec635 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/OptionSelector.gd @@ -0,0 +1,63 @@ +@tool +extends MenuButton + +## Event block field for constant options. For varying options use ComplexPicker. + +signal value_changed +var property_name : String + +var options : Array = [] + +## if true, only the symbol will be displayed. In the dropdown text will be visible. +## Useful for making UI simpler +var symbol_only := false: + set(value): + symbol_only = value + if value: text = "" + +var current_value :Variant = -1 + +func _ready() -> void: +# add_theme_stylebox_override("normal", get_theme_stylebox("normal", "LineEdit")) +# add_theme_stylebox_override("hover", get_theme_stylebox("normal", "LineEdit")) + +# add_theme_stylebox_override("focus", get_theme_stylebox("focus", "LineEdit")) +# add_theme_stylebox_override("disabled", get_theme_stylebox("normal", "LineEdit")) + add_theme_color_override("font_disabled_color", get_theme_color("font_color", "MenuButton")) + about_to_popup.connect(insert_options) + get_popup().index_pressed.connect(index_pressed) + + +func set_value(value) -> void: + for option in options: + if option['value'] == value: + if typeof(option.get('icon')) == TYPE_ARRAY: + option.icon = callv('get_theme_icon', option.get('icon')) + if !symbol_only: + text = option['label'] + icon = option.get('icon', null) + current_value = value + + +func get_value() -> Variant: + return current_value + + +func insert_options() -> void: + get_popup().clear() + + var idx := 0 + for option in options: + if typeof(option.get('icon')) == TYPE_ARRAY: + option.icon = callv('get_theme_icon', option.get('icon')) + get_popup().add_icon_item(option.get('icon', null), option['label']) + get_popup().set_item_metadata(idx, option['value']) + idx += 1 + + +func index_pressed(idx:int) -> void: + current_value = idx + if !symbol_only: + text = get_popup().get_item_text(idx) + icon = get_popup().get_item_icon(idx) + value_changed.emit(property_name, get_popup().get_item_metadata(idx)) diff --git a/addons/dialogic/Editor/Events/Fields/OptionSelector.tscn b/addons/dialogic/Editor/Events/Fields/OptionSelector.tscn new file mode 100644 index 0000000000000000000000000000000000000000..15c24588dedd5c83a81bfc6acfeb4f2b9788d0a3 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/OptionSelector.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://d3bhehatwoio"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/OptionSelector.gd" id="1"] + +[node name="OptionSelector" type="MenuButton"] +offset_right = 137.0 +offset_bottom = 43.0 +focus_mode = 2 +theme_type_variation = &"DialogicEventEdit" +theme_override_colors/font_disabled_color = Color(0.875, 0.875, 0.875, 1) +text = "Placeholder Text" +flat = false +script = ExtResource("1") diff --git a/addons/dialogic/Editor/Events/Fields/SinglelineText.gd b/addons/dialogic/Editor/Events/Fields/SinglelineText.gd new file mode 100644 index 0000000000000000000000000000000000000000..0dddfdf4b697cb67f27d65f7bebbe92d043849a8 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/SinglelineText.gd @@ -0,0 +1,29 @@ +@tool +extends LineEdit + +## Event block field for a single line of text. + +signal value_changed +var property_name : String + +var placeholder :String= "": + set(value): + placeholder = value + placeholder_text = placeholder + + +func _ready() -> void: + text_changed.connect(_on_text_changed) +# add_theme_stylebox_override('normal', get_theme_stylebox('normal', 'LineEdit')) +# add_theme_stylebox_override('focus', get_theme_stylebox('focus', 'LineEdit')) + + +func _on_text_changed(value := "") -> void: + value_changed.emit(property_name, text) + + +func set_value(value:String) -> void: + text = str(value) + +func take_autofocus(): + grab_focus() diff --git a/addons/dialogic/Editor/Events/Fields/SinglelineText.tscn b/addons/dialogic/Editor/Events/Fields/SinglelineText.tscn new file mode 100644 index 0000000000000000000000000000000000000000..327083c496b13e40dc25bea0203475c0c87da3e7 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/SinglelineText.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=3 uid="uid://c0vkcehgjsjy"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/SinglelineText.gd" id="1"] + +[node name="SingleLineText" type="LineEdit"] +offset_right = 1152.0 +offset_bottom = 81.0 +theme_type_variation = &"DialogicEventEdit" +expand_to_text_length = true +script = ExtResource("1") diff --git a/addons/dialogic/Editor/Events/Fields/Vector2.gd b/addons/dialogic/Editor/Events/Fields/Vector2.gd new file mode 100644 index 0000000000000000000000000000000000000000..3c02d2e03930c60ccab418a5460d330ffc9db57d --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Vector2.gd @@ -0,0 +1,26 @@ +@tool +extends Control + +## Event block field for a vector. + +signal value_changed +var property_name : String + +var current_value := Vector2() + +func _ready() -> void: + $X.value_changed.connect(_on_value_changed) + $Y.value_changed.connect(_on_value_changed) + + +func _on_value_changed(property:String, value:float) -> void: + current_value = Vector2($X.value, $Y.value) + emit_signal("value_changed", property_name, current_value) + + +func set_value(value:Vector2) -> void: + $X.tooltip_text = tooltip_text + $Y.tooltip_text = tooltip_text + $X.set_value(value.x) + $Y.set_value(value.y) + current_value = value diff --git a/addons/dialogic/Editor/Events/Fields/Vector2.tscn b/addons/dialogic/Editor/Events/Fields/Vector2.tscn new file mode 100644 index 0000000000000000000000000000000000000000..6e55d69f94eca2f52b872642078d0c40d9168515 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/Vector2.tscn @@ -0,0 +1,24 @@ +[gd_scene load_steps=3 format=3 uid="uid://dtimnsj014cu"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Vector2.gd" id="1_288li"] +[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/Number.tscn" id="3_l3bum"] + +[node name="Vector2" type="HBoxContainer"] +offset_right = 40.0 +offset_bottom = 40.0 +theme_override_constants/separation = -7 +script = ExtResource("1_288li") + +[node name="X" parent="." instance=ExtResource("3_l3bum")] +layout_mode = 2 +step = 1.0 +min = -9999.0 +max = 9999.0 +suffix = "x" + +[node name="Y" parent="." instance=ExtResource("3_l3bum")] +layout_mode = 2 +step = 1.0 +min = -9999.0 +max = 9999.0 +suffix = "y" diff --git a/addons/dialogic/Editor/Events/styles/InputFieldsStyle.tres b/addons/dialogic/Editor/Events/styles/InputFieldsStyle.tres new file mode 100644 index 0000000000000000000000000000000000000000..fa952ee420c1b0287ef1bcc9b1b22e5a46148069 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/InputFieldsStyle.tres @@ -0,0 +1,50 @@ +[gd_resource type="Theme" load_steps=3 format=3 uid="uid://d3g4i4dshtdpu"] + +[sub_resource type="StyleBoxFlat" id="1"] +content_margin_left = 30.0 +content_margin_top = 5.0 +content_margin_right = 20.0 +content_margin_bottom = 5.0 +bg_color = Color(0.12549, 0.141176, 0.192157, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.0980392, 0.113725, 0.152941, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="2"] +content_margin_left = 11.0 +content_margin_top = 5.0 +content_margin_right = 20.0 +content_margin_bottom = 5.0 +bg_color = Color(0.12549, 0.141176, 0.192157, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.0980392, 0.113725, 0.152941, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[resource] +LineEdit/colors/clear_button_color = Color(0, 0, 0, 1) +LineEdit/colors/clear_button_color_pressed = Color(0, 0, 0, 1) +LineEdit/colors/cursor_color = Color(1, 1, 1, 1) +LineEdit/colors/font_color = Color(1, 1, 1, 1) +LineEdit/colors/font_color_selected = Color(1, 1, 1, 1) +LineEdit/colors/font_color_uneditable = Color(1, 1, 1, 1) +LineEdit/colors/selection_color = Color(1, 1, 1, 0.235294) +LineEdit/constants/minimum_spaces = 10 +LineEdit/fonts/font = null +LineEdit/icons/clear = null +LineEdit/styles/focus = SubResource("1") +LineEdit/styles/normal = SubResource("2") +LineEdit/styles/read_only = SubResource("1") +LineEditWithIcon/base_type = &"LineEdit" +LineEditWithIcon/styles/normal = SubResource("1") diff --git a/addons/dialogic/Editor/Events/styles/ResourceMenuHover.tres b/addons/dialogic/Editor/Events/styles/ResourceMenuHover.tres new file mode 100644 index 0000000000000000000000000000000000000000..ecf83762c01d664eadb5ca8eb969a07f6a3e7c88 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/ResourceMenuHover.tres @@ -0,0 +1,11 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +content_margin_left = 25.0 +content_margin_right = 10.0 +content_margin_top = 4.0 +content_margin_bottom = 4.0 +bg_color = Color( 0.466667, 0.466667, 0.466667, 0.141176 ) +border_width_bottom = 2 +corner_radius_top_left = 4 +corner_radius_top_right = 4 diff --git a/addons/dialogic/Editor/Events/styles/ResourceMenuNormal.tres b/addons/dialogic/Editor/Events/styles/ResourceMenuNormal.tres new file mode 100644 index 0000000000000000000000000000000000000000..d14860acd3ce9dd954e81a230831e0001791757a --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/ResourceMenuNormal.tres @@ -0,0 +1,13 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +content_margin_left = 25.0 +content_margin_right = 10.0 +content_margin_top = 4.0 +content_margin_bottom = 4.0 +bg_color = Color( 0.180392, 0.180392, 0.180392, 0.219608 ) +draw_center = false +border_width_bottom = 2 +border_color = Color( 0.8, 0.8, 0.8, 0.286275 ) +corner_radius_top_left = 4 +corner_radius_top_right = 4 diff --git a/addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres b/addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres new file mode 100644 index 0000000000000000000000000000000000000000..314544b9f0ceeb1688e82dd6c67777fffa237676 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres @@ -0,0 +1,17 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://c8k6tbipodsg"] + +[resource] +content_margin_left = 10.0 +content_margin_top = 10.0 +content_margin_right = 10.0 +content_margin_bottom = 10.0 +bg_color = Color(0, 0, 0, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.8, 0.8, 0.8, 0.109804) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 diff --git a/addons/dialogic/Editor/Events/styles/SectionPanel.tres b/addons/dialogic/Editor/Events/styles/SectionPanel.tres new file mode 100644 index 0000000000000000000000000000000000000000..b886c6ea03c5381dcf25e8e14509469fe897e72d --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/SectionPanel.tres @@ -0,0 +1,17 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +content_margin_left = 6.0 +content_margin_right = 6.0 +content_margin_top = 5.0 +content_margin_bottom = 4.0 +bg_color = Color( 0.6, 0.6, 0.6, 0 ) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color( 0.2, 0.227451, 0.309804, 1 ) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 diff --git a/addons/dialogic/Editor/Events/styles/SettingsFieldBackground.tres b/addons/dialogic/Editor/Events/styles/SettingsFieldBackground.tres new file mode 100644 index 0000000000000000000000000000000000000000..91b4b38e75c2647137476ef9b17220479c8e80af --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/SettingsFieldBackground.tres @@ -0,0 +1,18 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +content_margin_left = 30.0 +content_margin_right = 25.0 +content_margin_top = 5.0 +content_margin_bottom = 5.0 +bg_color = Color( 0.12549, 0.141176, 0.192157, 1 ) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color( 0.0980392, 0.113725, 0.152941, 1 ) +border_blend = true +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 diff --git a/addons/dialogic/Editor/Events/styles/SimpleButtonHover.tres b/addons/dialogic/Editor/Events/styles/SimpleButtonHover.tres new file mode 100644 index 0000000000000000000000000000000000000000..5be19d4a0de326f7bfd37b2e1193019bb980be9b --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/SimpleButtonHover.tres @@ -0,0 +1,17 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +content_margin_left = 3.0 +content_margin_right = 3.0 +content_margin_top = 3.0 +content_margin_bottom = 3.0 +bg_color = Color( 0.2, 0.231373, 0.309804, 0.317647 ) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color( 0.8, 0.8, 0.8, 0.109804 ) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 diff --git a/addons/dialogic/Editor/Events/styles/SimpleButtonNormal.tres b/addons/dialogic/Editor/Events/styles/SimpleButtonNormal.tres new file mode 100644 index 0000000000000000000000000000000000000000..e5c06b496be9f051e3178a1aae1f90aeb5b96014 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/SimpleButtonNormal.tres @@ -0,0 +1,17 @@ +[gd_resource type="StyleBoxFlat" format=2] + +[resource] +content_margin_left = 3.0 +content_margin_right = 3.0 +content_margin_top = 3.0 +content_margin_bottom = 3.0 +bg_color = Color( 0.2, 0.231373, 0.309804, 0.235294 ) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color( 0.8, 0.8, 0.8, 0.109804 ) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 diff --git a/addons/dialogic/Editor/Events/styles/TextBackground.tres b/addons/dialogic/Editor/Events/styles/TextBackground.tres new file mode 100644 index 0000000000000000000000000000000000000000..0d74e3d7b14d5037cd0d0dc3e5efc5d340db2801 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/TextBackground.tres @@ -0,0 +1,12 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://cu8otiwksn8ma"] + +[resource] +content_margin_left = 10.0 +content_margin_top = 13.0 +content_margin_bottom = 2.0 +bg_color = Color(1, 1, 1, 0.0784314) +border_color = Color(0.454902, 0.454902, 0.454902, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 diff --git a/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres b/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres new file mode 100644 index 0000000000000000000000000000000000000000..603fe13451a2f0e1324af4815ec0d5f39f602d41 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres @@ -0,0 +1,16 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://obyrr26pqk2p"] + +[resource] +content_margin_left = 7.0 +content_margin_top = 0.0 +content_margin_right = 0.0 +content_margin_bottom = 0.0 +bg_color = Color(0.776471, 0.776471, 0.776471, 0.207843) +border_width_left = 3 +border_color = Color(1, 1, 1, 1) +corner_radius_top_left = 1 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 1 +expand_margin_top = 3.0 +expand_margin_bottom = 3.0 diff --git a/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres b/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres new file mode 100644 index 0000000000000000000000000000000000000000..f7cf0afe0f1c231865a249c0bea757f2cbd24f46 --- /dev/null +++ b/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres @@ -0,0 +1,4 @@ +[gd_resource type="StyleBoxEmpty" format=3 uid="uid://cl75ikyq2is7c"] + +[resource] +content_margin_left = 7.0 diff --git a/addons/dialogic/Editor/HomePage/home_page.gd b/addons/dialogic/Editor/HomePage/home_page.gd new file mode 100644 index 0000000000000000000000000000000000000000..a2044e5f460c4d2e170f1fdeac70af38633f118d --- /dev/null +++ b/addons/dialogic/Editor/HomePage/home_page.gd @@ -0,0 +1,86 @@ +@tool +extends DialogicEditor + +## A Main page in the dialogic editor. + +var tips : Array = [] + + + +func _get_icon() -> Texture: + return load("res://addons/dialogic/Editor/Images/plugin-icon.svg") + + +func _ready(): + self_modulate = get_theme_color("font_color", "Editor") + self_modulate.a = 0.2 + + var edit_scale := DialogicUtil.get_editor_scale() + %HomePageBox.custom_minimum_size = Vector2(600, 350)*edit_scale + %TopPanel.custom_minimum_size.y = 100*edit_scale + %VersionLabel.set('theme_override_font_sizes/font_size', 10 * edit_scale) + var plugin_cfg := ConfigFile.new() + plugin_cfg.load("res://addons/dialogic/plugin.cfg") + %VersionLabel.text = plugin_cfg.get_value('plugin', 'version', 'unknown version') + + %BottomPanel.self_modulate = get_theme_color("dark_color_3", "Editor") + + %RandomTipLabel.add_theme_color_override("font_color", get_theme_color("property_color_z", "Editor")) + %RandomTipMoreButton.icon = get_theme_icon("ExternalLink", "EditorIcons") + + + +func _register(): + editors_manager.register_simple_editor(self) + + self.alternative_text = "Welcome to dialogic!" + + + +func _open(extra_info:Variant="") -> void: + if tips.is_empty(): + var file := FileAccess.open('res://addons/dialogic/Editor/HomePage/tips.txt', FileAccess.READ) + tips = file.get_as_text().split('\n') + tips = tips.filter(func(item): return !item.is_empty()) + + randomize() + var tip :String = tips[randi()%len(tips)] + var text := tip.get_slice(';',0).strip_edges() + var action := tip.get_slice(';',1).strip_edges() + if action == text: + action = "" + show_tip(text, action) + + +func show_tip(text:String='', action:String='') -> void: + if text.is_empty(): + %TipBox.hide() + %RandomTipLabel.hide() + return + + %TipBox.show() + %RandomTipLabel.show() + %RandomTip.text = '[i]'+text + + if action.is_empty(): + %RandomTipMoreButton.hide() + return + + %RandomTipMoreButton.show() + + if %RandomTipMoreButton.pressed.is_connected(_on_tip_action): + %RandomTipMoreButton.pressed.disconnect(_on_tip_action) + %RandomTipMoreButton.pressed.connect(_on_tip_action.bind(action)) + + +func _on_tip_action(action:String) -> void: + if action.begins_with('https://'): + OS.shell_open(action) + return + elif action.begins_with('editor://'): + var editor_name := action.trim_prefix('editor://').get_slice('->',0) + var extra_info := action.trim_prefix('editor://').get_slice('->',1) + if editor_name in editors_manager.editors: + editors_manager.open_editor(editors_manager.editors[editor_name].node, false, extra_info) + return + print("Tip button doesn't do anything (", action, ")") diff --git a/addons/dialogic/Editor/HomePage/home_page.tscn b/addons/dialogic/Editor/HomePage/home_page.tscn new file mode 100644 index 0000000000000000000000000000000000000000..81481e938467e00bb931dd2c6be0e7c1658723bd --- /dev/null +++ b/addons/dialogic/Editor/HomePage/home_page.tscn @@ -0,0 +1,363 @@ +[gd_scene load_steps=23 format=3 uid="uid://cqy73hshqqgga"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/HomePage/home_page.gd" id="1_6g38w"] +[ext_resource type="Texture2D" uid="uid://cvmlp5nxb2rer" path="res://addons/dialogic/Editor/HomePage/icon_bg.png" id="1_ed1g1"] +[ext_resource type="Texture2D" uid="uid://bt87p6qlso0ya" path="res://addons/dialogic/Editor/Images/dialogic-logo.svg" id="3_3leok"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_imi2d"] +draw_center = false +corner_radius_top_left = 15 +corner_radius_top_right = 15 +shadow_color = Color(0.796078, 0.572549, 0.933333, 0.0627451) +shadow_size = 24 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_n2afh"] +corner_radius_top_left = 15 +corner_radius_top_right = 15 + +[sub_resource type="Gradient" id="Gradient_lt7uf"] +colors = PackedColorArray(0.296484, 0.648457, 1, 1, 0.732014, 0.389374, 1, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_2klx3"] +gradient = SubResource("Gradient_lt7uf") +fill_from = Vector2(0.151515, 0.272727) +fill_to = Vector2(1, 1) + +[sub_resource type="Gradient" id="Gradient_1gns2"] +offsets = PackedFloat32Array(0.302013, 0.872483) +colors = PackedColorArray(0.365323, 0.360806, 0.260695, 0, 0.615686, 0.615686, 0.615686, 0.592157) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_u0aw3"] +gradient = SubResource("Gradient_1gns2") +fill = 1 +fill_from = Vector2(0.497835, 0.493506) +fill_to = Vector2(1, 1) + +[sub_resource type="FontVariation" id="FontVariation_vepxx"] +variation_embolden = 2.0 + +[sub_resource type="LabelSettings" id="LabelSettings_w8q1h"] +font = SubResource("FontVariation_vepxx") +font_size = 40 +outline_size = 14 +outline_color = Color(0.0901961, 0.0901961, 0.0901961, 0.258824) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_p7ka2"] +content_margin_left = 10.0 +content_margin_top = 10.0 +content_margin_right = 10.0 +content_margin_bottom = 10.0 +bg_color = Color(1, 1, 1, 1) +corner_radius_bottom_right = 15 +corner_radius_bottom_left = 15 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_es88k"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ce6uo"] +content_margin_left = 7.0 +content_margin_top = 7.0 +content_margin_right = 7.0 +content_margin_bottom = 14.0 +bg_color = Color(0.803922, 0.352941, 1, 0.141176) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 + +[sub_resource type="FontVariation" id="FontVariation_elu6e"] +variation_embolden = 1.1 + +[sub_resource type="FontVariation" id="FontVariation_5kbdj"] +variation_transform = Transform2D(1, 0.239, 0, 1, 0, 0) + +[sub_resource type="FontVariation" id="FontVariation_g0m61"] +variation_embolden = 1.43 +variation_transform = Transform2D(1, 0.343, 0, 1, 0, 0) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_a8dvw"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ckyhx"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.470588, 0.196078, 0.6, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l1doy"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.470588, 0.196078, 0.6, 1) +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 + +[sub_resource type="Image" id="Image_2imc3"] +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_udy8i"] +image = SubResource("Image_2imc3") + +[node name="HomePage" type="TextureRect"] +self_modulate = Color(0, 0, 0, 0.2) +clip_contents = true +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -2.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("1_ed1g1") +expand_mode = 1 +stretch_mode = 3 +script = ExtResource("1_6g38w") + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="HomePageBox" type="VBoxContainer" parent="CenterContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(450, 262.5) +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="TopPanel" type="Panel" parent="CenterContainer/HomePageBox"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 75) +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_imi2d") + +[node name="Header2" type="Panel" parent="CenterContainer/HomePageBox/TopPanel"] +clip_children = 1 +clip_contents = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.4 +theme_override_styles/panel = SubResource("StyleBoxFlat_n2afh") + +[node name="BG" type="TextureRect" parent="CenterContainer/HomePageBox/TopPanel/Header2"] +modulate = Color(0.65098, 0.65098, 0.65098, 1) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.3 +texture = SubResource("GradientTexture2D_2klx3") +expand_mode = 1 + +[node name="Vignette" type="TextureRect" parent="CenterContainer/HomePageBox/TopPanel/Header2"] +modulate = Color(0, 0, 0, 1) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -166.0 +offset_bottom = 166.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = SubResource("GradientTexture2D_u0aw3") +expand_mode = 1 + +[node name="Logo" type="TextureRect" parent="CenterContainer/HomePageBox/TopPanel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 19.0 +offset_top = 10.0 +offset_right = -23.0 +offset_bottom = -10.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.3 +texture = ExtResource("3_3leok") +expand_mode = 1 +stretch_mode = 5 + +[node name="Label" type="Label" parent="CenterContainer/HomePageBox/TopPanel/Logo"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 155.0 +offset_top = -37.0 +offset_right = 185.0 +offset_bottom = 21.0 +grow_horizontal = 2 +grow_vertical = 2 +rotation = -0.201447 +text = "2" +label_settings = SubResource("LabelSettings_w8q1h") + +[node name="BottomPanel" type="PanelContainer" parent="CenterContainer/HomePageBox"] +unique_name_in_owner = true +self_modulate = Color(0, 0, 0, 1) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_p7ka2") + +[node name="VersionLabel" type="Label" parent="CenterContainer/HomePageBox/BottomPanel"] +unique_name_in_owner = true +modulate = Color(1, 1, 1, 0.501961) +layout_mode = 2 +size_flags_vertical = 8 +theme_override_font_sizes/font_size = 7 +text = "2.0-Alpha-8 (for Godot 4.0.2 stable)" +horizontal_alignment = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="CenterContainer/HomePageBox/BottomPanel"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 50 + +[node name="CenterContainer" type="VBoxContainer" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer"] +layout_mode = 2 +size_flags_stretch_ratio = 0.4 + +[node name="Label" type="Label" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicSection" +theme_override_constants/line_spacing = 0 +text = "Documentation" + +[node name="WikiButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicLink" +text = " Wiki" +underline = 2 +uri = "https://github.com/coppolaemilio/dialogic/wiki" + +[node name="WikiGettingStartedButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicLink" +text = " Getting Started" +underline = 2 +uri = "https://github.com/coppolaemilio/dialogic/wiki/Tutorial:-Getting-Started" + +[node name="Separator" type="Control" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="Label2" type="Label" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "Get in touch" + +[node name="BugRequestButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicLink" +text = " Bug / Request" +underline = 2 +uri = "https://github.com/coppolaemilio/dialogic/issues/new/choose" + +[node name="DiscordButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicLink" +text = " Discord" +underline = 2 +uri = "https://discord.gg/2hHQzkf2pX" + +[node name="DonateButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicLink" +text = " Donate" +underline = 2 +uri = "https://www.patreon.com/coppolaemilio" + +[node name="CenterContainer2" type="VBoxContainer" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 15 + +[node name="WelcomeText" type="RichTextLabel" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer2"] +visible = false +layout_mode = 2 +theme_override_styles/normal = SubResource("StyleBoxEmpty_es88k") +bbcode_enabled = true +text = "[center]Welcome to dialogic, a plugin that let's you easily create stories and dialogs for your game!" +fit_content = true + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer2"] +layout_mode = 2 +theme_override_constants/separation = -4 +alignment = 1 + +[node name="RandomTipLabel" type="Label" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer2/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicSection" +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "Random Tip" + +[node name="TipBox" type="PanelContainer" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer2/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ce6uo") + +[node name="RandomTip" type="RichTextLabel" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer2/VBoxContainer/TipBox"] +unique_name_in_owner = true +clip_contents = false +layout_mode = 2 +theme_override_fonts/bold_font = SubResource("FontVariation_elu6e") +theme_override_fonts/italics_font = SubResource("FontVariation_5kbdj") +theme_override_fonts/bold_italics_font = SubResource("FontVariation_g0m61") +theme_override_styles/normal = SubResource("StyleBoxEmpty_a8dvw") +bbcode_enabled = true +text = "[i]You can[/i] [b]create custom[/b] events, [i][b]subsystems, text effects and even editors for[/b][i] [code]dialogic!" +fit_content = true + +[node name="RandomTipMoreButton" type="Button" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer2/VBoxContainer/TipBox/RandomTip"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -30.0 +offset_top = 1.0 +offset_right = -8.0 +offset_bottom = 23.0 +grow_horizontal = 0 +grow_vertical = 0 +tooltip_text = "Check it out!" +theme_override_styles/normal = SubResource("StyleBoxFlat_ckyhx") +theme_override_styles/hover = SubResource("StyleBoxFlat_l1doy") +icon = SubResource("ImageTexture_udy8i") +expand_icon = true diff --git a/addons/dialogic/Editor/HomePage/icon_bg.png b/addons/dialogic/Editor/HomePage/icon_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..77f127d8e6683e4fa520d7e48df25435ab78d531 Binary files /dev/null and b/addons/dialogic/Editor/HomePage/icon_bg.png differ diff --git a/addons/dialogic/Editor/HomePage/icon_bg.png.import b/addons/dialogic/Editor/HomePage/icon_bg.png.import new file mode 100644 index 0000000000000000000000000000000000000000..f4c58ef88b848804847331e63b7f0841147f0864 --- /dev/null +++ b/addons/dialogic/Editor/HomePage/icon_bg.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvmlp5nxb2rer" +path="res://.godot/imported/icon_bg.png-5937ce0a857c4a8a9d624ea9ebf09a97.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/HomePage/icon_bg.png" +dest_files=["res://.godot/imported/icon_bg.png-5937ce0a857c4a8a9d624ea9ebf09a97.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Editor/HomePage/tips.txt b/addons/dialogic/Editor/HomePage/tips.txt new file mode 100644 index 0000000000000000000000000000000000000000..334fa65b8b104513f4e3f6dba670e1282194f8e4 --- /dev/null +++ b/addons/dialogic/Editor/HomePage/tips.txt @@ -0,0 +1,11 @@ +Dialogic variables can be changed from timelines [b]and[/b] scripts! They can be used in conditions and inside of texts!; editor://VariablesEditor +You can create [b]custom modules[/b] for dialogic, including events, subsystems, text effects, ui layouts and even editors!; editor://Settings->General +If there are events you never need, you can hide them from the list in the editor!; editor://Settings->Modules +Did you know that dialogic supports translations? It does!; editor://Settings->Translations +You can use [b]bbcode effects[/b] in text events! What are they though???; https://docs.godotengine.org/en/latest/tutorials/ui/bbcode_in_richtextlabel.html +Writing [/i][i] in a text event will pick a random one of the three strings! +There are a number of cool text effects like [pause=x], [speed=x] and [portrait=x]. Try them out!; editor://Settings->DialogText +You can use scenes as portraits! This gives you basically limiteless freedom.; https://github.com/coppolaemilio/dialogic/wiki/Tutorial:-Custom-Portraits +You can use scenes as backgrounds. This way they can be animated or whatever you want! +Dialogic has a built in save and load system! It's pretty powerful!; editor://Settings->Saving +You can add multiple glossary files, each containing words that can be hovered for information!; editor://GlossaryEditor diff --git a/addons/dialogic/Editor/Images/Dropdown/default.svg b/addons/dialogic/Editor/Images/Dropdown/default.svg new file mode 100644 index 0000000000000000000000000000000000000000..1437dbca2255bfddf2aac1dbea40394770f86421 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/default.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/default.svg.import b/addons/dialogic/Editor/Images/Dropdown/default.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..e8c82b0ada65f5e6e02b3663681097b58b5ed809 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/default.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsx8dtqf3vych" +path="res://.godot/imported/default.svg-3f34de5e45bef5de4d9c15ef78c00c6c.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/default.svg" +dest_files=["res://.godot/imported/default.svg-3f34de5e45bef5de4d9c15ef78c00c6c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/divide.svg b/addons/dialogic/Editor/Images/Dropdown/divide.svg new file mode 100644 index 0000000000000000000000000000000000000000..7fb881f0a75a5acfd9a5f2cedc6bbe115997d936 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/divide.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/divide.svg.import b/addons/dialogic/Editor/Images/Dropdown/divide.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..9a64c5f5d3d79ee821a45504e705689dc3f33c5f --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/divide.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5laykjsxaxtl" +path="res://.godot/imported/divide.svg-4928f878a07ba93ebc44d8ae73ad4c1f.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/divide.svg" +dest_files=["res://.godot/imported/divide.svg-4928f878a07ba93ebc44d8ae73ad4c1f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/join.svg b/addons/dialogic/Editor/Images/Dropdown/join.svg new file mode 100644 index 0000000000000000000000000000000000000000..9eb7aa9d3c2702898e84a25406266cbf11823bbf --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/join.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/join.svg.import b/addons/dialogic/Editor/Images/Dropdown/join.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..83426076577161afc2e3dcd0578885b84c0e04ff --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/join.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7j220k0ewh35" +path="res://.godot/imported/join.svg-2f0d7b9e8e01cf0e62b8c3a85aff6213.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/join.svg" +dest_files=["res://.godot/imported/join.svg-2f0d7b9e8e01cf0e62b8c3a85aff6213.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/leave.svg b/addons/dialogic/Editor/Images/Dropdown/leave.svg new file mode 100644 index 0000000000000000000000000000000000000000..e23461969bac8b46bce1bf7434fefa9352719c33 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/leave.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/leave.svg.import b/addons/dialogic/Editor/Images/Dropdown/leave.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..bd1bff503dab84311cec6c37092f1def27a017a6 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/leave.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cspjyvye6c0r6" +path="res://.godot/imported/leave.svg-c936f6e3d601b8c12c23f205a765084e.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/leave.svg" +dest_files=["res://.godot/imported/leave.svg-c936f6e3d601b8c12c23f205a765084e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/minus.svg b/addons/dialogic/Editor/Images/Dropdown/minus.svg new file mode 100644 index 0000000000000000000000000000000000000000..eb5d732de8124c8efa05bae56450971f61330b15 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/minus.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/minus.svg.import b/addons/dialogic/Editor/Images/Dropdown/minus.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..d44178a5dfda6d2c92dff72f07c26c5000d24718 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/minus.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dwy14qrkfoeb" +path="res://.godot/imported/minus.svg-29f22d1aa24635bae2c03057c07be8bc.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/minus.svg" +dest_files=["res://.godot/imported/minus.svg-29f22d1aa24635bae2c03057c07be8bc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/multiply.svg b/addons/dialogic/Editor/Images/Dropdown/multiply.svg new file mode 100644 index 0000000000000000000000000000000000000000..d4327d1b79a646340f1af3bc6a22230faa7c251b --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/multiply.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/multiply.svg.import b/addons/dialogic/Editor/Images/Dropdown/multiply.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..a23c37b6d6ce6b1c9495bcd8d9e86a0a602bd5bc --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/multiply.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddmapfkunbtg7" +path="res://.godot/imported/multiply.svg-0e9db99aafb66d43ee14adcca26c5b47.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/multiply.svg" +dest_files=["res://.godot/imported/multiply.svg-0e9db99aafb66d43ee14adcca26c5b47.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/plus.svg b/addons/dialogic/Editor/Images/Dropdown/plus.svg new file mode 100644 index 0000000000000000000000000000000000000000..adf5179e2d87acfe77a5a2393da9c4377ae8a35a --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/plus.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/plus.svg.import b/addons/dialogic/Editor/Images/Dropdown/plus.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..e86414be7a101d37d76d7e65a0388db41384bd32 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/plus.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cqqtygfbvgtag" +path="res://.godot/imported/plus.svg-e094b0b8505b5d910717883d06553532.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/plus.svg" +dest_files=["res://.godot/imported/plus.svg-e094b0b8505b5d910717883d06553532.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/set.svg b/addons/dialogic/Editor/Images/Dropdown/set.svg new file mode 100644 index 0000000000000000000000000000000000000000..16c6a8be8d506fa4230e2a05d725a3021cf70070 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/set.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/set.svg.import b/addons/dialogic/Editor/Images/Dropdown/set.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..6c31572630a227148f0f3419ce8156d4132d87e5 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/set.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddcfl67v0r1lw" +path="res://.godot/imported/set.svg-f100fad003be2285d5d0da5c58417203.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/set.svg" +dest_files=["res://.godot/imported/set.svg-f100fad003be2285d5d0da5c58417203.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Dropdown/update.svg b/addons/dialogic/Editor/Images/Dropdown/update.svg new file mode 100644 index 0000000000000000000000000000000000000000..44f1f3cc9dfb6b7eee0b4f43bb03789cf336ecf8 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/update.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/addons/dialogic/Editor/Images/Dropdown/update.svg.import b/addons/dialogic/Editor/Images/Dropdown/update.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..17546a6b18c97ca2749b77edd95efae95eb8b848 --- /dev/null +++ b/addons/dialogic/Editor/Images/Dropdown/update.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://t1roknwygcf3" +path="res://.godot/imported/update.svg-cefa0fe6bfa50911bb9a77982288e485.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Dropdown/update.svg" +dest_files=["res://.godot/imported/update.svg-cefa0fe6bfa50911bb9a77982288e485.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/add-folder.svg b/addons/dialogic/Editor/Images/Pieces/add-folder.svg new file mode 100644 index 0000000000000000000000000000000000000000..7331b615b41f002c7a21e84a3af727f53b796027 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/add-folder.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/dialogic/Editor/Images/Pieces/add-folder.svg.import b/addons/dialogic/Editor/Images/Pieces/add-folder.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..b7765a4304ed07ced85c23bbdba47722b54421ad --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/add-folder.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://babwe22dqjta" +path="res://.godot/imported/add-folder.svg-41a970370f904038e63c13bddbdb6450.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/add-folder.svg" +dest_files=["res://.godot/imported/add-folder.svg-41a970370f904038e63c13bddbdb6450.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/closed-icon.svg b/addons/dialogic/Editor/Images/Pieces/closed-icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..4032eab9758254384a85e93a2893079acb8b32ce --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/closed-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Pieces/closed-icon.svg.import b/addons/dialogic/Editor/Images/Pieces/closed-icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..146327e59398e764200ef09b38c53297758501ae --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/closed-icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dx3o2ild56i76" +path="res://.godot/imported/closed-icon.svg-b4f16653b91d6792313a130565319b2f.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/closed-icon.svg" +dest_files=["res://.godot/imported/closed-icon.svg-b4f16653b91d6792313a130565319b2f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/expand-icon.svg b/addons/dialogic/Editor/Images/Pieces/expand-icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..3ec3def8948ef7766c1676b6460ee7a434378f4a --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/expand-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/addons/dialogic/Editor/Images/Pieces/expand-icon.svg.import b/addons/dialogic/Editor/Images/Pieces/expand-icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..0813669edde99154c09a6097f7b8ef4fd14d166a --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/expand-icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cl03vrbj5wsjk" +path="res://.godot/imported/expand-icon.svg-26099b197ab0f314e2253848fcc22962.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/expand-icon.svg" +dest_files=["res://.godot/imported/expand-icon.svg-26099b197ab0f314e2253848fcc22962.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/open-icon.svg b/addons/dialogic/Editor/Images/Pieces/open-icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..c66c422dcfa2e2034a6a54d289f613cc357d9bdd --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/open-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Pieces/open-icon.svg.import b/addons/dialogic/Editor/Images/Pieces/open-icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..eaec29ceb8b8190d95f91c1a34ff503db75cba0e --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/open-icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mc7a24bcvjo3" +path="res://.godot/imported/open-icon.svg-1a2ae6d0121a79b624c0fb87cc9ceea2.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/open-icon.svg" +dest_files=["res://.godot/imported/open-icon.svg-1a2ae6d0121a79b624c0fb87cc9ceea2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/variable.svg b/addons/dialogic/Editor/Images/Pieces/variable.svg new file mode 100644 index 0000000000000000000000000000000000000000..236ca35a01af8f3b5384b6bf145fbebd21da9732 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/variable.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Pieces/variable.svg.import b/addons/dialogic/Editor/Images/Pieces/variable.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..80be2aeb0483fe2c90659d86317924c3e8302e90 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/variable.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dih1coellhwm8" +path="res://.godot/imported/variable.svg-50a50248b9d47e5556571e4111e8d5b4.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/variable.svg" +dest_files=["res://.godot/imported/variable.svg-50a50248b9d47e5556571e4111e8d5b4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/variable_icon.png b/addons/dialogic/Editor/Images/Pieces/variable_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..779e80f04b53c193feea0e0782266087d9bd1e5f Binary files /dev/null and b/addons/dialogic/Editor/Images/Pieces/variable_icon.png differ diff --git a/addons/dialogic/Editor/Images/Pieces/variable_icon.png.import b/addons/dialogic/Editor/Images/Pieces/variable_icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..7f8b408fdd691fc8e0d1dd6e2ad4640020cfdc30 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/variable_icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ikdhcat2nq2r" +path="res://.godot/imported/variable_icon.png-df9d711980209a7752dc8762037e39ad.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/variable_icon.png" +dest_files=["res://.godot/imported/variable_icon.png-df9d711980209a7752dc8762037e39ad.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Editor/Images/Pieces/warning.svg b/addons/dialogic/Editor/Images/Pieces/warning.svg new file mode 100644 index 0000000000000000000000000000000000000000..a252bde4ad6bd5506cfdd9c03d466c1c1a93a519 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Pieces/warning.svg.import b/addons/dialogic/Editor/Images/Pieces/warning.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..ada5976389b46f7c3c977360fd01ac99987e5ac3 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/warning.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d4n3j4lvatwxb" +path="res://.godot/imported/warning.svg-a48ae93c4663637f2aca88d055604495.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/warning.svg" +dest_files=["res://.godot/imported/warning.svg-a48ae93c4663637f2aca88d055604495.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=3.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Resources/character.svg b/addons/dialogic/Editor/Images/Resources/character.svg new file mode 100644 index 0000000000000000000000000000000000000000..8871f5e4d159d3da0a64e1d0d5173ea0e14b0fea --- /dev/null +++ b/addons/dialogic/Editor/Images/Resources/character.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/dialogic/Editor/Images/Resources/character.svg.import b/addons/dialogic/Editor/Images/Resources/character.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..37eeb11aa1236fbf7c0158af01fa67e761afb484 --- /dev/null +++ b/addons/dialogic/Editor/Images/Resources/character.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbea0efx0ybu7" +path="res://.godot/imported/character.svg-48bc1c93fa13733a935ca2c669d933a7.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Resources/character.svg" +dest_files=["res://.godot/imported/character.svg-48bc1c93fa13733a935ca2c669d933a7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Resources/icon_character.png b/addons/dialogic/Editor/Images/Resources/icon_character.png new file mode 100644 index 0000000000000000000000000000000000000000..5858854742aa5e2f19debcb1c6310581a24ded7f Binary files /dev/null and b/addons/dialogic/Editor/Images/Resources/icon_character.png differ diff --git a/addons/dialogic/Editor/Images/Resources/icon_character.png.import b/addons/dialogic/Editor/Images/Resources/icon_character.png.import new file mode 100644 index 0000000000000000000000000000000000000000..667d33028e424ddfa235b2e29e0bacdd980e37fb --- /dev/null +++ b/addons/dialogic/Editor/Images/Resources/icon_character.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmwrsq48ywc50" +path="res://.godot/imported/icon_character.png-97a1851bbafe2b302ea88c25a87ee2c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Resources/icon_character.png" +dest_files=["res://.godot/imported/icon_character.png-97a1851bbafe2b302ea88c25a87ee2c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Editor/Images/Resources/portrait.svg b/addons/dialogic/Editor/Images/Resources/portrait.svg new file mode 100644 index 0000000000000000000000000000000000000000..e7bc69d0eb930b115ffc67bb3f43758f3072bcab --- /dev/null +++ b/addons/dialogic/Editor/Images/Resources/portrait.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/Resources/portrait.svg.import b/addons/dialogic/Editor/Images/Resources/portrait.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..eea180b7725ee1d71db6393bd841a8cb9739efd0 --- /dev/null +++ b/addons/dialogic/Editor/Images/Resources/portrait.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dfi7fhfc4dbc3" +path="res://.godot/imported/portrait.svg-7d29c7cfe3e086d65dce33c3d66c48cd.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Resources/portrait.svg" +dest_files=["res://.godot/imported/portrait.svg-7d29c7cfe3e086d65dce33c3d66c48cd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Toolbar/add-character.svg b/addons/dialogic/Editor/Images/Toolbar/add-character.svg new file mode 100644 index 0000000000000000000000000000000000000000..9a420639b8d7788251fe7fe4449ba99c9fbe156a --- /dev/null +++ b/addons/dialogic/Editor/Images/Toolbar/add-character.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/dialogic/Editor/Images/Toolbar/add-character.svg.import b/addons/dialogic/Editor/Images/Toolbar/add-character.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..5f2b494d2ae6c4e942703ea75f4d3bbf7ad9f057 --- /dev/null +++ b/addons/dialogic/Editor/Images/Toolbar/add-character.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://my600mb32ydt" +path="res://.godot/imported/add-character.svg-a658b65c1225b02657a50d5c965e0d5e.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Toolbar/add-character.svg" +dest_files=["res://.godot/imported/add-character.svg-a658b65c1225b02657a50d5c965e0d5e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Toolbar/add-timeline.svg b/addons/dialogic/Editor/Images/Toolbar/add-timeline.svg new file mode 100644 index 0000000000000000000000000000000000000000..5fa7ac13772e891257f721fdc8f06728cc014e69 --- /dev/null +++ b/addons/dialogic/Editor/Images/Toolbar/add-timeline.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/addons/dialogic/Editor/Images/Toolbar/add-timeline.svg.import b/addons/dialogic/Editor/Images/Toolbar/add-timeline.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..807fec0a4d843b4cc045b905f1fd6062ca1f0391 --- /dev/null +++ b/addons/dialogic/Editor/Images/Toolbar/add-timeline.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bymlbr4o2m3jc" +path="res://.godot/imported/add-timeline.svg-86961b528ebdf01f585931a15fea1755.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Toolbar/add-timeline.svg" +dest_files=["res://.godot/imported/add-timeline.svg-86961b528ebdf01f585931a15fea1755.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Unknown.png b/addons/dialogic/Editor/Images/Unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..87ab9137a7d2ab521b1004222cb7070decd02141 Binary files /dev/null and b/addons/dialogic/Editor/Images/Unknown.png differ diff --git a/addons/dialogic/Editor/Images/Unknown.png.import b/addons/dialogic/Editor/Images/Unknown.png.import new file mode 100644 index 0000000000000000000000000000000000000000..7be217ad858bf36e05c7e8b0d8ddc8e5e00e6aca --- /dev/null +++ b/addons/dialogic/Editor/Images/Unknown.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbf2dlmbn12h0" +path="res://.godot/imported/Unknown.png-1cc7645f56036e8d378a70ac1dd772bb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Unknown.png" +dest_files=["res://.godot/imported/Unknown.png-1cc7645f56036e8d378a70ac1dd772bb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Editor/Images/dialogic-logo.svg b/addons/dialogic/Editor/Images/dialogic-logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..d6e5b6930b3ba89104e9e0ae81435573d9cbbb12 --- /dev/null +++ b/addons/dialogic/Editor/Images/dialogic-logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/addons/dialogic/Editor/Images/dialogic-logo.svg.import b/addons/dialogic/Editor/Images/dialogic-logo.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..cc2421bf51c1201b3fce6ceaad933b0ea9ae3e3b --- /dev/null +++ b/addons/dialogic/Editor/Images/dialogic-logo.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bt87p6qlso0ya" +path="res://.godot/imported/dialogic-logo.svg-e43201cabc9573eeb3f78fd91ea9d909.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/dialogic-logo.svg" +dest_files=["res://.godot/imported/dialogic-logo.svg-e43201cabc9573eeb3f78fd91ea9d909.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/dialogic/Editor/Images/plugin-icon.svg b/addons/dialogic/Editor/Images/plugin-icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..6f542b64a7538b815ad58c6f44f9f98d6931bf31 --- /dev/null +++ b/addons/dialogic/Editor/Images/plugin-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Editor/Images/plugin-icon.svg.import b/addons/dialogic/Editor/Images/plugin-icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..e6f203a60944faf647c178eb7d42f922da070bfb --- /dev/null +++ b/addons/dialogic/Editor/Images/plugin-icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dybg3l5pwetne" +path="res://.godot/imported/plugin-icon.svg-aa6701e8ed73f5fe5d177dfddce3a0e3.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/plugin-icon.svg" +dest_files=["res://.godot/imported/plugin-icon.svg-aa6701e8ed73f5fe5d177dfddce3a0e3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/preview_character.png b/addons/dialogic/Editor/Images/preview_character.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef6e853644a7d2bb7a1bf98e487c5f15847c3f6 Binary files /dev/null and b/addons/dialogic/Editor/Images/preview_character.png differ diff --git a/addons/dialogic/Editor/Images/preview_character.png.import b/addons/dialogic/Editor/Images/preview_character.png.import new file mode 100644 index 0000000000000000000000000000000000000000..abf95b106e6dec53e5067867f36cf287a379b192 --- /dev/null +++ b/addons/dialogic/Editor/Images/preview_character.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://41634vnjwsfw" +path="res://.godot/imported/preview_character.png-54f0625ad8281c635fea35a4930d95b6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/preview_character.png" +dest_files=["res://.godot/imported/preview_character.png-54f0625ad8281c635fea35a4930d95b6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Editor/Images/preview_character_speaker.png b/addons/dialogic/Editor/Images/preview_character_speaker.png new file mode 100644 index 0000000000000000000000000000000000000000..1e1562663021c908e055e76ae398a52d9c051b32 Binary files /dev/null and b/addons/dialogic/Editor/Images/preview_character_speaker.png differ diff --git a/addons/dialogic/Editor/Images/preview_character_speaker.png.import b/addons/dialogic/Editor/Images/preview_character_speaker.png.import new file mode 100644 index 0000000000000000000000000000000000000000..6f21e669916d9ef8e966fe543a7ddd1abe57e01f --- /dev/null +++ b/addons/dialogic/Editor/Images/preview_character_speaker.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dp7np6b4ipm0a" +path="res://.godot/imported/preview_character_speaker.png-c0667c648e2901adcbe8bf93ddda7f06.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/preview_character_speaker.png" +dest_files=["res://.godot/imported/preview_character_speaker.png-c0667c648e2901adcbe8bf93ddda7f06.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd b/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd new file mode 100644 index 0000000000000000000000000000000000000000..1b51f30d46b4e60dc0a062b7fc31a8ce55079c10 --- /dev/null +++ b/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd @@ -0,0 +1,13 @@ +@tool +extends Label + +# Called when the node enters the scene tree for the first time. +func _ready(): + # 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") diff --git a/addons/dialogic/Editor/Settings/settings_editor.gd b/addons/dialogic/Editor/Settings/settings_editor.gd new file mode 100644 index 0000000000000000000000000000000000000000..fef6f39cbc7069543c5c4f8696127ee1e92671a5 --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_editor.gd @@ -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(): + editors_manager.register_simple_editor(self) + self.alternative_text = "Customize dialogic and it's behaviour" + + +func _ready(): + 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 + 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() + # TODO recreate scroll to behaviour! +# if typeof(extra_information) == TYPE_STRING and has_node('Tabs/'+extra_information): +# $Tabs.current_tab = get_node('Tabs/'+extra_information).get_index() + + +func _close(): + 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(): + for child in %SettingsContent.get_children(): + if child.get_meta('section').has_method('_refresh'): + child.get_meta('section')._refresh() + diff --git a/addons/dialogic/Editor/Settings/settings_editor.tscn b/addons/dialogic/Editor/Settings/settings_editor.tscn new file mode 100644 index 0000000000000000000000000000000000000000..01d385f56c0c7ce9abd9c225fd318ef4f9599c5e --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_editor.tscn @@ -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 diff --git a/addons/dialogic/Editor/Settings/settings_general.gd b/addons/dialogic/Editor/Settings/settings_general.gd new file mode 100644 index 0000000000000000000000000000000000000000..62076e89d2361c3da672f159781a6e9a14e2fbb2 --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_general.gd @@ -0,0 +1,235 @@ +@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.pressed.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 :Array[DialogicEvent] = find_parent('Settings').editors_manager.resource_helper.event_script_cache + var sections := [] + var section_order :Array = DialogicUtil.get_editor_setting('event_section_order', ['Main', 'Logic', 'Timeline', 'Audio', 'Godot','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() + var _scale := DialogicUtil.get_editor_scale() + for color in DialogicUtil.get_color_palette(): + var button := ColorPickerButton.new() + button.custom_minimum_size = Vector2(50 ,50)*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(button_pressed:bool) -> void: + ProjectSettings.set_setting('dialogic/timer/process_in_physics', button_pressed) + 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 != 1: # 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( +"""@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 + + +################################################################################ +## INITIALIZE +################################################################################ + +# Set fixed settings of this event +func _init() -> void: + event_name = \""""+%NameEdit.text.capitalize()+"""\" + event_category = "Other" + +\n +################################################################################ +## 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() + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor() -> void: + pass""") + + 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( +"""extends DialogicSubsystem + +## Describe the subsystems purpose here. + + +#################################################################################################### +## STATE +#################################################################################################### + +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR): + pass + +func load_game_state(): + pass + + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +# Add some useful methods here. + +""") + 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() + diff --git a/addons/dialogic/Editor/Settings/settings_general.tscn b/addons/dialogic/Editor/Settings/settings_general.tscn new file mode 100644 index 0000000000000000000000000000000000000000..fb24630c93cdcc1af45def12daaedb32a9222843 --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_general.tscn @@ -0,0 +1,277 @@ +[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/FilePicker.tscn" id="3_i7rug"] + +[sub_resource type="Image" id="Image_pqmjp"] +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_fyskv"] +image = SubResource("Image_pqmjp") + +[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_fyskv") +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_fyskv") +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_fyskv") +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_fyskv") +hint_text = "Configure where dialogic looks for custom modules. + +You will have to restart the project to see the change take action." + +[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_fyskv") + +[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_fyskv") +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_fyskv") +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_fyskv") +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/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"] diff --git a/addons/dialogic/Editor/Settings/settings_modules.gd b/addons/dialogic/Editor/Settings/settings_modules.gd new file mode 100644 index 0000000000000000000000000000000000000000..f7a523a28ffb39c4ee3988bd055d1821d5463fad --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_modules.gd @@ -0,0 +1,404 @@ +@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: + 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)['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 :Array = find_parent('EditorsManager').resource_helper.event_script_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(): + 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_scenes(): + 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(): + 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: + # Label + var label := Label.new() + label.text = prop.capitalize() + %EventDefaults.add_child(label) + + # Editing field + var editor_node :Node = null + 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) + + 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/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/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) + + %EventDefaults.add_child(editor_node) + + +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 _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:Vector2) -> void: + set_event_default_override(prop, value) + +func _on_event_default_bool_toggled(value:bool, prop:String) -> void: + set_event_default_override(prop, value) + diff --git a/addons/dialogic/Editor/Settings/settings_modules.tscn b/addons/dialogic/Editor/Settings/settings_modules.tscn new file mode 100644 index 0000000000000000000000000000000000000000..bd5bf0ae2d3323d13c211f84ff687b30a825d6cc --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_modules.tscn @@ -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_pu0o6"] +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_pu0o6") + +[sub_resource type="Image" id="Image_g84xy"] +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_g84xy") + +[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 = 2 + +[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"] diff --git a/addons/dialogic/Editor/Settings/settings_page.gd b/addons/dialogic/Editor/Settings/settings_page.gd new file mode 100644 index 0000000000000000000000000000000000000000..b11a1fc9808efa0d4344007759091b7788b0185f --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_page.gd @@ -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 diff --git a/addons/dialogic/Editor/Settings/settings_translation.gd b/addons/dialogic/Editor/Settings/settings_translation.gd new file mode 100644 index 0000000000000000000000000000000000000000..43458dc741731048791d064a6cbdab02d8fadd09 --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_translation.gd @@ -0,0 +1,254 @@ +@tool +extends DialogicSettingsPage + +## Settings tab that allows enabeling and updating translation csv-files. + + +enum TranslationModes {PER_PROJECT, PER_TIMELINE} +var loading := false +@onready var settings_editor :Control = find_parent('Settings') + + +func _get_icon(): + return get_theme_icon("Translation", "EditorIcons") + +func _get_info_section() -> Control: + return $InfoSection + +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) + + %UpdateCsvFiles.pressed.connect(update_csv_files) + %CollectTranslations.pressed.connect(collect_translations) + %TransRemove.pressed.connect(_on_erase_translations_pressed) + + + +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', '')) + loading = false + + +func store_changes(fake_arg = "", fake_arg2 = "") -> void: + if loading: return + 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.save() + + +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()} + for locale in TranslationServer.get_all_languages(): + suggestions[locale] = {'value':locale, 'tooltip':TranslationServer.get_language_name(locale)} + return suggestions + + +func update_csv_files() -> void: + var orig_locale :String= %OrigLocale.current_value.strip_edges() + if orig_locale.is_empty(): + orig_locale = ProjectSettings.get_setting('internationalization/locale/fallback') + %OrigLocale.set_value(orig_locale) + + var translation_mode :int = %TransMode.selected + + var counts := [0,0,0,0] # [new events, new_timelines, updated_events, updated_timelines] + var file : FileAccess + var csv_lines := [] # collects all current lines + var old_csv_lines := {} # contains already existing csv_lines as [key] = [value, value, ...] dict + + settings_editor.editors_manager.clear_editor(settings_editor.editors_manager.editors['Timeline']['node']) + + # collect old lines in per project mode + if translation_mode == TranslationModes.PER_PROJECT: + var file_path :String= ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://').path_join('dialogic_translations.csv') + if FileAccess.file_exists(file_path): + file = FileAccess.open(file_path,FileAccess.READ_WRITE) + counts[3] += 1 + while !file.eof_reached(): + var line := file.get_csv_line() + old_csv_lines[line[0]] = line + else: + counts[1] += 1 + csv_lines.append(['keys', orig_locale]) + + for timeline_path in DialogicUtil.list_resources_of_type('.dtl'): + + # collect old lines in per timeline mode + var file_path :String= timeline_path.trim_suffix('.dtl')+'_translation.csv' + if translation_mode == TranslationModes.PER_TIMELINE: + if FileAccess.file_exists(file_path): + file = FileAccess.open(file_path,FileAccess.READ_WRITE) + while !file.eof_reached(): + var line := file.get_csv_line() + old_csv_lines[line[0]] = line + csv_lines.append(['keys', orig_locale]) + + # load and process timeline (make events to resources) + var tml : DialogicTimeline = load(timeline_path) + await tml.process() + + # now collect all the current csv_lines from timeline + for event in tml.events: + if event.can_be_translated(): + if event._translation_id.is_empty(): + event.add_translation_id() + event.update_text_version() + for property in event._get_translatable_properties(): + csv_lines.append([event.get_property_translation_key(property), event._get_property_original_translation(property)]) + + # in case new translation_id's were added, we save the timeline again + tml.set_meta("timeline_not_saved", true) + ResourceSaver.save(tml, timeline_path) + + # for per_timeline mode save the file now, then reset for next timeline + if translation_mode == TranslationModes.PER_TIMELINE: + if !FileAccess.file_exists(file_path): + pass#counts[1] += 1 + elif len(csv_lines): + counts[3] += 1 + file = FileAccess.open(file_path, FileAccess.WRITE) + for line in csv_lines: + # in case there might be translations for this line already, + # add them at the end again (orig locale text is replaced). + if line[0] in old_csv_lines: + file.store_csv_line(line+Array(old_csv_lines[line[0]]).slice(2)) + counts[2] += 1 + else: + file.store_csv_line(line) + counts[0] += 1 + + csv_lines.clear() + old_csv_lines.clear() + + if translation_mode == TranslationModes.PER_PROJECT: + var file_path :String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://').path_join('dialogic_translations.csv') + if FileAccess.file_exists(file_path): + counts[3] += 1 + else: + counts[1] += 1 + file = FileAccess.open(file_path, FileAccess.WRITE) + for line in csv_lines: + # in case there might be translations for this line already, + # add them at the end again (orig locale text is replaced). + if line[0] in old_csv_lines: + file.store_csv_line(PackedStringArray(line)+old_csv_lines[line[0]].slice(2)) + counts[2] += 1 + else: + file.store_csv_line(line) + counts[0] += 1 + + ## ADD CREATION/UPDATE OF CHARACTER NAMES FILE HERE! + + # trigger reimport + find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources() + %StatusMessage.text = "Indexed "+str(counts[0])+" new events ("+str(counts[2])+" were updated). \nAdded "+str(counts[1])+" new csv files ("+str(counts[3])+" were updated)." + + +func collect_translations() -> void: + var trans_files := [] + var translation_mode :int = %TransMode.selected + + if translation_mode == TranslationModes.PER_TIMELINE: + for timeline_path in DialogicUtil.list_resources_of_type('.dtl'): + for file 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 trans_files: + trans_files.append(file) + + if translation_mode == TranslationModes.PER_PROJECT: + var trans_folder :String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://') + for file in DialogicUtil.listdir(trans_folder): + file = trans_folder.path_join(file) + if file.ends_with('.translation'): + if not file in trans_files: + trans_files.append(file) + + var all_trans_files : Array = ProjectSettings.get_setting('internationalization/locale/translations', []) + var orig_file_amount := len(all_trans_files) + for file in trans_files: + if not file in all_trans_files: + all_trans_files.append(file) + + ProjectSettings.set_setting('internationalization/locale/translations', PackedStringArray(all_trans_files)) + ProjectSettings.save() + + %StatusMessage.text = "Collected "+str(len(all_trans_files)-orig_file_amount) + " new translation files." + + +func _on_erase_translations_pressed(): + $EraseConfirmationDialog.popup_centered() + + +func erase_translations() -> void: + var trans_files := Array(ProjectSettings.get_setting('internationalization/locale/translations', [])) + var translation_mode : int = %TransMode.selected + + var counts := [0,0] # csv files, translation files + + if translation_mode == TranslationModes.PER_PROJECT: + var trans_path :String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://') + DirAccess.remove_absolute(trans_path+'dialogic_translations.csv') + DirAccess.remove_absolute(trans_path+'dialogic_translations.csv.import') + counts[0] += 1 + for x_file in DialogicUtil.listdir(trans_path): + if x_file.ends_with('.translation'): + trans_files.erase(trans_path.get_base_dir().path_join(x_file)) + DirAccess.remove_absolute(trans_path.get_base_dir().path_join(x_file)) + counts[1] += 1 + + for timeline_path in DialogicUtil.list_resources_of_type('.dtl'): + # in per project mode, remove all translation files/resources next to the timelines + if translation_mode == TranslationModes.PER_TIMELINE: + DirAccess.remove_absolute(timeline_path.trim_suffix('.dtl')+'_translation.csv') + DirAccess.remove_absolute(timeline_path.trim_suffix('.dtl')+'_translation.csv.import') + counts[0] += 1 + for x_file in DialogicUtil.listdir(timeline_path.get_base_dir()): + if x_file.ends_with('.translation'): + trans_files.erase(timeline_path.get_base_dir().path_join(x_file)) + DirAccess.remove_absolute(timeline_path.get_base_dir().path_join(x_file)) + counts[1] += 1 + + # clear the timeline events of their translation_id's + var tml:DialogicTimeline = load(timeline_path) + await tml.process() + + for event in tml.events: + if event._translation_id: + event.remove_translation_id() + event.update_text_version() + tml.set_meta("timeline_not_saved", true) + ResourceSaver.save(tml, timeline_path) + + ProjectSettings.set_setting('dialogic/translation/id_counter', 16) + ProjectSettings.set_setting('internationalization/locale/translations', PackedStringArray(trans_files)) + ProjectSettings.save() + + find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources() + + %StatusMessage.text = "Removed "+str(counts[0])+" csv files, "+str(counts[1])+" translations and all translation id's." + _refresh() + diff --git a/addons/dialogic/Editor/Settings/settings_translation.tscn b/addons/dialogic/Editor/Settings/settings_translation.tscn new file mode 100644 index 0000000000000000000000000000000000000000..9cc11428e4b5dfa383b95106cf3e2176fd76d350 --- /dev/null +++ b/addons/dialogic/Editor/Settings/settings_translation.tscn @@ -0,0 +1,300 @@ +[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/ComplexPicker.tscn" id="3_dq4j2"] +[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="4_kvsma"] + +[sub_resource type="Image" id="Image_o33h3"] +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_w0vi3"] +image = SubResource("Image_o33h3") + +[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_w0vi3") +hint_text = "Change this locale to test your game in a different language (only in-editor). +Equivalent of the testing local project setting. " + +[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="Title2" 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 = "This should be the local of the language your game is written in." +texture = SubResource("ImageTexture_w0vi3") +hint_text = "This should be the local of the language your game is written in." + +[node name="OrigLocale" parent="TranslationSettings/VBoxContainer/Grid" instance=ExtResource("3_dq4j2")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="VBox2" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"] +layout_mode = 2 + +[node name="Label2" type="Label" parent="TranslationSettings/VBoxContainer/Grid/VBox2"] +layout_mode = 2 +text = "Translation file mode" + +[node name="HintTooltip2" parent="TranslationSettings/VBoxContainer/Grid/VBox2" instance=ExtResource("2_k2lou")] +layout_mode = 2 +tooltip_text = "You can either create one csv with all the strings from the whole +project or one csv file per timeline (will be placed next to that timeline). " +texture = SubResource("ImageTexture_w0vi3") +hint_text = "You can either create one csv with all the strings from the whole +project or one csv file per timeline (will be placed next to that timeline). " + +[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 Project" +popup/item_0/id = 0 +popup/item_1/text = "Per Timeline" +popup/item_1/id = 1 + +[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 = "Translations folder" + +[node name="HintTooltip3" parent="TranslationSettings/VBoxContainer/Grid/TransFile" instance=ExtResource("2_k2lou")] +layout_mode = 2 +tooltip_text = "Choose a folder general translations will be saved to. " +texture = SubResource("ImageTexture_w0vi3") +hint_text = "Choose a folder general translations will be saved to. " + +[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="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="HintTooltip4" parent="TranslationSettings/VBoxContainer2/HBoxContainer" instance=ExtResource("2_k2lou")] +layout_mode = 2 +tooltip_text = "Check the Information to learn more about the individual actions." +texture = SubResource("ImageTexture_w0vi3") +hint_text = "Check the Information to learn more about the individual 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 +text = "Update CSV files" + +[node name="HintTooltip5" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")] +layout_mode = 2 +tooltip_text = "Will check all timelines for events that haven't been added to the csv file(s) and adds them. " +texture = SubResource("ImageTexture_w0vi3") +hint_text = "Will check all timelines for events that haven't been added to the csv file(s) and adds them. " + +[node name="CollectTranslations" type="Button" parent="TranslationSettings/VBoxContainer2/Actions"] +unique_name_in_owner = true +layout_mode = 2 +text = "Collect translations" + +[node name="HintTooltip6" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")] +layout_mode = 2 +tooltip_text = "When you've added translations to a csv file, +godot will import them as .translation files. +These have to be added to a list (Project Settings > Localization) +and this is a quick way to do so." +texture = SubResource("ImageTexture_w0vi3") +hint_text = "When you've added translations to a csv file, +godot will import them as .translation files. +These have to be added to a list (Project Settings > Localization) +and this is a quick way to do so." + +[node name="HSeparator3" type="HSeparator" parent="TranslationSettings/VBoxContainer2/Actions"] +layout_mode = 2 + +[node name="HSeparator4" type="HSeparator" parent="TranslationSettings/VBoxContainer2/Actions"] +layout_mode = 2 + +[node name="TransRemove" type="Button" parent="TranslationSettings/VBoxContainer2/Actions"] +unique_name_in_owner = true +layout_mode = 2 +text = "Remove translations" + +[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. +It will also remove translation ids from all events. " +texture = SubResource("ImageTexture_w0vi3") +hint_text = "Be very careful with this button. It will try to delete any +.csv and .translation files that are related to dialogic. +It will also remove translation ids from all events. " + +[node name="StatusMessage" type="Label" parent="TranslationSettings/VBoxContainer2"] +unique_name_in_owner = true +layout_mode = 2 +autowrap_mode = 3 + +[node name="InfoSection" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="Setup" type="VBoxContainer" parent="InfoSection"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="SetupTitle" type="Label" parent="InfoSection/Setup"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "Setting up translations" + +[node name="Label2" type="Label" parent="InfoSection/Setup"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "1. Enable translations +2. Set default locale (the language you used) and file mode. For per-project-mode also select a folder. In per-timeline mode files will be put next to timelines (and need to stay there!). +3. Hit \"Update CSV files\". This will create new csv files containing all translatable strings from your timelnes as well as one for the character names. +4. Wait until reimport is finnished. Then click \"Collect translations\"" +autowrap_mode = 3 + +[node name="HSeparator" type="HSeparator" parent="InfoSection"] +layout_mode = 2 + +[node name="Workflow" type="VBoxContainer" parent="InfoSection"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="WorkflowTitle" type="Label" parent="InfoSection/Workflow"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "How to work with translations" + +[node name="Label" type="Label" parent="InfoSection/Workflow"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Editing: +You can edit the csv files in most table editors (like google sheets or libre office calc). You can add new languages by creating a new column with a valid locale in the first cell. After doing this you will have to hit \"Collect translations\" again. + +Updating: +If you make changes to your timelines after having created translations, just hit \"Update CSV files\" again, it will not remove changes already made to the file. + +Testing: +You can test in different languages by editing the testing locale. + +" +autowrap_mode = 3 + +[node name="EraseConfirmationDialog" type="ConfirmationDialog" parent="."] +size = Vector2i(200, 187) +ok_button_text = "Yes" +dialog_text = "This will permanently delete all translation files created by dialogic. Are you sure this is what you want?" +dialog_autowrap = true + +[connection signal="confirmed" from="EraseConfirmationDialog" to="." method="erase_translations"] diff --git a/addons/dialogic/Editor/Theme/MainTheme.tres b/addons/dialogic/Editor/Theme/MainTheme.tres new file mode 100644 index 0000000000000000000000000000000000000000..bd77e0f8dc2cf90ecb1baf9d3a2fd1dc81791112 --- /dev/null +++ b/addons/dialogic/Editor/Theme/MainTheme.tres @@ -0,0 +1,3 @@ +[gd_resource type="Theme" format=3 uid="uid://cqst728xxipcw"] + +[resource] diff --git a/addons/dialogic/Editor/Theme/PickerTheme.tres b/addons/dialogic/Editor/Theme/PickerTheme.tres new file mode 100644 index 0000000000000000000000000000000000000000..36b1955360b6536444dd4ef945c9063ccc6cab13 --- /dev/null +++ b/addons/dialogic/Editor/Theme/PickerTheme.tres @@ -0,0 +1,7 @@ +[gd_resource type="Theme" format=2] + +[resource] +Button/colors/font_color = Color( 1, 1, 1, 1 ) +Button/colors/font_color_disabled = Color( 0.901961, 0.901961, 0.901961, 0.2 ) +Button/colors/font_color_hover = Color( 0.870588, 0.870588, 0.870588, 1 ) +Button/colors/font_color_pressed = Color( 1, 1, 1, 1 ) diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd new file mode 100644 index 0000000000000000000000000000000000000000..f21dd42d064943c45b485539cc02125de357db16 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd @@ -0,0 +1,269 @@ +@tool +extends Node + +enum Modes {TEXT_EVENT_ONLY, FULL_HIGHLIGHTING} +@export var mode := Modes.FULL_HIGHLIGHTING + +# These RegEx's are used to deduce information from the current line for auto-completion + +# To find the currently typed word and the symbol before +var completion_word_regex := RegEx.new() +# To find the shortcode of the current shortcode event (basically the type) +var completion_shortcode_getter_regex := RegEx.new() +# To find the parameter name of the current if typing a value +var completion_shortcode_param_getter_regex := RegEx.new() + +# Stores references to all shortcode events for parameter and value suggestions +var shortcode_events := {} +var custom_syntax_events := [] +var text_event :DialogicTextEvent = null + +func _ready(): + # Compile RegEx's + completion_word_regex.compile("(?(\\W)|^)(?\\w*)\\x{FFFF}") + completion_shortcode_getter_regex.compile("\\[(?\\w*)") + completion_shortcode_param_getter_regex.compile("(?\\w*)\\W*=\\s*\"?(\\w|\\s)*"+String.chr(0xFFFF)) + +################################################################################ +## AUTO COMPLETION +################################################################################ + +# Helper that gets the current line with a special character where the caret is +func get_code_completion_line(text:CodeEdit) -> String: + return text.get_line(text.get_caret_line()).insert(text.get_caret_column(), String.chr(0xFFFF)).strip_edges() + + +# Helper that gets the currently typed word +func get_code_completion_word(text:CodeEdit) -> String: + var result := completion_word_regex.search(get_code_completion_line(text)) + return result.get_string('word') if result else "" + + +# Helper that gets the symbol before the current word +func get_code_completion_prev_symbol(text:CodeEdit) -> String: + var result := completion_word_regex.search(get_code_completion_line(text)) + return result.get_string('s') if result else "" + + +func get_line_untill_caret(line:String) -> String: + return line.substr(0, line.find(String.chr(0xFFFF))) + + +# Called if something was typed +# Adds all kinds of options depending on the +# content of the current line, the last word and the symbol that came before +# Triggers opening of the popup +func request_code_completion(force:bool, text:CodeEdit) -> void: + ## TODO remove this once https://github.com/godotengine/godot/issues/38560 is fixed + if mode != Modes.FULL_HIGHLIGHTING: + return + + # make sure shortcode event references are loaded + if mode == Modes.FULL_HIGHLIGHTING: + var hidden_events :Array= DialogicUtil.get_editor_setting('hidden_event_buttons') + if shortcode_events.is_empty(): + for event in text.timeline_editor.editors_manager.resource_helper.event_script_cache: + if event.get_shortcode() != 'default_shortcode': + shortcode_events[event.get_shortcode()] = event + + else: + custom_syntax_events.append(event) + if event.event_name in hidden_events: + event.set_meta('hidden', true) + if event is DialogicTextEvent: + text_event = event + # this is done to force-load the text effects regex which is used below + event.load_text_effects() + + # fill helpers + var line := get_code_completion_line(text) + var word := get_code_completion_word(text) + var symbol := get_code_completion_prev_symbol(text) + + + ## Note on use of KIND types for options. + # These types are mostly useless for us. + # However I decidede to assign some special cases for them: + # - KIND_PLAIN_TEXT is only shown if the beginnging of the option is already typed + # !word.is_empty() and option.begins_with(word) + # - KIND_CLASS is only shown if anything from the options is already typed + # !word.is_empty() and word in option + # - KIND_CONSTANT is shown and checked against the beginning + # option.begins_with(word) + # - KIND_MEMBER is shown and searched completely + # word in option + + ## Note on VALUE key + # The value key is used to store a potential closing letter for the completion. + # The completion will check if the letter is already present and add it otherwise. + + # Shortcode event suggestions + if line.begins_with('[') and !text_event.text_effects_regex.search(line.get_slice(' ', 0)) and mode == Modes.FULL_HIGHLIGHTING: + if symbol == '[': + # suggest shortcodes if a shortcode event has just begun + var shortcodes := shortcode_events.keys() + shortcodes.sort() + for shortcode in shortcodes: + if shortcode_events[shortcode].get_meta('hidden', false): + continue + if shortcode_events[shortcode].get_shortcode_parameters().is_empty(): + text.add_code_completion_option(CodeEdit.KIND_MEMBER, shortcode, shortcode, shortcode_events[shortcode].event_color.lerp(text.syntax_highlighter.normal_color, 0.3), shortcode_events[shortcode]._get_icon()) + else: + text.add_code_completion_option(CodeEdit.KIND_MEMBER, shortcode, shortcode+" ", shortcode_events[shortcode].event_color.lerp(text.syntax_highlighter.normal_color, 0.3), shortcode_events[shortcode]._get_icon()) + else: + var current_shortcode := completion_shortcode_getter_regex.search(line) + if !current_shortcode: + text.update_code_completion_options(false) + return + + var code := current_shortcode.get_string('code') + if !code in shortcode_events.keys(): + text.update_code_completion_options(false) + return + + # suggest parameters + if symbol == ' ': + var parameters :Array = shortcode_events[code].get_shortcode_parameters().keys() + for param in parameters: + if !param+'=' in line: + text.add_code_completion_option(CodeEdit.KIND_MEMBER, param, param+'="' , shortcode_events[code].event_color.lerp(text.syntax_highlighter.normal_color, 0.3), text.get_theme_icon("MemberProperty", "EditorIcons")) + + # suggest values + elif symbol == '=' or symbol == '"' or get_code_completion_prev_symbol(text) == '"': + var current_parameter_gex := completion_shortcode_param_getter_regex.search(line) + if !current_parameter_gex: + text.update_code_completion_options(false) + return + + var current_parameter := current_parameter_gex.get_string('param') + if !shortcode_events[code].get_shortcode_parameters().has(current_parameter): + text.update_code_completion_options(false) + return + if !shortcode_events[code].get_shortcode_parameters()[current_parameter].has('suggestions'): + if typeof(shortcode_events[code].get_shortcode_parameters()[current_parameter].default) == TYPE_BOOL: + suggest_bool(text, shortcode_events[code].event_color.lerp(text.syntax_highlighter.normal_color, 0.3)) + elif len(word) > 0: + text.add_code_completion_option(CodeEdit.KIND_MEMBER, word, word, shortcode_events[code].event_color.lerp(text.syntax_highlighter.normal_color, 0.3), text.get_theme_icon("GuiScrollArrowRight", "EditorIcons"), '" ') + text.update_code_completion_options(true) + return + + var suggestions : Dictionary= shortcode_events[code].get_shortcode_parameters()[current_parameter]['suggestions'].call() + for key in suggestions.keys(): + text.add_code_completion_option(CodeEdit.KIND_MEMBER, key, suggestions[key].value, shortcode_events[code].event_color.lerp(text.syntax_highlighter.normal_color, 0.3), suggestions[key].get('icon', null), '" ') + + # Force update and showing of the popup + text.update_code_completion_options(true) + return + + + for event in custom_syntax_events: + if mode == Modes.TEXT_EVENT_ONLY and !event is DialogicTextEvent: + continue + + if ! ' ' in line: + event._get_start_code_completion(self, text) + + if event.is_valid_event(line): + event._get_code_completion(self, text, line, word, symbol) + break + + # Force update and showing of the popup + text.update_code_completion_options(true) + + + +# Helper that adds all characters as options +func suggest_characters(text:CodeEdit, type := CodeEdit.KIND_MEMBER) -> void: + for character in text.timeline_editor.editors_manager.resource_helper.character_directory: + text.add_code_completion_option(type, character, character, text.syntax_highlighter.character_name_color, load("res://addons/dialogic/Editor/Images/Resources/character.svg")) + + +# Helper that adds all timelines as options +func suggest_timelines(text:CodeEdit, type := CodeEdit.KIND_MEMBER, color:=Color()) -> void: + for timeline in text.timeline_editor.editors_manager.resource_helper.timeline_directory: + text.add_code_completion_option(type, timeline, timeline+'/', color, text.get_theme_icon("TripleBar", "EditorIcons")) + + +func suggest_labels(text:CodeEdit, timeline:String='', end:='', color:=Color()) -> void: + if timeline in Engine.get_main_loop().get_meta('dialogic_label_directory', {}): + for i in Engine.get_main_loop().get_meta('dialogic_label_directory')[timeline]: + text.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+end, color, load("res://addons/dialogic/Modules/Jump/icon_label.png")) + + +# Helper that adds all portraits of a given character as options +func suggest_portraits(text:CodeEdit, character_name:String, end_check:=')') -> void: + if !character_name in text.timeline_editor.editors_manager.resource_helper.character_directory: + return + var character_resource :DialogicCharacter= text.timeline_editor.editors_manager.resource_helper.character_directory[character_name]['resource'] + for portrait in character_resource.portraits: + text.add_code_completion_option(CodeEdit.KIND_MEMBER, portrait, portrait, text.syntax_highlighter.character_portrait_color, load("res://addons/dialogic/Editor/Images/Resources/character.svg"), end_check) + if character_resource.portraits.is_empty(): + text.add_code_completion_option(CodeEdit.KIND_MEMBER, 'Has no portraits!', '', text.syntax_highlighter.character_portrait_color, load("res://addons/dialogic/Editor/Images/Pieces/warning.svg")) + + +# Helper that adds all variable paths as options +func suggest_variables(text:CodeEdit): + for variable in DialogicUtil.list_variables(ProjectSettings.get_setting('dialogic/variables')): + text.add_code_completion_option(CodeEdit.KIND_MEMBER, variable, variable, text.syntax_highlighter.variable_color, text.get_theme_icon("MemberProperty", "EditorIcons"), '}') + + +# Helper that adds true and false as options +func suggest_bool(text:CodeEdit, color:Color): + text.add_code_completion_option(CodeEdit.KIND_MEMBER, 'true', 'true', color, text.get_theme_icon("GuiChecked", "EditorIcons"), '" ') + text.add_code_completion_option(CodeEdit.KIND_MEMBER, 'false', 'false', color, text.get_theme_icon("GuiUnchecked", "EditorIcons"), '" ') + + +# Filters the list of all possible options, depending on what was typed +# Purpose of the different Kinds is explained in [_request_code_completion] +func filter_code_completion_candidates(candidates:Array, text:CodeEdit) -> Array: + var valid_candidates := [] + var current_word := get_code_completion_word(text) + for candidate in candidates: + if candidate.kind == text.KIND_PLAIN_TEXT: + if !current_word.is_empty() and candidate.insert_text.begins_with(current_word): + valid_candidates.append(candidate) + elif candidate.kind == text.KIND_MEMBER: + if current_word.is_empty() or current_word.to_lower() in candidate.insert_text.to_lower(): + valid_candidates.append(candidate) + elif candidate.kind == text.KIND_CONSTANT: + if current_word.is_empty() or candidate.insert_text.begins_with(current_word): + valid_candidates.append(candidate) + elif candidate.kind == text.KIND_CLASS: + if !current_word.is_empty() and current_word.to_lower() in candidate.insert_text.to_lower(): + valid_candidates.append(candidate) + return valid_candidates + + +# Called when code completion was activated +# Inserts the selected item +func confirm_code_completion(replace:bool, text:CodeEdit) -> void: + # Note: I decided to ALWAYS use replace mode, as dialogic is supposed to be beginner friendly + var word := get_code_completion_word(text) + var code_completion := text.get_code_completion_option(text.get_code_completion_selected_index()) + text.remove_text(text.get_caret_line(), text.get_caret_column()-len(word), text.get_caret_line(), text.get_caret_column()) + text.set_caret_column(text.get_caret_column()-len(word)) + text.insert_text_at_caret(code_completion.insert_text)# + if code_completion.has('default_value') and typeof(code_completion['default_value']) == TYPE_STRING: + var next_letter := text.get_line(text.get_caret_line()).substr(text.get_caret_column(), 1) + if next_letter != code_completion['default_value']: + text.insert_text_at_caret(code_completion['default_value']) + else: + text.set_caret_column(text.get_caret_column()+1) + + +################################################################################ +## SYMBOL CLICKING +################################################################################ + +# Performs an action (like opening a link) when a valid symbol was clicked +func symbol_lookup(symbol:String, line:int, column:int) -> void: + if symbol in shortcode_events.keys(): + if !shortcode_events[symbol].help_page_path.is_empty(): + OS.shell_open(shortcode_events[symbol].help_page_path) + + +# Called to test if a symbol can be clicked +func symbol_validate(symbol:String, text:CodeEdit) -> void: + if symbol in shortcode_events.keys(): + if !shortcode_events[symbol].help_page_path.is_empty(): + text.set_symbol_lookup_word_as_valid(true) diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd new file mode 100644 index 0000000000000000000000000000000000000000..4989ea087cb90410bcdf88556c1cf3b3a63592dd --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd @@ -0,0 +1,153 @@ +@tool +extends SyntaxHighlighter + +## Syntax highlighter for the dialogic text timeline editor and text events in the visual editor. + +enum Modes {TEXT_EVENT_ONLY, FULL_HIGHLIGHTING} +var mode := Modes.FULL_HIGHLIGHTING + + +## RegEx's +var word_regex := RegEx.new() +var region_regex := RegEx.new() +var number_regex := RegEx.create_from_string("(\\d|\\.)+") +var shortcode_regex := RegEx.create_from_string("\\W*\\[(?\\w*)(?[^\\]]*)?") +var shortcode_param_regex := RegEx.create_from_string('((?[^\\s=]*)\\s*=\\s*"(?([^=]|\\\\=)*)(? Dictionary: + var str_line := get_text_edit().get_line(line) + + if shortcode_events.is_empty(): + for event in Engine.get_main_loop().get_meta('dialogic_event_cache', []): + if event.get_shortcode() != 'default_shortcode': + shortcode_events[event.get_shortcode()] = event + else: + custom_syntax_events.append(event) + if event is DialogicTextEvent: + text_event = event + text_event.load_text_effects() + + var dict := {} + dict[0] = {'color':normal_color} + + dict = color_translation_id(dict, str_line) + + if mode == Modes.FULL_HIGHLIGHTING: + if str_line.strip_edges().begins_with("[") and !text_event.text_effects_regex.search(str_line.get_slice(' ', 0)): + var result:= shortcode_regex.search(str_line) + if result: + if result.get_string('id') in shortcode_events: + dict[result.get_start('id')] = {"color":shortcode_events[result.get_string('id')].event_color.lerp(normal_color, 0.4)} + dict[result.get_end('id')] = {"color":normal_color} + + if result.get_string('args'): + color_shortcode_content(dict, str_line, result.get_start('args'), result.get_end('args'), shortcode_events[result.get_string('id')].event_color) + return fix_dict(dict) + + else: + for event in custom_syntax_events: + if event.is_valid_event(str_line.strip_edges()): + dict = event._get_syntax_highlighting(self, dict, str_line) + return fix_dict(dict) + + else: + dict = text_event._get_syntax_highlighting(self, dict, str_line) + return fix_dict(dict) + + +func fix_dict(dict:Dictionary) -> Dictionary: + var d := {} + var k := dict.keys() + k.sort() + for i in k: + d[i] = dict[i] + return d + + +func color_condition(dict:Dictionary, line:String, from:int = 0, to:int = 0) -> Dictionary: + dict = color_word(dict, code_flow_color, line, 'or', from, to) + dict = color_word(dict, code_flow_color, line, 'and', from, to) + dict = color_word(dict, code_flow_color, line, '==', from, to) + dict = color_word(dict, code_flow_color, line, '!=', from, to) + if !">=" in line: + dict = color_word(dict, code_flow_color, line, '>', from, to) + else: + dict = color_word(dict, code_flow_color, line, '>=', from, to) + if !"<=" in line: + dict = color_word(dict, code_flow_color, line, '<', from, to) + else: + dict = color_word(dict, code_flow_color, line, '<=', from, to) + dict = color_region(dict, variable_color, line, '{', '}', from, to) + dict = color_region(dict, string_color, line, '"', '"', from, to) + + + return dict + + +func color_translation_id(dict:Dictionary, line:String) -> Dictionary: + dict = color_region(dict, translation_id_color, line, '#id:', '') + return dict + + +func color_word(dict:Dictionary, color:Color, line:String, word:String, from:int= 0, to:int = 0) -> Dictionary: + word_regex.compile("\\W(?"+word+")\\W") + if to <= from: + to = len(line)-1 + for i in word_regex.search_all(line.substr(from, to-from+2)): + dict[i.get_start('word')+from] = {'color':color} + dict[i.get_end('word')+from] = {'color':normal_color} + return dict + + +func color_region(dict:Dictionary, color:Color, line:String, start:String, end:String, from:int = 0, to:int = 0) -> Dictionary: + if end.is_empty(): + region_regex.compile("(? Dictionary: + if to <= from: + to = len(line)-1 + var args_result:= shortcode_param_regex.search_all(line.substr(from, to-from+2)) + for x in args_result: + dict[x.get_start()+from] = {"color":base_color.lerp(normal_color, 0.5)} + dict[x.get_start('value')+from-1] = {"color":base_color.lerp(normal_color, 0.7)} + dict[x.get_end()+from] = {"color":normal_color} + return dict diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd new file mode 100644 index 0000000000000000000000000000000000000000..e1148c041d782ef9c0b618c0a33bc3991d76f544 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd @@ -0,0 +1,235 @@ +@tool +extends CodeEdit + +## Sub-Editor that allows editing timelines in a text format. + +@onready var timeline_editor := get_parent().get_parent() + +var label_regex := RegEx.create_from_string('label +(?[^\n]+)') + +func _ready(): + syntax_highlighter = load("res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd").new() + + await find_parent('EditorView').ready + timeline_editor.editors_manager.sidebar.content_item_activated.connect(_on_content_item_clicked) + +func _on_text_editor_text_changed(): + timeline_editor.current_resource_state = DialogicEditor.ResourceStates.UNSAVED + request_code_completion(true) + $UpdateTimer.start() + + +func clear_timeline(): + text = '' + update_content_list() + + +func load_timeline(timeline:DialogicTimeline) -> void: + clear_timeline() + + text = timeline.as_text() + + timeline_editor.current_resource.set_meta("timeline_not_saved", false) + await get_tree().process_frame + update_content_list() + + +func save_timeline(): + if !timeline_editor.current_resource: + return + + var text_array:Array = text_timeline_to_array(text) + + timeline_editor.current_resource.events = text_array + timeline_editor.current_resource.events_processed = false + ResourceSaver.save(timeline_editor.current_resource, timeline_editor.current_resource.resource_path) + + timeline_editor.current_resource.set_meta("timeline_not_saved", false) + timeline_editor.current_resource_state = DialogicEditor.ResourceStates.SAVED + timeline_editor.editors_manager.resource_helper.rebuild_timeline_directory() + + +func text_timeline_to_array(text:String) -> Array: + # Parse the lines down into an array + var events := [] + + var lines := text.split('\n', true) + var idx := -1 + + while idx < len(lines)-1: + idx += 1 + var line :String = lines[idx] + var line_stripped :String = line.strip_edges(true, true) + events.append(line) + + return events + + +################################################################################ +## HELPFUL EDITOR FUNCTIONALITY +################################################################################ + +func _gui_input(event): + if not event is InputEventKey: return + if not event.is_pressed(): return + match event.as_text(): + "Ctrl+K": + toggle_comment() + "Alt+Up": + move_line(-1) + "Alt+Down": + move_line(1) + "Ctrl+Shift+D": + duplicate_line() + _: + return + get_viewport().set_input_as_handled() + +# Toggle the selected lines as comments +func toggle_comment() -> void: + var cursor: Vector2 = Vector2(get_caret_column(), get_caret_line()) + var from: int = cursor.y + var to: int = cursor.y + if has_selection(): + from = get_selection_from_line() + to = get_selection_to_line() + + var lines: PackedStringArray = text.split("\n") + var will_comment: bool = not lines[from].begins_with("# ") + for i in range(from, to + 1): + lines[i] = "# " + lines[i] if will_comment else lines[i].substr(2) + + text = "\n".join(lines) + select(from, 0, to, get_line_width(to)) + set_caret_line(cursor.y) + set_caret_column(cursor.x) + text_changed.emit() + + +# Move the selected lines up or down +func move_line(offset: int) -> void: + offset = clamp(offset, -1, 1) + + var cursor: Vector2 = Vector2(get_caret_column(), get_caret_line()) + var reselect: bool = false + var from: int = cursor.y + var to: int = cursor.y + if has_selection(): + reselect = true + from = get_selection_from_line() + to = get_selection_to_line() + + var lines := text.split("\n") + + if from + offset < 0 or to + offset >= lines.size(): return + + var target_from_index: int = from - 1 if offset == -1 else to + 1 + var target_to_index: int = to if offset == -1 else from + var line_to_move: String = lines[target_from_index] + lines.remove_at(target_from_index) + lines.insert(target_to_index, line_to_move) + + text = "\n".join(lines) + + cursor.y += offset + from += offset + to += offset + if reselect: + select(from, 0, to, get_line_width(to)) + set_caret_line(cursor.y) + set_caret_column(cursor.x) + text_changed.emit() + + +func duplicate_line() -> void: + var cursor: Vector2 = Vector2(get_caret_column(), get_caret_line()) + var from: int = cursor.y + var to: int = cursor.y+1 + if has_selection(): + from = get_selection_from_line() + to = get_selection_to_line()+1 + + var lines := text.split("\n") + var lines_to_dupl: PackedStringArray = lines.slice(from, to) + + text = "\n".join(lines.slice(0, from)+lines_to_dupl+lines.slice(from)) + + set_caret_line(cursor.y+to-from) + set_caret_column(cursor.x) + text_changed.emit() + + +# Allows dragging files into the editor +func _can_drop_data(at_position:Vector2, data:Variant) -> bool: + if typeof(data) == TYPE_DICTIONARY and 'files' in data.keys() and len(data.files) == 1: + return true + return false + +# Allows dragging files into the editor +func _drop_data(at_position:Vector2, data:Variant) -> void: + if typeof(data) == TYPE_DICTIONARY and 'files' in data.keys() and len(data.files) == 1: + set_caret_column(get_line_column_at_pos(at_position).x) + set_caret_line(get_line_column_at_pos(at_position).y) + insert_text_at_caret('"'+data.files[0]+'"') + + + +func _on_update_timer_timeout(): + update_content_list() + + +func update_content_list(): + var labels :PackedStringArray = [] + for i in label_regex.search_all(text): + labels.append(i.get_string('name')) + timeline_editor.editors_manager.sidebar.update_content_list(labels) + + +func _on_content_item_clicked(label:String) -> void: + if label == "~ Top": + set_caret_line(0) + set_caret_column(0) + adjust_viewport_to_caret() + return + + for i in label_regex.search_all(text): + if i.get_string('name') == label: + set_caret_column(0) + set_caret_line(text.count('\n', 0, i.get_start()+1)) + center_viewport_to_caret() + return + + +################################################################################ +## AUTO COMPLETION +################################################################################ + +# Called if something was typed +func _request_code_completion(force:bool): + $CodeCompletionHelper.request_code_completion(force, self) + + +# Filters the list of all possible options, depending on what was typed +# Purpose of the different Kinds is explained in [_request_code_completion] +func _filter_code_completion_candidates(candidates:Array) -> Array: + return $CodeCompletionHelper.filter_code_completion_candidates(candidates, self) + + +# Called when code completion was activated +# Inserts the selected item +func _confirm_code_completion(replace:bool) -> void: + $CodeCompletionHelper.confirm_code_completion(replace, self) + + +################################################################################ +## SYMBOL CLICKING +################################################################################ + +# Performs an action (like opening a link) when a valid symbol was clicked +func _on_symbol_lookup(symbol, line, column): + $CodeCompletionHelper.symbol_lookup(symbol, line, column) + + +# Called to test if a symbol can be clicked +func _on_symbol_validate(symbol:String) -> void: + $CodeCompletionHelper.symbol_validate(symbol, self) diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.tscn b/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.tscn new file mode 100644 index 0000000000000000000000000000000000000000..417b675191e3bcb3ce8f43b5149a48e66719270d --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=5 format=3 uid="uid://defdeav8rli6o"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd" id="1_1kbx2"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd" id="1_5qfro"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd" id="3_3bgmj"] + +[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_yb1h4"] +script = ExtResource("1_5qfro") + +[node name="TimelineTextEditor" type="CodeEdit"] +offset_top = 592.0 +offset_right = 1024.0 +offset_bottom = 600.0 +theme_override_constants/line_spacing = 10 +highlight_current_line = true +draw_tabs = true +syntax_highlighter = SubResource("SyntaxHighlighter_yb1h4") +minimap_draw = true +caret_blink = true +line_folding = true +gutters_draw_line_numbers = true +gutters_draw_fold_gutter = true +code_completion_enabled = true +code_completion_prefixes = Array[String](["[", "{"]) +indent_automatic = true +auto_brace_completion_enabled = true +auto_brace_completion_highlight_matching = true +script = ExtResource("1_1kbx2") + +[node name="UpdateTimer" type="Timer" parent="."] +one_shot = true + +[node name="CodeCompletionHelper" type="Node" parent="."] +script = ExtResource("3_3bgmj") + +[connection signal="code_completion_requested" from="." to="." method="_on_code_completion_requested"] +[connection signal="symbol_lookup" from="." to="." method="_on_symbol_lookup"] +[connection signal="symbol_validate" from="." to="." method="_on_symbol_validate"] +[connection signal="text_changed" from="." to="." method="_on_text_editor_text_changed"] +[connection signal="timeout" from="UpdateTimer" to="." method="_on_update_timer_timeout"] diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd new file mode 100644 index 0000000000000000000000000000000000000000..db48ec1d0b92447e091ae0dc7ae108db834f5ba3 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd @@ -0,0 +1,64 @@ +@tool +extends Button + +@export var visible_name:String = "" +@export var event_id:String = '' +@export var event_icon:Texture : + get: + return event_icon + set(texture): + event_icon = texture + icon = event_icon +@export var event_sorting_index:int = 0 +@export var resource:DialogicEvent +@export var dialogic_color_name:String = '' + + +func _ready() -> void: + tooltip_text = visible_name + + custom_minimum_size = Vector2(get_theme_font("font", 'Label').get_string_size(text).x+35,30)* DialogicUtil.get_editor_scale() + + add_theme_color_override("font_color", get_theme_color("font_color", "Editor")) + add_theme_color_override("font_color_hover", get_theme_color("accent_color", "Editor")) + apply_base_button_style() + + +func apply_base_button_style() -> void: + var scale := DialogicUtil.get_editor_scale() + var nstyle :StyleBoxFlat= get_parent().get_theme_stylebox('normal', 'Button').duplicate() + nstyle.border_width_left = 5 *scale + add_theme_stylebox_override('normal', nstyle) + var hstyle :StyleBoxFlat= get_parent().get_theme_stylebox('hover', 'Button').duplicate() + hstyle.border_width_left = 5 *scale + add_theme_stylebox_override('hover', hstyle) + set_color(resource.event_color) + + +func set_color(color:Color) -> void: + var style := get_theme_stylebox('normal', 'Button') + style.border_color = color + add_theme_stylebox_override('normal', style) + style = get_theme_stylebox('hover', 'Button') + style.border_color = color + add_theme_stylebox_override('hover', style) + + +func toggle_name(on:= false) -> void: + if !on: + text = "" + custom_minimum_size = Vector2(40, 40)*DialogicUtil.get_editor_scale() + var style := get_theme_stylebox('normal', 'Button') + style.bg_color = style.border_color.darkened(0.2) + add_theme_stylebox_override('normal', style) + style = get_theme_stylebox('hover', 'Button') + style.bg_color = style.border_color + add_theme_stylebox_override('hover', style) + else: + text = visible_name + custom_minimum_size = Vector2(get_theme_font("font", 'Label').get_string_size(text).x+35,30)* DialogicUtil.get_editor_scale() + apply_base_button_style() + + +func _on_button_down(): + find_parent('VisualEditor').get_node('%TimelineArea').start_dragging(1, resource) diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.tscn b/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.tscn new file mode 100644 index 0000000000000000000000000000000000000000..2aad5ef75d6ee1ae65f2e5935ab84c5c4da7e17b --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=4 format=3 uid="uid://depcrpeh3f4rv"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd" id="1_s43sc"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qx31r"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.1, 0.1, 0.1, 0.6) +border_width_left = 3 +border_color = Color(0.231373, 0.545098, 0.94902, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_n1o16"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.225, 0.225, 0.225, 0.6) +border_width_left = 3 +border_color = Color(0.231373, 0.545098, 0.94902, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 + +[node name="AddEventButton" type="Button"] +custom_minimum_size = Vector2(44, 30) +offset_right = 97.0 +offset_bottom = 42.0 +tooltip_text = "S" +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_styles/normal = SubResource("StyleBoxFlat_qx31r") +theme_override_styles/hover = SubResource("StyleBoxFlat_n1o16") +alignment = 0 +expand_icon = true +script = ExtResource("1_s43sc") +visible_name = "S" + +[connection signal="button_down" from="." to="." method="_on_button_down"] diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd new file mode 100644 index 0000000000000000000000000000000000000000..7479891a02b6a813e73f18dc1ff9250dffa20e4d --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd @@ -0,0 +1,145 @@ +@tool +extends ScrollContainer + +# Script of the TimelineArea (that contains the event blocks). +# Manages the drawing of the event lines and event dragging. + + +enum DragTypes {NOTHING, NEW_EVENT, EXISTING_EVENTS} + +var drag_type : DragTypes = DragTypes.NOTHING +var drag_data : Variant +var drag_to_position := 0 +var dragging := false + + +signal drag_completed(type, index, data) +signal drag_canceled() + + +func _ready() -> void: + resized.connect(add_extra_scroll_area_to_timeline) + %Timeline.child_entered_tree.connect(add_extra_scroll_area_to_timeline) + + # This prevents the view to turn black if you are editing this scene in Godot + if find_parent('EditorView'): + %TimelineArea.get_theme_color("background_color", "CodeEdit") + + +################### EVENT DRAGGING ############################################# +################################################################################ + +func start_dragging(type:DragTypes, data:Variant) -> void: + dragging = true + drag_type = type + drag_data = data + + +func _input(event:InputEvent) -> void: + if !dragging: + return + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: + if !event.is_pressed(): + finish_dragging() + + +func _process(delta:float) -> void: + if !dragging: + return + + for child in %Timeline.get_children(): + if (child.global_position.y < get_global_mouse_position().y) and \ + (child.global_position.y+child.size.y > get_global_mouse_position().y): + + if get_global_mouse_position().y > child.global_position.y+(child.size.y/2.0): + drag_to_position = child.get_index()+1 + queue_redraw() + else: + drag_to_position = child.get_index() + queue_redraw() + + +func finish_dragging(): + dragging = false + if get_global_rect().has_point(get_global_mouse_position()): + drag_completed.emit(drag_type, drag_to_position, drag_data) + else: + drag_canceled.emit() + queue_redraw() + + + +##################### LINE DRAWING ############################################# +################################################################################ + +func _draw() -> void: + var _scale := DialogicUtil.get_editor_scale() + var line_width := 5 * _scale + var horizontal_line_length := 100*_scale + var color_multiplier := Color(1,1,1,0.5) + var selected_color_multiplier := Color(1,1,1,1) + for idx in range($Timeline.get_child_count()): + var event : Control = $Timeline.get_child(idx) + + if not "resource" in event: + continue + + if not event.visible: + continue + + if event.resource is DialogicEndBranchEvent: + continue + + if not (event.has_any_enabled_body_content or event.resource.can_contain_events): + continue + + var icon_panel_height := 32*_scale + var rect_position :Vector2= event.get_node('%IconPanel').global_position+Vector2(0,1)*event.get_node('%IconPanel').size+Vector2(0,-4) + var color :Color= event.resource.event_color + if event.is_selected(): + color *= selected_color_multiplier + else: + color *= color_multiplier + + if idx < $Timeline.get_child_count()-1 and event.current_indent_level < $Timeline.get_child(idx+1).current_indent_level: + var end_node :Node= event.end_node + var sub_idx := idx + + if !end_node: # this doesn't have an end node (e.g. text event with choices in it) + while sub_idx < $Timeline.get_child_count()-1: + sub_idx += 1 + if $Timeline.get_child(sub_idx).current_indent_level == event.current_indent_level: + end_node = $Timeline.get_child(sub_idx-1) + break + var rect_size := Vector2() + if end_node != null: + rect_size = Vector2(line_width, end_node.global_position.y+end_node.size.y-rect_position.y) + if end_node.resource is DialogicEndBranchEvent and event.resource.can_contain_events: + rect_size = Vector2(line_width, end_node.global_position.y+end_node.size.y/2-rect_position.y) + else: + rect_size = Vector2(line_width, $Timeline.get_child(-1).global_position.y+$Timeline.get_child(-4).size.y-rect_position.y) + + draw_rect(Rect2(rect_position-global_position, rect_size), color) + draw_rect(Rect2(Vector2(event.get_node('%IconPanel').global_position.x+line_width, rect_position.y+rect_size.y-line_width)-global_position, Vector2(horizontal_line_length, line_width)), color) + + elif event.expanded: + draw_rect(Rect2(rect_position-global_position, Vector2(line_width, event.size.y-event.get_node('%IconPanel').size.y+10*_scale)), color.darkened(0.5)) + + if dragging and get_global_rect().has_point(get_global_mouse_position()): + var height :int = 0 + if drag_to_position == %Timeline.get_child_count(): + height = %Timeline.get_child(-1).global_position.y+%Timeline.get_child(-1).size.y-global_position.y-(line_width/2.0) + else: + height = %Timeline.get_child(drag_to_position).global_position.y-global_position.y-(line_width/2.0) + + draw_line(Vector2(0, height), Vector2(size.x*0.9, height), get_theme_color("accent_color", "Editor"), line_width*0.2) + +##################### SPACE BELOW ############################################## +################################################################################ + +func add_extra_scroll_area_to_timeline(fake_arg:Variant=null) -> void: + if %Timeline.get_children().size() > 4: + %Timeline.custom_minimum_size.y = 0 + %Timeline.size.y = 0 + if %Timeline.size.y + 200 > %TimelineArea.size.y: + %Timeline.custom_minimum_size = Vector2(0, %Timeline.size.y + 200) diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd new file mode 100644 index 0000000000000000000000000000000000000000..cbf968054f7e7eecc9b72f0ee97a5744e6a600c7 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd @@ -0,0 +1,1087 @@ +@tool +extends Container + +## Visual mode of the timeline editor. + + +################################################################################ +## EDITOR NODES +################################################################################ +var TimelineUndoRedo := UndoRedo.new() +@onready var timeline_editor := get_parent().get_parent() +var event_node +var sidebar_collapsed := false + +################################################################################ +## SIGNALS +################################################################################ +signal selection_updated +signal batch_loaded +signal timeline_loaded + + +################################################################################ +## TIMELINE LOADING +################################################################################ +var _batches := [] +var _building_timeline := false +var _timeline_changed_while_loading := false + + +################################################################################ +## TIMELINE EVENT MANAGEMENT +################################################################################ +var selected_items : Array = [] + + +################################################################################ +## CREATE/SAVE/LOAD +################################################################################ + +func something_changed(): + timeline_editor.current_resource_state = DialogicEditor.ResourceStates.UNSAVED + + +func save_timeline() -> void: + if !is_inside_tree(): + return + + # return if resource is unchanged + if timeline_editor.current_resource_state != DialogicEditor.ResourceStates.UNSAVED: + return + + # create a list of text versions of all the events with the right indent + var new_events := [] + var indent := 0 + for event in %Timeline.get_children(): + if 'event_name' in event.resource: + event.resource.update_text_version() + new_events.append(event.resource) + + if !timeline_editor.current_resource: + return + + timeline_editor.current_resource.events = new_events + timeline_editor.current_resource.events_processed = true + var error :int = ResourceSaver.save(timeline_editor.current_resource, timeline_editor.current_resource.resource_path) + if error != OK: + print('[Dialogic] Saving error: ', error) + + timeline_editor.current_resource.set_meta("unsaved", false) + timeline_editor.current_resource_state = DialogicEditor.ResourceStates.SAVED + timeline_editor.editors_manager.resource_helper.rebuild_timeline_directory() + + +func _notification(what): + if what == NOTIFICATION_WM_CLOSE_REQUEST: + save_timeline() + + +func load_timeline(resource:DialogicTimeline) -> void: + if _building_timeline: + _timeline_changed_while_loading = true + await batch_loaded + _timeline_changed_while_loading = false + _building_timeline = false + + clear_timeline_nodes() + + if timeline_editor.current_resource.events.size() == 0: + pass + else: + await timeline_editor.current_resource.process() + + if timeline_editor.current_resource.events.size() == 0: + return + + var data := resource.events + var page := 1 + var batch_size := 10 + _batches = [] + _building_timeline = true + while batch_events(data, batch_size, page).size() != 0: + _batches.append(batch_events(data, batch_size, page)) + page += 1 + batch_loaded.emit() + # Reset the scroll position + %TimelineArea.scroll_vertical = 0 + + +func batch_events(array, size, batch_number): + return array.slice((batch_number - 1) * size, batch_number * size) + + +# a list of all events like choice and condition events (so they get connected to their end events) +var opener_events_stack := [] + +func load_batch(data:Array) -> void: + var current_batch :Array = _batches.pop_front() + if current_batch: + for i in current_batch: + if i is DialogicEndBranchEvent: + create_end_branch_event(%Timeline.get_child_count(), opener_events_stack.pop_back()) + else: + var piece := add_event_node(i, %Timeline.get_child_count()) + if i.can_contain_events: + opener_events_stack.push_back(piece) + batch_loaded.emit() + +func _on_batch_loaded(): + if _timeline_changed_while_loading: + return + if _batches.size() > 0: + indent_events() + await get_tree().process_frame + load_batch(_batches) + return + + if opener_events_stack: + for ev in opener_events_stack: + create_end_branch_event(%Timeline.get_child_count(), ev) + opener_events_stack = [] + indent_events() + update_content_list() + _building_timeline = false + + +func clear_timeline_nodes(): + deselect_all_items() + for event in %Timeline.get_children(): + event.free() + + +##################### SETUP #################################################### +################################################################################ + +func _ready(): + DialogicUtil.get_dialogic_plugin().dialogic_save.connect(save_timeline) + event_node = load("res://addons/dialogic/Editor/Events/EventBlock/event_block.tscn") + + batch_loaded.connect(_on_batch_loaded) + + await find_parent('EditorView').ready + timeline_editor.editors_manager.sidebar.content_item_activated.connect(_on_content_item_clicked) + + +func _on_content_item_clicked(label:String) -> void: + if label == "~ Top": + %TimelineArea.scroll_vertical = 0 + return + + for event in %Timeline.get_children(): + if 'event_name' in event.resource and event.resource is DialogicLabelEvent: + if event.resource.name == label: + scroll_to_piece(event.get_index()) + return + + +func update_content_list(): + var labels :PackedStringArray = [] + for event in %Timeline.get_children(): + if 'event_name' in event.resource and event.resource is DialogicLabelEvent: + labels.append(event.resource.name) + timeline_editor.editors_manager.sidebar.update_content_list(labels) + + +func load_event_buttons() -> void: + # Clear previous event buttons + for child in %RightSidebar.get_child(0).get_children(): + if child is FlowContainer: + for button in child.get_children(): + button.queue_free() + + var scripts: Array = timeline_editor.editors_manager.resource_helper.get_event_scripts() + + # Event buttons + var buttonScene := load("res://addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.tscn") + + var hidden_buttons :Array = DialogicUtil.get_editor_setting('hidden_event_buttons', []) + var sections := {} + + for child in %RightSidebar.get_child(0).get_children(): + child.queue_free() + + for event_script in scripts: + var event_resource: Variant + + if typeof(event_script) == TYPE_STRING: + event_resource = load(event_script).new() + else: + event_resource = event_script + + if event_resource.disable_editor_button == true: + continue + + if event_resource.event_name in hidden_buttons: + continue + + var button :Button = buttonScene.instantiate() + button.resource = event_resource + button.visible_name = event_resource.event_name + button.event_icon = event_resource._get_icon() + button.set_color(event_resource.event_color) + button.dialogic_color_name = event_resource.dialogic_color_name + button.event_sorting_index = event_resource.event_sorting_index + + button.button_up.connect(_add_event_button_pressed.bind(event_resource)) + + if !event_resource.event_category in sections: + var section := VBoxContainer.new() + section.name = event_resource.event_category + + var section_header := HBoxContainer.new() + section_header.add_child(Label.new()) + section_header.get_child(0).text = event_resource.event_category + section_header.get_child(0).size_flags_horizontal = SIZE_SHRINK_BEGIN + section_header.get_child(0).theme_type_variation = "DialogicSection" + section_header.add_child(HSeparator.new()) + section_header.get_child(1).size_flags_horizontal = SIZE_EXPAND_FILL + section.add_child(section_header) + + var button_container := FlowContainer.new() + section.add_child(button_container) + + sections[event_resource.event_category] = button_container + %RightSidebar.get_child(0).add_child(section) + + + sections[event_resource.event_category].add_child(button) + + # Sort event button + while event_resource.event_sorting_index < sections[event_resource.event_category].get_child(max(0, button.get_index()-1)).resource.event_sorting_index: + sections[event_resource.event_category].move_child(button, button.get_index()-1) + + var sections_order :Array= DialogicUtil.get_editor_setting('event_section_order', + ['Main', 'Flow', 'Logic', 'Audio', 'Godot','Other', 'Helper']) + + # Sort event sections + for section in sections_order: + if %RightSidebar.get_child(0).has_node(section): + %RightSidebar.get_child(0).move_child(%RightSidebar.get_child(0).get_node(section), sections_order.find(section)) + + # Resize RightSidebar + var _scale := DialogicUtil.get_editor_scale() + %RightSidebar.custom_minimum_size.x = 50 * _scale + + $View.split_offset = -200*_scale + _on_right_sidebar_resized() + + +#################### CLEANUP ################################################### +################################################################################ + +func _exit_tree() -> void: + # Explicitly free any open cache resources on close, so we don't get leaked resource errors on shutdown + clear_timeline_nodes() + + +################# DRAG&DROP + DRAGGING EVENTS ################################### +################################################################################# + +# SIGNAL handles input on the events mainly for selection and moving events +func _on_event_block_gui_input(event, item: Node): + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: + if event.is_pressed(): + if not _is_item_selected(item) and not len(selected_items) > 1: + select_item(item) + + else: + if len(selected_items) > 1: + select_item(item) + + if len(selected_items) > 0 and event is InputEventMouseMotion: + if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): + if !%TimelineArea.dragging: + %TimelineArea.start_dragging(%TimelineArea.DragTypes.EXISTING_EVENTS, get_events_indexed(selected_items, EndBranchMode.ONLY_SINGLE)) + + +func _on_timeline_area_drag_completed(type:int, index:int, data:Variant) -> void: + if type == %TimelineArea.DragTypes.NEW_EVENT: + var resource :DialogicEvent = data.duplicate() + resource._load_custom_defaults() + + TimelineUndoRedo.create_action("[D] Add "+resource.event_name+" event.") + if resource.can_contain_events: + TimelineUndoRedo.add_do_method(add_event_with_end_branch.bind(resource, index, true, true)) + TimelineUndoRedo.add_undo_method(remove_events_at_index.bind(index, 2)) + else: + TimelineUndoRedo.add_do_method(add_event_node.bind(resource, index, true, true)) + TimelineUndoRedo.add_undo_method(remove_events_at_index.bind(index, 1)) + TimelineUndoRedo.commit_action() + + elif type == %TimelineArea.DragTypes.EXISTING_EVENTS: + # if the index is after some selected events, correct it + var c := 0 + for i in data.keys(): + if i void: + # we protect this with is_visible_in_tree to not + # invoke a shortcut by accident + if !((event is InputEventKey or !event is InputEventWithModifiers) and is_visible_in_tree()): + return + + if "pressed" in event: + if !event.pressed: + return + + ## Some shortcuts should always work + match event.as_text(): + "Ctrl+T": # Add text event + _add_event_button_pressed(DialogicTextEvent.new(), true) + get_viewport().set_input_as_handled() + + "Ctrl+Shift+T", "Ctrl+Alt+T", "Ctrl+Option+T": # Add text event with current or previous character + get_viewport().set_input_as_handled() + var ev := DialogicTextEvent.new() + ev.character = get_previous_character(event.as_text() == "Ctrl+Alt+T" or event.as_text() == "Ctrl+Option+T") + _add_event_button_pressed(ev, true) + + "Ctrl+E": # Add character join event + _add_event_button_pressed(DialogicCharacterEvent.new(), true) + get_viewport().set_input_as_handled() + + "Ctrl+Shift+E": # Add character update event + var ev := DialogicCharacterEvent.new() + ev.action = DialogicCharacterEvent.Actions.UPDATE + _add_event_button_pressed(ev, true) + get_viewport().set_input_as_handled() + + "Ctrl+Alt+E", "Ctrl+Option+E": # Add character leave event + var ev := DialogicCharacterEvent.new() + ev.action = DialogicCharacterEvent.Actions.LEAVE + _add_event_button_pressed(ev, true) + get_viewport().set_input_as_handled() + + "Ctrl+J": # Add jump event + _add_event_button_pressed(DialogicJumpEvent.new(), true) + get_viewport().set_input_as_handled() + "Ctrl+L": # Add label event + _add_event_button_pressed(DialogicLabelEvent.new(), true) + get_viewport().set_input_as_handled() + + ## Some shortcuts should be disabled when writing text. + if get_viewport().gui_get_focus_owner() is TextEdit || get_viewport().gui_get_focus_owner() is LineEdit: + return + + match event.as_text(): + "Ctrl+Z": # UNDO + TimelineUndoRedo.undo() + indent_events() + get_viewport().set_input_as_handled() + + "Ctrl+Shift+Z", "Ctrl+Y": # REDO + TimelineUndoRedo.redo() + indent_events() + get_viewport().set_input_as_handled() + + "Up": #select previous + if (len(selected_items) == 1): + var prev := maxi(0, selected_items[0].get_index() - 1) + var prev_node := %Timeline.get_child(prev) + if (prev_node != selected_items[0]): + selected_items = [] + select_item(prev_node) + get_viewport().set_input_as_handled() + + "Down": #select next + if (len(selected_items) == 1): + var next := mini(%Timeline.get_child_count() - 1, selected_items[0].get_index() + 1) + var next_node := %Timeline.get_child(next) + if (next_node != selected_items[0]): + selected_items = [] + select_item(next_node) + get_viewport().set_input_as_handled() + + "Delete": + if (len(selected_items) != 0): + var events_indexed := get_events_indexed(selected_items) + TimelineUndoRedo.create_action("[D] Deleting "+str(len(selected_items))+" event(s).") + TimelineUndoRedo.add_do_method(delete_events_indexed.bind(events_indexed)) + TimelineUndoRedo.add_undo_method(add_events_indexed.bind(events_indexed)) + TimelineUndoRedo.commit_action() + get_viewport().set_input_as_handled() + + "Ctrl+A": # select all + if (len(selected_items) != 0): + select_all_items() + get_viewport().set_input_as_handled() + + "Ctrl+Shift+A": # deselect all + if (len(selected_items) != 0): + deselect_all_items() + get_viewport().set_input_as_handled() + + "Ctrl+C": + copy_selected_events() + get_viewport().set_input_as_handled() + + "Ctrl+V": + var events_list := paste_check() + var paste_position := -1 + if selected_items: + paste_position = selected_items[-1].get_index()+1 + else: + paste_position = %Timeline.get_child_count()-1 + if events_list: + TimelineUndoRedo.create_action("[D] Pasting "+str(len(events_list))+" event(s).") + TimelineUndoRedo.add_do_method(add_events_at_index.bind(events_list, paste_position)) + TimelineUndoRedo.add_undo_method(remove_events_at_index.bind(paste_position+1, len(events_list))) + TimelineUndoRedo.commit_action() + get_viewport().set_input_as_handled() + + "Ctrl+X": + var events_indexed := get_events_indexed(selected_items) + TimelineUndoRedo.create_action("[D] Cut "+str(len(selected_items))+" event(s).") + TimelineUndoRedo.add_do_method(cut_events_indexed.bind(events_indexed)) + TimelineUndoRedo.add_undo_method(add_events_indexed.bind(events_indexed)) + TimelineUndoRedo.commit_action() + get_viewport().set_input_as_handled() + + "Ctrl+D": + if len(selected_items) > 0: + var events := get_events_indexed(selected_items).values() + var at_index :int= selected_items[-1].get_index() + TimelineUndoRedo.create_action("[D] Duplicate "+str(len(events))+" event(s).") + TimelineUndoRedo.add_do_method(add_events_at_index.bind(events, at_index)) + TimelineUndoRedo.add_undo_method(remove_events_at_index.bind(at_index, len(events))) + TimelineUndoRedo.commit_action() + get_viewport().set_input_as_handled() + + "Alt+Up", "Option+Up": + if len(selected_items) > 0: + TimelineUndoRedo.create_action("[D] Move event(s) up.") + TimelineUndoRedo.add_do_method(move_blocks_by_index.bind(selected_items.map(func(x):return x.get_index()), -1)) + TimelineUndoRedo.add_do_method(indent_events) + TimelineUndoRedo.add_do_method(something_changed) + TimelineUndoRedo.add_undo_method(move_blocks_by_index.bind(selected_items.map(func(x):return x.get_index()-1), 1)) + TimelineUndoRedo.add_undo_method(indent_events) + TimelineUndoRedo.add_undo_method(something_changed) + TimelineUndoRedo.commit_action() + + get_viewport().set_input_as_handled() + + "Alt+Down", "Option+Down": + if len(selected_items) > 0: + TimelineUndoRedo.create_action("[D] Move event(s) down.") + TimelineUndoRedo.add_do_method(move_blocks_by_index.bind(selected_items.map(func(x):return x.get_index()), 1)) + TimelineUndoRedo.add_do_method(indent_events) + TimelineUndoRedo.add_do_method(something_changed) + TimelineUndoRedo.add_undo_method(move_blocks_by_index.bind(selected_items.map(func(x):return x.get_index()+1), -1)) + TimelineUndoRedo.add_undo_method(indent_events) + TimelineUndoRedo.add_undo_method(something_changed) + TimelineUndoRedo.commit_action() + + get_viewport().set_input_as_handled() + + +func get_previous_character(double_previous := false) -> DialogicCharacter: + var character :DialogicCharacter = null + var idx :int = %Timeline.get_child_count() + if idx == 0: + return null + if len(selected_items): + idx = selected_items[0].get_index() + var one_skipped := false + idx += 1 + for i in range(selected_items[0].get_index()+1): + idx -= 1 + if !('resource' in %Timeline.get_child(idx) and 'character' in %Timeline.get_child(idx).resource): + continue + if %Timeline.get_child(idx).resource.character == null: + continue + if double_previous: + if %Timeline.get_child(idx).resource.character == character: + continue + if character != null: + if one_skipped: + one_skipped = false + else: + character = %Timeline.get_child(idx).resource.character + break + character = %Timeline.get_child(idx).resource.character + else: + character = %Timeline.get_child(idx).resource.character + break + return character + + +#################### DELETING, COPY, PASTE ##################################### +################################################################################ + +enum EndBranchMode {FORCE_NO_SINGLE, ONLY_SINGLE} +# FORCE_NO_SINGLE = End branches are effected if their parent is selected, not alone +# -> for delete, copy, cut, paste (to avoid lonly end branches) +# ONLY_SINGLE = Single End branches are allowed alone and are not effected if only the parent is selected +# -> for moving events + +func get_events_indexed(events:Array, end_branch_mode:=EndBranchMode.FORCE_NO_SINGLE) -> Dictionary: + var indexed_dict := {} + for event in events: + # do not collect selected end branches (e.g. on delete, copy, etc.) + if event.resource is DialogicEndBranchEvent and end_branch_mode == EndBranchMode.FORCE_NO_SINGLE: + continue + + indexed_dict[event.get_index()] = event.resource.to_text() + + # store an end branch if it is selected or connected to a selected event + if end_branch_mode == EndBranchMode.FORCE_NO_SINGLE: + if 'end_node' in event and event.end_node: + event = event.end_node + indexed_dict[event.get_index()] = event.resource.to_text() + + if event.resource is DialogicEndBranchEvent: + if event.parent_node in events: # add local index + indexed_dict[event.get_index()] += str(events.find(event.parent_node)) + else: # add global index + indexed_dict[event.get_index()] += '#'+str(event.parent_node.get_index()) + return indexed_dict + + +func select_indexed_events(indexed_events:Dictionary) -> void: + selected_items = [] + for event_index in indexed_events.keys(): + selected_items.append(%Timeline.get_child(event_index)) + + +func add_events_indexed(indexed_events:Dictionary) -> void: + var indexes := indexed_events.keys() + indexes.sort() + + var events := [] + for event_idx in indexes: + deselect_all_items() + + var event_resource :Variant + if timeline_editor.editors_manager.resource_helper: + for i in timeline_editor.editors_manager.resource_helper.get_event_scripts(): + if i._test_event_string(indexed_events[event_idx]): + event_resource = i.duplicate() + break + else: + printerr("[Dialogic] Unable to access resource_helper!") + continue + event_resource.set_meta('editor_character_directory', timeline_editor.editors_manager.resource_helper.character_directory) + event_resource.from_text(indexed_events[event_idx]) + if event_resource is DialogicEndBranchEvent: + var idx :String= indexed_events[event_idx].trim_prefix('<>') + if idx.begins_with('#'): # a global index + events.append(create_end_branch_event(%Timeline.get_child_count(), %Timeline.get_child(int(idx.trim_prefix('#'))))) + else: # a local index (index in the added events list) + events.append(create_end_branch_event(%Timeline.get_child_count(), events[int(idx)])) + %Timeline.move_child(events[-1], event_idx) + else: + events.append(add_event_node(event_resource)) + %Timeline.move_child(events[-1], event_idx) + + selected_items = events + visual_update_selection() + indent_events() + + +func delete_events_indexed(indexed_events:Dictionary) -> void: + select_indexed_events(indexed_events) + delete_selected_events() + indent_events() + + +func delete_selected_events() -> void: + if len(selected_items) == 0: + return + + # get next element + var next := mini(%Timeline.get_child_count() - 1, selected_items[-1].get_index() + 1) + var next_node := %Timeline.get_child(next) + if _is_item_selected(next_node): + next_node = null + + for event in selected_items: + if 'end_node' in event and event.end_node != null and is_instance_valid(event.end_node): + if !is_instance_valid(event.end_node.get_parent()): return + event.end_node.get_parent().remove_child(event.end_node) + event.end_node.queue_free() + if is_instance_valid(event): + if !is_instance_valid(event.get_parent()): return + event.get_parent().remove_child(event) + event.queue_free() + + # select next + if (next_node != null): + select_item(next_node, false) + else: + if (%Timeline.get_child_count() > 0): + next_node = %Timeline.get_child(max(0, %Timeline.get_child_count() - 1)) + if (next_node != null): + select_item(next_node, false) + else: + deselect_all_items() + something_changed() + indent_events() + + +func cut_selected_events() -> void: + copy_selected_events() + delete_selected_events() + indent_events() + + +func cut_events_indexed(indexed_events:Dictionary) -> void: + select_indexed_events(indexed_events) + cut_selected_events() + indent_events() + + +func copy_selected_events() -> void: + if len(selected_items) == 0: + return + var event_copy_array := [] + for item in selected_items: + event_copy_array.append(item.resource.to_text()) + if item.resource is DialogicEndBranchEvent: + if item.parent_node in selected_items: # add local index + event_copy_array[-1] += str(selected_items.find(item.parent_node)) + else: # add global index + event_copy_array[-1] += '#'+str(item.parent_node.get_index()) + var _json := JSON.new() + DisplayServer.clipboard_set(_json.stringify( + { + "events":event_copy_array, + "project_name": ProjectSettings.get_setting("application/config/name") + })) + + +func paste_check() -> Array: + var _json := JSON.new() + var clipboard_parse :Variant= _json.parse(DisplayServer.clipboard_get()) + if clipboard_parse == OK: + clipboard_parse = _json.get_data() + if clipboard_parse.has("project_name"): + if clipboard_parse.project_name != ProjectSettings.get_setting("application/config/name"): + print("[D] Be careful when copying from another project!") + if clipboard_parse.has('events'): + return clipboard_parse.events + return [] + + +func remove_events_at_index(at_index:int, amount:int = 1)-> void: + selected_items = [] + something_changed() + for i in range(0, amount): + selected_items.append(%Timeline.get_child(at_index + i)) + delete_selected_events() + indent_events() + + +func add_events_at_index(event_list:Array, at_index:int) -> void: + var new_items := [] + for c in range(len(event_list)): + var item :String = event_list[c] + var resource: Variant + if timeline_editor.editors_manager.resource_helper: + for i in timeline_editor.editors_manager.resource_helper.get_event_scripts(): + if i._test_event_string(item): + resource = i.duplicate() + break + resource.set_meta('editor_character_directory', timeline_editor.editors_manager.resource_helper.character_directory) + resource.from_text(item) + else: + printerr("[Dialogic] Unable to access resource_helper!") + continue + if resource is DialogicEndBranchEvent: + var idx :String= item.trim_prefix('<>') + if idx.begins_with('#'): # a global index + new_items.append(create_end_branch_event(at_index+c, %Timeline.get_child(int(idx.trim_prefix('#'))))) + else: # a local index (index in the added events list) + new_items.append(create_end_branch_event(at_index+c, new_items[int(idx)])) + else: + new_items.append(add_event_node(resource, at_index+c)) + selected_items = new_items + something_changed() + sort_selection() + visual_update_selection() + indent_events() + + +#################### BLOCK SELECTION ########################################### +################################################################################ + +func _is_item_selected(item: Node) -> bool: + return item in selected_items + + +func select_item(item: Node, multi_possible:bool = true) -> void: + if item == null: + return + + if Input.is_key_pressed(KEY_CTRL) and multi_possible: + # deselect the item if it is selected + if _is_item_selected(item): + selected_items.erase(item) + else: + selected_items.append(item) + elif Input.is_key_pressed(KEY_SHIFT) and multi_possible: + + if len(selected_items) == 0: + selected_items = [item] + else: + var index :int= selected_items[-1].get_index() + var goal_idx := item.get_index() + while true: + if index < goal_idx: index += 1 + else: index -= 1 + if not %Timeline.get_child(index) in selected_items: + selected_items.append(%Timeline.get_child(index)) + + if index == goal_idx: + break + else: + if len(selected_items) == 1: + if _is_item_selected(item): + selected_items.erase(item) + else: + selected_items = [item] + else: + selected_items = [item] + + sort_selection() + visual_update_selection() + + +# checks all the events and sets their styles (selected/deselected) +func visual_update_selection() -> void: + for item in %Timeline.get_children(): + item.visual_deselect() + if 'end_node' in item and item.end_node != null: + item.end_node.unhighlight() + for item in selected_items: + item.visual_select() + if 'end_node' in item and item.end_node != null: + item.end_node.highlight() + + +## Sorts the selection using 'custom_sort_selection' +func sort_selection() -> void: + selected_items.sort_custom(custom_sort_selection) + + +## Compares two event blocks based on their position in the timeline +func custom_sort_selection(item1, item2) -> bool: + return item1.get_index() < item2.get_index() + + +func select_all_items() -> void: + selected_items = [] + for event in %Timeline.get_children(): + selected_items.append(event) + visual_update_selection() + + +func deselect_all_items() -> void: + selected_items = [] + visual_update_selection() + +############ CREATING NEW EVENTS USING THE BUTTONS ############################# +################################################################################ + +# Event Creation signal for buttons +# If force_resource is true, the event will be added with the actual resource +func _add_event_button_pressed(event_resource:DialogicEvent, force_resource := false): + if %TimelineArea.get_global_rect().has_point(get_global_mouse_position()) and !force_resource: + return + + var at_index := -1 + if selected_items: + at_index = selected_items[-1].get_index()+1 + else: + at_index = %Timeline.get_child_count() + + + var resource :DialogicEvent = null + if force_resource: + resource = event_resource + else: + resource = event_resource.duplicate() + resource._load_custom_defaults() + + resource.created_by_button = true + + TimelineUndoRedo.create_action("[D] Add "+event_resource.event_name+" event.") + if event_resource.can_contain_events: + TimelineUndoRedo.add_do_method(add_event_with_end_branch.bind(resource, at_index, true, true)) + TimelineUndoRedo.add_undo_method(remove_events_at_index.bind(at_index, 2)) + else: + TimelineUndoRedo.add_do_method(add_event_node.bind(resource, at_index, true, true)) + TimelineUndoRedo.add_undo_method(remove_events_at_index.bind(at_index, 1)) + TimelineUndoRedo.commit_action() + + resource.created_by_button = false + + something_changed() + scroll_to_piece(at_index) + indent_events() + + +################# CREATING THE TIMELINE ######################################## +################################################################################ + +# Adding an event to the timeline +func add_event_node(event_resource:DialogicEvent, at_index:int = -1, auto_select: bool = false, indent: bool = false) -> Control: + if event_resource is DialogicEndBranchEvent: + return create_end_branch_event(at_index, %Timeline.get_child(0)) + + if event_resource['event_node_ready'] == false: + if event_resource['event_node_as_text'] != "": + event_resource._load_from_string(event_resource['event_node_as_text']) + + var piece :Control = event_node.instantiate() + piece.resource = event_resource + event_resource._editor_node = piece + piece.content_changed.connect(something_changed) + if event_resource.event_name == "Label": + piece.content_changed.connect(update_content_list) + piece.tree_exited.connect(update_content_list) + if at_index == -1: + if len(selected_items) != 0: + selected_items[0].add_sibling(piece) + else: + %Timeline.add_child(piece) + else: + %Timeline.add_child(piece) + %Timeline.move_child(piece, at_index) + + piece.gui_input.connect(_on_event_block_gui_input.bind(piece)) + + # Building editing part + piece.build_editor(true, event_resource.expand_by_default) + + if auto_select: + select_item(piece, false) + + # Indent on create + if indent: + indent_events() + + if not _building_timeline: + piece.focus() + + return piece + + +func create_end_branch_event(at_index:int, parent_node:Node) -> Node: + var end_branch_event :Control = load("res://addons/dialogic/Editor/Events/BranchEnd.tscn").instantiate() + end_branch_event.resource = DialogicEndBranchEvent.new() + end_branch_event.gui_input.connect(_on_event_block_gui_input.bind(end_branch_event)) + parent_node.end_node = end_branch_event + end_branch_event.parent_node = parent_node + end_branch_event.add_end_control(parent_node.resource.get_end_branch_control()) + %Timeline.add_child(end_branch_event) + %Timeline.move_child(end_branch_event, at_index) + return end_branch_event + + +# combination of the above that establishes the correct connection between the event and it's end branch +func add_event_with_end_branch(resource, at_index:int=-1, auto_select:bool = false, indent:bool = false): + var event := add_event_node(resource, at_index, auto_select, indent) + create_end_branch_event(at_index+1, event) + + +##################### BLOCK GETTERS ############################################ +################################################################################ + +func get_block_above(block:Node) -> Node: + if block.get_index() > 0: + return %Timeline.get_child(block.get_index() - 1) + return null + + +func get_block_below(block:Node) -> Node: + if block.get_index() < %Timeline.get_child_count() - 1: + return %Timeline.get_child(block.get_index() + 1) + return null + + +##################### BLOCK MOVEMENT ########################################### +################################################################################ + +func move_blocks_by_index(block_idxs:Array, offset:int): + move_blocks(block_idxs.map(func(x): return %Timeline.get_child(x)), offset) + + +func move_blocks(blocks:Array, offset:int) -> void: + if offset > 0: + blocks = blocks.duplicate() + blocks.reverse() + for block in blocks: + var to_idx := maxi(min(block.get_index()+offset, %Timeline.get_child_count()-1), 0) + if !%Timeline.get_child(to_idx) in blocks: + move_block_to_index(block.get_index(), to_idx) + + +func move_block_up(block:Node) -> void: + if block.get_index() < 1: return + %Timeline.move_child(block, block.get_index() - 1) + %TimelineArea.queue_redraw() + + +func move_block_down(block:Node) -> void: + %Timeline.move_child(block, block.get_index() + 1) + %TimelineArea.queue_redraw() + + +func move_block_to_index(block_index:int, index:int) -> void: + %Timeline.move_child(%Timeline.get_child(block_index), index) + something_changed() + indent_events() + + + +################### VISIBILITY/VISUALS ######################################### +################################################################################ + +func scroll_to_piece(piece_index:int) -> void: + await get_tree().process_frame + var height :float = %Timeline.get_child(piece_index).position.y + if height < %TimelineArea.scroll_vertical or height > %TimelineArea.scroll_vertical+%TimelineArea.size.y-(200*DialogicUtil.get_editor_scale()): + %TimelineArea.scroll_vertical = height + + +func indent_events() -> void: + var indent: int = 0 + var event_list: Array = %Timeline.get_children() + + if event_list.size() < 2: + return + + var currently_hidden := false + var hidden_count := 0 + var hidden_until :Control= null + + # will be applied to the indent after the current event + var delayed_indent: int = 0 + + for event in event_list: + if (not "resource" in event): + continue + + if (not currently_hidden) and event.resource.can_contain_events and event.end_node and event.collapsed: + currently_hidden = true + hidden_until = event.end_node + hidden_count = 0 + elif currently_hidden and event == hidden_until: + event.update_hidden_events_indicator(hidden_count) + currently_hidden = false + hidden_until = null + elif currently_hidden: + event.hide() + hidden_count += 1 + else: + event.show() + if event.resource is DialogicEndBranchEvent: + event.update_hidden_events_indicator(0) + + delayed_indent = 0 + + if event.resource.can_contain_events: + delayed_indent = 1 + + if event.resource.needs_parent_event: + var current_block_above := get_block_above(event) + while current_block_above != null and current_block_above.resource is DialogicEndBranchEvent: + if current_block_above.parent_node == event: + break + current_block_above = get_block_above(current_block_above.parent_node) + + if current_block_above != null and event.resource.is_expected_parent_event(current_block_above.resource): + indent += 1 + event.set_warning() + else: + event.set_warning('This event needs a specific parent event!') + + elif event.resource is DialogicEndBranchEvent: + event.parent_node_changed() + delayed_indent -= 1 + if event.parent_node.resource.needs_parent_event: + delayed_indent -= 1 + + if indent >= 0: + event.set_indent(indent) + else: + event.set_indent(0) + indent += delayed_indent + + %TimelineArea.queue_redraw() + + + +################ SPECIAL BLOCK OPERATIONS ###################################### +################################################################################ + +func _on_event_popup_menu_index_pressed(index:int) -> void: + var item :Control = %EventPopupMenu.current_event + if index == 0: + if not item.resource.help_page_path.is_empty(): + OS.shell_open(item.resource.help_page_path) + elif index == 2 or index == 3: + if index == 2: + TimelineUndoRedo.create_action("[D] Move event up.") + TimelineUndoRedo.add_do_method(move_blocks_by_index.bind([item].map(func(x):return x.get_index()), -1)) + TimelineUndoRedo.add_undo_method(move_blocks_by_index.bind([item].map(func(x):return x.get_index()-1), 1)) + else: + TimelineUndoRedo.create_action("[D] Move event down.") + TimelineUndoRedo.add_do_method(move_blocks_by_index.bind([item].map(func(x):return x.get_index()), 1)) + TimelineUndoRedo.add_undo_method(move_blocks_by_index.bind([item].map(func(x):return x.get_index()+1), -1)) + TimelineUndoRedo.add_do_method(indent_events) + TimelineUndoRedo.add_do_method(something_changed) + TimelineUndoRedo.add_undo_method(indent_events) + TimelineUndoRedo.add_undo_method(something_changed) + TimelineUndoRedo.commit_action() + elif index == 5: + var events_indexed := get_events_indexed([item]) + TimelineUndoRedo.create_action("[D] Deleting 1 event.") + TimelineUndoRedo.add_do_method(delete_events_indexed.bind(events_indexed)) + TimelineUndoRedo.add_undo_method(add_events_indexed.bind(events_indexed)) + TimelineUndoRedo.commit_action() + indent_events() + something_changed() + + +func _on_right_sidebar_resized(): + var _scale := DialogicUtil.get_editor_scale() + if %RightSidebar.size.x < 160*_scale and !sidebar_collapsed: + sidebar_collapsed = true + for section in %RightSidebar.get_node('EventContainer').get_children(): + for con in section.get_children(): + if con.get_child_count() == 0: + continue + if con.get_child(0) is Label: + con.get_child(0).hide() + elif con.get_child(0) is Button: + for button in con.get_children(): + button.toggle_name(false) + + elif %RightSidebar.size.x > 160*_scale and sidebar_collapsed: + sidebar_collapsed = false + for section in %RightSidebar.get_node('EventContainer').get_children(): + for con in section.get_children(): + if con.get_child_count() == 0: + continue + if con.get_child(0) is Label: + con.get_child(0).show() + elif con.get_child(0) is Button: + for button in con.get_children(): + button.toggle_name(true) diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.tscn b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.tscn new file mode 100644 index 0000000000000000000000000000000000000000..6ae797154880da8e1957dc75f39858899db0a94d --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.tscn @@ -0,0 +1,111 @@ +[gd_scene load_steps=10 format=3 uid="uid://ysqbusmy0qma"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd" id="1_8smxc"] +[ext_resource type="Theme" uid="uid://cqst728xxipcw" path="res://addons/dialogic/Editor/Theme/MainTheme.tres" id="2_x0fhp"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd" id="3_sap1x"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd" id="4_ugiq6"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_phyjj"] +content_margin_top = 10.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_207c8"] +bg_color = Color(0, 0, 0, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ias3t"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(1, 0.365, 0.365, 1) +draw_center = false +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +corner_detail = 1 + +[sub_resource type="Image" id="Image_pqmjp"] +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_fyskv"] +image = SubResource("Image_pqmjp") + +[node name="TimelineVisualEditor" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 +script = ExtResource("1_8smxc") + +[node name="View" type="HSplitContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme = ExtResource("2_x0fhp") + +[node name="TimelineArea" type="ScrollContainer" parent="View"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxEmpty_phyjj") +script = ExtResource("3_sap1x") + +[node name="Timeline" type="VBoxContainer" parent="View/TimelineArea"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="EventPopupMenu" type="PopupMenu" parent="View/TimelineArea"] +unique_name_in_owner = true +size = Vector2i(165, 124) +theme_override_styles/panel = SubResource("StyleBoxFlat_207c8") +theme_override_styles/hover = SubResource("StyleBoxFlat_ias3t") +item_count = 6 +item_0/text = "Documentation" +item_0/icon = SubResource("ImageTexture_fyskv") +item_0/id = 0 +item_1/text = "" +item_1/id = -1 +item_1/separator = true +item_2/text = "Move up" +item_2/icon = SubResource("ImageTexture_fyskv") +item_2/id = 2 +item_3/text = "Move down" +item_3/icon = SubResource("ImageTexture_fyskv") +item_3/id = 3 +item_4/text = "" +item_4/id = -1 +item_4/separator = true +item_5/text = "Delete" +item_5/icon = SubResource("ImageTexture_fyskv") +item_5/id = 5 +script = ExtResource("4_ugiq6") + +[node name="RightSidebar" type="ScrollContainer" parent="View"] +unique_name_in_owner = true +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +size_flags_stretch_ratio = 0.2 +horizontal_scroll_mode = 0 + +[node name="EventContainer" type="VBoxContainer" parent="View/RightSidebar"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.2 + +[connection signal="drag_completed" from="View/TimelineArea" to="." method="_on_timeline_area_drag_completed"] +[connection signal="index_pressed" from="View/TimelineArea/EventPopupMenu" to="." method="_on_event_popup_menu_index_pressed"] +[connection signal="resized" from="View/RightSidebar" to="." method="_on_right_sidebar_resized"] diff --git a/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd new file mode 100644 index 0000000000000000000000000000000000000000..8c75f9deb98c3810c1a36bac6aa522829a981726 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd @@ -0,0 +1,37 @@ +extends Control + +func _ready() -> void: + print("[Dialogic] Testing scene was started.") + if !ProjectSettings.get_setting('internationalization/locale/test', "").is_empty(): + print("Testing locale is: ", ProjectSettings.get_setting('internationalization/locale/test')) + $PauseIndictator.hide() + var dialog_scene_path: String = ProjectSettings.get_setting( + 'dialogic/layout/layout_scene', + DialogicUtil.get_default_layout() + ) + var scene: Node = load(dialog_scene_path).instantiate() + DialogicUtil.apply_scene_export_overrides(scene, ProjectSettings.get_setting('dialogic/layout/export_overrides', {})) + add_child(scene) + if not scene is CanvasLayer: + if scene is Control: + scene.position = get_viewport_rect().size/2.0 + if scene is Node2D: + scene.position = get_viewport_rect().size/2.0 + + randomize() + var current_timeline: String = DialogicUtil.get_editor_setting('current_timeline_path') + Dialogic.start_timeline(current_timeline) + Dialogic.timeline_ended.connect(get_tree().quit) + Dialogic.signal_event.connect(recieve_event_signal) + Dialogic.text_signal.connect(recieve_text_signal) + +func recieve_event_signal(argument:String) -> void: + print("[Dialogic] Encountered a signal event: ", argument) + +func recieve_text_signal(argument:String) -> void: + print("[Dialogic] Encountered a signal in text: ", argument) + +func _input(event:InputEvent) -> void: + if event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE: + Dialogic.paused = !Dialogic.paused + $PauseIndictator.visible = Dialogic.paused diff --git a/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.tscn b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.tscn new file mode 100644 index 0000000000000000000000000000000000000000..926b23ffaa930450a52f9a858ffdc38b8f1bdcec --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=2 format=3 uid="uid://ud18ke1g2nw4"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd" id="1_bamud"] + +[node name="TestTimelineScene" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1_bamud") + +[node name="PauseIndictator" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -65.0 +offset_top = 7.0 +offset_right = -8.0 +offset_bottom = 33.0 +grow_horizontal = 0 +text = "Paused" +metadata/_edit_layout_mode = 1 diff --git a/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd b/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd new file mode 100644 index 0000000000000000000000000000000000000000..c8a52493da9fa4bcc8e6dadb827ba3ac912ac4b0 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd @@ -0,0 +1,178 @@ +@tool +extends DialogicEditor + +## Editor that holds both the visual and the text timeline editors. + +# references +var current_editor_mode: int = 0 # 0 = visal, 1 = text +var play_timeline_button : Button = null + +## Overwrite. Register to the editor manager in here. +func _register() -> void: + resource_unsaved.connect(_on_resource_unsaved) + resource_saved.connect(_on_resource_saved) + + # register editor + editors_manager.register_resource_editor('dtl', self) + # add timeline button + var add_timeline_button: Button = editors_manager.add_icon_button( + load("res://addons/dialogic/Editor/Images/Toolbar/add-timeline.svg"), + "Add Timeline", + self) + add_timeline_button.pressed.connect(_on_create_timeline_button_pressed) + add_timeline_button.shortcut = Shortcut.new() + add_timeline_button.shortcut.events.append(InputEventKey.new()) + add_timeline_button.shortcut.events[0].keycode = KEY_1 + add_timeline_button.shortcut.events[0].ctrl_pressed = true + # play timeline button + play_timeline_button = editors_manager.add_custom_button( + "Play Timeline", + get_theme_icon("PlayScene", "EditorIcons"), + self) + play_timeline_button.pressed.connect(play_timeline) + play_timeline_button.tooltip_text = "Play the current timeline (CTRL+F5)" + + %VisualEditor.load_event_buttons() + + current_editor_mode = DialogicUtil.get_editor_setting('timeline_editor_mode', 0) + + match current_editor_mode: + 0: + %VisualEditor.show() + %TextEditor.hide() + %SwitchEditorMode.text = "Text Editor" + 1: + %VisualEditor.hide() + %TextEditor.show() + %SwitchEditorMode.text = "Visual Editor" + + $NoTimelineScreen.show() + play_timeline_button.disabled = true + + +func _get_title() -> String: + return "Timeline" + + +func _get_icon() -> Texture: + return get_theme_icon("TripleBar", "EditorIcons") + + +## If this editor supports editing resources, load them here (overwrite in subclass) +func _open_resource(resource:Resource) -> void: + current_resource = resource + current_resource_state = ResourceStates.SAVED + match current_editor_mode: + 0: + %VisualEditor.load_timeline(current_resource) + 1: + %TextEditor.load_timeline(current_resource) + $NoTimelineScreen.hide() + for t in editors_manager.resource_helper.timeline_directory.keys(): + if editors_manager.resource_helper.timeline_directory[t] == current_resource.resource_path: + %TimelineName.text = t + play_timeline_button.disabled = false + + +## If this editor supports editing resources, save them here (overwrite in subclass) +func _save() -> void: + match current_editor_mode: + 0: + %VisualEditor.save_timeline() + 1: + %TextEditor.save_timeline() + + +func _input(event: InputEvent) -> void: + + if event is InputEventKey and event.keycode == KEY_F5 and event.pressed: + if Input.is_key_pressed(KEY_CTRL): + play_timeline() + + +## Method to play the current timeline. Connected to the button in the sidebar. +func play_timeline(): + _save() + + var dialogic_plugin = DialogicUtil.get_dialogic_plugin() + + # Save the current opened timeline + DialogicUtil.set_editor_setting('current_timeline_path', current_resource.resource_path) + + DialogicUtil.get_dialogic_plugin().get_editor_interface().play_custom_scene("res://addons/dialogic/Editor/TimelineEditor/test_timeline_scene.tscn") + + +## Method to switch from visual to text editor (and vice versa). Connected to the button in the sidebar. +func toggle_editor_mode(): + match current_editor_mode: + 0: + current_editor_mode = 1 + %VisualEditor.save_timeline() + %VisualEditor.hide() + %TextEditor.show() + %TextEditor.load_timeline(current_resource) + %SwitchEditorMode.text = "Visual Editor" + 1: + current_editor_mode = 0 + %TextEditor.save_timeline() + %TextEditor.hide() + %VisualEditor.load_timeline(current_resource) + %VisualEditor.show() + %SwitchEditorMode.text = "Text Editor" + + DialogicUtil.set_editor_setting('timeline_editor_mode', current_editor_mode) + + +func _on_resource_unsaved(): + if current_resource: + current_resource.set_meta("timeline_not_saved", true) + + +func _on_resource_saved(): + if current_resource: + current_resource.set_meta("timeline_not_saved", false) + + +func new_timeline(path:String) -> void: + _save() + var new_timeline := DialogicTimeline.new() + new_timeline.resource_path = path + new_timeline.set_meta('timeline_not_saved', true) + var err := ResourceSaver.save(new_timeline) + editors_manager.resource_helper.rebuild_timeline_directory() + editors_manager.edit_resource(new_timeline) + + +func _ready(): + $NoTimelineScreen.add_theme_stylebox_override("panel", get_theme_stylebox("Background", "EditorStyles")) + + # switch editor mode button + %SwitchEditorMode.text = "Text editor" + %SwitchEditorMode.icon = get_theme_icon("ArrowRight", "EditorIcons") + %SwitchEditorMode.pressed.connect(toggle_editor_mode) + var _scale := DialogicUtil.get_editor_scale() + %SwitchEditorMode.custom_minimum_size.x = 200 * _scale + + + + + +func _on_create_timeline_button_pressed(): + editors_manager.show_add_resource_dialog( + new_timeline, + '*.dtl; DialogicTimeline', + 'Create new timeline', + 'timeline', + ) + + +func _clear(): + current_resource = null + current_resource_state = ResourceStates.SAVED + match current_editor_mode: + 0: + %VisualEditor.clear_timeline_nodes() + 1: + %TextEditor.clear_timeline() + $NoTimelineScreen.show() + play_timeline_button.disabled = true diff --git a/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn b/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn new file mode 100644 index 0000000000000000000000000000000000000000..a3d0c1b2bd413c593ac81bf71865082910869905 --- /dev/null +++ b/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn @@ -0,0 +1,131 @@ +[gd_scene load_steps=10 format=3 uid="uid://crce0na84rhfd"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/timeline_editor.gd" id="1_4aceh"] +[ext_resource type="PackedScene" uid="uid://ysqbusmy0qma" path="res://addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.tscn" id="2_qs7vc"] +[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_yqd26"] +[ext_resource type="PackedScene" uid="uid://defdeav8rli6o" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.tscn" id="3_up2bn"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd" id="4_1t6bf"] + +[sub_resource type="Image" id="Image_pnrtc"] +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_ajdpw"] +image = SubResource("Image_pnrtc") + +[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_7lpql"] +script = ExtResource("4_1t6bf") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dumog"] +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="Timeline" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_4aceh") + +[node name="VBox" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="HBox" type="HBoxContainer" parent="VBox"] +layout_mode = 2 + +[node name="TimelineName" type="Label" parent="VBox/HBox"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicTitle" +text = "Cool Name" + +[node name="NameTooltip" parent="VBox/HBox" instance=ExtResource("2_yqd26")] +layout_mode = 2 +tooltip_text = "The name of the timeline is determined from the file name. +This is what you should use in a jump event to reference this timeline. + +Besides the file path, you can also use this name in Dialogic.start()" +texture = SubResource("ImageTexture_ajdpw") +hint_text = "The name of the timeline is determined from the file name. +This is what you should use in a jump event to reference this timeline. + +Besides the file path, you can also use this name in Dialogic.start()" + +[node name="SwitchEditorMode" type="Button" parent="VBox/HBox"] +unique_name_in_owner = true +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Switch between Text Editor and Visual Editor" +text = "Text editor" +icon = SubResource("ImageTexture_ajdpw") + +[node name="VisualEditor" parent="VBox" instance=ExtResource("2_qs7vc")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 0 +theme_override_constants/margin_top = 0 +theme_override_constants/margin_right = 0 +theme_override_constants/margin_bottom = 0 + +[node name="TextEditor" parent="VBox" instance=ExtResource("3_up2bn")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +syntax_highlighter = SubResource("SyntaxHighlighter_7lpql") +symbol_lookup_on_click = true +line_folding = false +gutters_draw_fold_gutter = false + +[node name="NoTimelineScreen" type="PanelContainer" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_dumog") + +[node name="CenterContainer" type="CenterContainer" parent="NoTimelineScreen"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="NoTimelineScreen/CenterContainer"] +custom_minimum_size = Vector2(250, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="NoTimelineScreen/CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "No timeline opened. +Create a timeline or double-click one in the file system dock." +horizontal_alignment = 1 +autowrap_mode = 3 + +[node name="CreateTimelineButton" type="Button" parent="NoTimelineScreen/CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "Create New Timeline" + +[connection signal="pressed" from="NoTimelineScreen/CenterContainer/VBoxContainer/CreateTimelineButton" to="." method="_on_create_timeline_button_pressed"] diff --git a/addons/dialogic/Editor/dialogic_editor.gd b/addons/dialogic/Editor/dialogic_editor.gd new file mode 100644 index 0000000000000000000000000000000000000000..5b731d2dc625959539a42cd59153aaa900d622c5 --- /dev/null +++ b/addons/dialogic/Editor/dialogic_editor.gd @@ -0,0 +1,64 @@ +@tool +class_name DialogicEditor +extends Control + +## Base class for all dialogic editors. + +# These signals will automatically be emitted if current_resource_state is changed. +signal resource_saved() +signal resource_unsaved() + +var current_resource: Resource + +## State of the current resource +enum ResourceStates {SAVED, UNSAVED} +var current_resource_state: ResourceStates: + set(value): + current_resource_state = value + if value == ResourceStates.SAVED: + resource_saved.emit() + else: + resource_unsaved.emit() + +var editors_manager: Control +# text displayed on the current resource label on non-resource editors +var alternative_text: String = "" + +## Overwrite. Register to the editor manager in here. +func _register() -> void: + pass + + +## Used on the tab +func _get_icon() -> Texture: + return null + +## Used on the tab +func _get_title() -> String: + return "" + + +## If this editor supports editing resources, load them here (overwrite in subclass) +func _open_resource(resource:Resource) -> void: + pass + + +## If this editor supports editing resources, save them here (overwrite in subclass) +func _save() -> void: + pass + + +## Overwrite. Called when this editor is shown. (show() doesn't have to be called) +func _open(extra_info:Variant = null) -> void: + pass + + +## Overwrite. Called when another editor is opened. (hide() doesn't have to be called) +func _close(): + pass + + +## Overwrite. Called to clear all current state and resource from the editor. +## Although rarely used, sometimes you just want NO timeline to be open. +func _clear(): + pass diff --git a/addons/dialogic/Editor/directory_holder.gd b/addons/dialogic/Editor/directory_holder.gd new file mode 100644 index 0000000000000000000000000000000000000000..a2517e4304d2556f943c33bf49c399d66803a7c1 --- /dev/null +++ b/addons/dialogic/Editor/directory_holder.gd @@ -0,0 +1,91 @@ +@tool +extends Node + +## Node that holds timeline and character directories for use in editor. + +# barebones instance of DGH, with local Editor references to the event cache and charcater directory +var dialogic_handler: Node + +var event_script_cache: Array[DialogicEvent] = [] +var character_directory: Dictionary = {} +var timeline_directory: Dictionary = {} +var label_directory :Dictionary = {}: + set(value): + label_directory = value + Engine.get_main_loop().set_meta("dialogic_label_directory", value) + + + +func _ready() -> void: + if owner.get_parent() is SubViewport: + return + + ## DIRECTORIES SETUP + #initialize DGH, and set the local variables to references of the DGH ones + #since we're not actually adding it to the event node, we have to manually run the commands to build the cache's + dialogic_handler = load("res://addons/dialogic/Other/DialogicGameHandler.gd").new() + rebuild_character_directory() + rebuild_timeline_directory() + rebuild_event_script_cache() + label_directory = DialogicUtil.get_editor_setting('label_ref', {}) + for i in label_directory: + if !i in timeline_directory: + label_directory.erase(i) + + find_parent('EditorView').plugin_reference.get_editor_interface().get_file_system_dock().files_moved.connect(_on_file_moved) + + +func _on_file_moved(old_name:String, new_name:String) -> void: + if old_name.ends_with('.dch'): + rebuild_character_directory() + elif old_name.ends_with('.dtl'): + rebuild_timeline_directory() + + +func rebuild_event_script_cache() -> Array: + event_script_cache = [] + if dialogic_handler != null: + dialogic_handler.collect_subsystems() + event_script_cache = dialogic_handler._event_script_cache + else: + for indexer in DialogicUtil.get_indexers(): + # build event cache + for event in indexer._get_events(): + if not 'event_end_branch.gd' in event and not 'event_text.gd' in event: + event_script_cache.append(load(event).new()) + + # Events are checked in order while testing them. EndBranch needs to be first, Text needs to be last + event_script_cache.push_front(DialogicEndBranchEvent.new()) + event_script_cache.push_back(DialogicTextEvent.new()) + + Engine.get_main_loop().set_meta("dialogic_event_cache", event_script_cache) + + return event_script_cache + + +func rebuild_character_directory() -> void: + character_directory = {} + if dialogic_handler != null: + dialogic_handler.rebuild_character_directory() + character_directory = dialogic_handler.character_directory + + +func get_character_short_path(resource:DialogicCharacter) -> String: + for chr in character_directory.values(): + if chr.resource == resource: + return chr.unique_short_path + return resource.resource_path.get_file().trim_suffix(resource.resource_path.get_extension()) + + +func rebuild_timeline_directory() -> void: + timeline_directory = {} + if dialogic_handler != null: + dialogic_handler.rebuild_timeline_directory() + timeline_directory = dialogic_handler.timeline_directory + + +func get_event_scripts() -> Array: + if event_script_cache.size() > 0: + return event_script_cache + else: + return rebuild_event_script_cache() diff --git a/addons/dialogic/Editor/editor_main.gd b/addons/dialogic/Editor/editor_main.gd new file mode 100644 index 0000000000000000000000000000000000000000..484dbe67dc0faa52b51ab20bd6a42bcc701cd5ec --- /dev/null +++ b/addons/dialogic/Editor/editor_main.gd @@ -0,0 +1,183 @@ +@tool +extends ColorRect + +## Editor root node. Most editor functionality is handled by EditorsManager node! + +var plugin_reference: EditorPlugin = null +var editors_manager: Control = null + +var editor_file_dialog: EditorFileDialog + +## Styling +@export var editor_tab_bg := StyleBoxFlat.new() + + +func _ready() -> void: + if get_parent() is SubViewport: + return + + ## REFERENCES + editors_manager = $Margin/EditorsManager + + ## STYLING + color = get_theme_color("base_color", "Editor") + editor_tab_bg.border_color = get_theme_color("base_color", "Editor") + editor_tab_bg.bg_color = get_theme_color("dark_color_2", "Editor") + $Margin/EditorsManager.editors_holder.add_theme_stylebox_override('panel', editor_tab_bg) + + # File dialog + editor_file_dialog = EditorFileDialog.new() + add_child(editor_file_dialog) + + var info_message := Label.new() + info_message.add_theme_color_override('font_color', get_theme_color("warning_color", "Editor")) + editor_file_dialog.get_line_edit().get_parent().add_sibling(info_message) + info_message.get_parent().move_child(info_message, info_message.get_index()-1) + editor_file_dialog.set_meta('info_message_label', info_message) + + $SaveConfirmationDialog.add_button('No Saving Please!', true, 'nosave') + $SaveConfirmationDialog.hide() + update_theme_additions() + + +func update_theme_additions(): + if theme == null: + theme = Theme.new() + theme.clear() + + theme.set_type_variation('DialogicTitle', 'Label') + theme.set_font('font', 'DialogicTitle', get_theme_font("title", "EditorFonts")) + theme.set_color('font_color', 'DialogicTitle', get_theme_color('warning_color', 'Editor')) + theme.set_font_size('font_size', 'DialogicTitle', get_theme_font_size("doc_size", "EditorFonts")) + + theme.set_type_variation('DialogicSubTitle', 'Label') + theme.set_font('font', 'DialogicSubTitle', get_theme_font("title", "EditorFonts")) + theme.set_font_size('font_size', 'DialogicSubTitle', get_theme_font_size("doc_size", "EditorFonts")) + theme.set_color('font_color', 'DialogicSubTitle', get_theme_color('accent_color', 'Editor')) + + theme.set_type_variation('DialogicPanelA', 'PanelContainer') + var panel_style := DCSS.inline({ + 'border-radius': 10, + 'border': 0, + 'border_color':get_theme_color("dark_color_3", "Editor"), + 'background': get_theme_color("base_color", "Editor"), + 'padding': [5, 5], + }) + theme.set_stylebox('panel', 'DialogicPanelA', panel_style) + theme.set_stylebox('normal', 'DialogicPanelA', panel_style) + + var dark_panel := panel_style.duplicate() + dark_panel.bg_color = get_theme_color("dark_color_3", "Editor") + theme.set_stylebox('panel', 'DialogicPanelDarkA', dark_panel) + + + + # panel used for example for portrait previews in character editor + theme.set_type_variation('DialogicPanelB', 'PanelContainer') + var side_panel :StyleBoxFlat= panel_style.duplicate() + side_panel.corner_radius_top_left = 0 + side_panel.corner_radius_bottom_left = 0 + side_panel.expand_margin_left = 8 + side_panel.bg_color = get_theme_color("dark_color_2", "Editor") + side_panel.set_border_width_all(1) + side_panel.border_width_left = 0 + side_panel.border_color = get_theme_color("contrast_color_2", "Editor") + theme.set_stylebox('panel', 'DialogicPanelB', side_panel) + + + theme.set_type_variation('DialogicEventEdit', 'Control') + var edit_panel := StyleBoxFlat.new() + edit_panel.draw_center = true + edit_panel.bg_color = get_theme_color("accent_color", "Editor") + edit_panel.bg_color.a = 0.05 + edit_panel.border_width_bottom = 2 + edit_panel.border_color = get_theme_color("accent_color", "Editor").lerp(get_theme_color("dark_color_2", "Editor"), 0.4) + edit_panel.content_margin_left = 5 + edit_panel.content_margin_right = 5 + edit_panel.set_corner_radius_all(1) + theme.set_stylebox('panel', 'DialogicEventEdit', edit_panel) + theme.set_stylebox('normal', 'DialogicEventEdit', edit_panel) + + var focus_edit := edit_panel.duplicate() + focus_edit.border_color = get_theme_color("property_color_z", "Editor") + focus_edit.draw_center = false + theme.set_stylebox('focus', 'DialogicEventEdit', focus_edit) + + var hover_edit := edit_panel.duplicate() + hover_edit.border_color = get_theme_color("warning_color", "Editor") + theme.set_stylebox('hover', 'DialogicEventEdit', hover_edit) + + var disabled_edit := edit_panel.duplicate() + disabled_edit.border_color = get_theme_color("property_color", "Editor") + theme.set_stylebox('disabled', 'DialogicEventEdit', disabled_edit) + + theme.set_type_variation('DialogicHintText', 'Label') + theme.set_color('font_color', 'DialogicHintText', get_theme_color("readonly_color", "Editor")) + theme.set_font('font', 'DialogicHintText', get_theme_font("doc_italic", "EditorFonts")) + + theme.set_type_variation('DialogicHintText2', 'Label') + theme.set_color('font_color', 'DialogicHintText2', get_theme_color("property_color_w", "Editor")) + theme.set_font('font', 'DialogicHintText2', get_theme_font("doc_italic", "EditorFonts")) + + theme.set_type_variation('DialogicSection', 'Label') + theme.set_font('font', 'DialogicSection', get_theme_font("main_msdf", "EditorFonts")) + theme.set_color('font_color', 'DialogicSection', get_theme_color("property_color_z", "Editor")) + theme.set_font_size('font_size', 'DialogicSection', get_theme_font_size("doc_size", "EditorFonts")) + + theme.set_type_variation('DialogicSettingsSection', 'DialogicSection') + theme.set_font('font', 'DialogicSettingsSection', get_theme_font("main_msdf", "EditorFonts")) + theme.set_color('font_color', 'DialogicSettingsSection', get_theme_color("property_color_z", "Editor")) + theme.set_font_size('font_size', 'DialogicSettingsSection', get_theme_font_size("doc_size", "EditorFonts")) + + theme.set_type_variation('DialogicSectionBig', 'DialogicSection') + theme.set_color('font_color', 'DialogicSectionBig', get_theme_color("accent_color", "Editor")) + theme.set_font_size('font_size', 'DialogicSectionBig', get_theme_font_size("doc_title_size", "EditorFonts")) + + theme.set_type_variation('DialogicLink', 'LinkButton') + theme.set_color('font_hover_color', 'DialogicLink', get_theme_color("warning_color", "Editor")) + + theme.set_type_variation('DialogicMegaSeparator', 'HSeparator') + theme.set_stylebox('separator', 'DialogicMegaSeparator', DCSS.inline({ + 'border-radius': 10, + 'border': 0, + 'background': get_theme_color("accent_color", "Editor"), + 'padding': [5, 5], + })) + theme.set_constant('separation', 'DialogicMegaSeparator', 50) + + + + theme.set_icon('Plugin', 'Dialogic', load("res://addons/dialogic/Editor/Images/plugin-icon.svg")) +# theme.set_icon('Character', 'Dialogic', load("res://addons/dialogic/Editor/Images/Resources/character.svg")) +# theme.set_icon('Portrait', 'Dialogic', load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")) +# theme.set_icon('Timeline', 'Dialogic', get_theme_icon("TripleBar", "EditorIcons")) + + + + +func godot_file_dialog(callable:Callable, filter:String, mode := EditorFileDialog.FILE_MODE_OPEN_FILE, window_title := "Save", current_file_name := 'New_File', saving_something := false, extra_message:String = "") -> EditorFileDialog: + for connection in editor_file_dialog.file_selected.get_connections(): + editor_file_dialog.file_selected.disconnect(connection.callable) + for connection in editor_file_dialog.dir_selected.get_connections(): + editor_file_dialog.dir_selected.disconnect(connection.callable) + editor_file_dialog.file_mode = mode + editor_file_dialog.clear_filters() + editor_file_dialog.popup_centered_ratio(0.6) + editor_file_dialog.add_filter(filter) + editor_file_dialog.title = window_title + editor_file_dialog.current_file = current_file_name + editor_file_dialog.disable_overwrite_warning = !saving_something + if extra_message: + editor_file_dialog.get_meta('info_message_label').show() + editor_file_dialog.get_meta('info_message_label').text = extra_message + else: + editor_file_dialog.get_meta('info_message_label').hide() + + if mode == EditorFileDialog.FILE_MODE_OPEN_FILE or mode == EditorFileDialog.FILE_MODE_SAVE_FILE: + editor_file_dialog.file_selected.connect(callable) + elif mode == EditorFileDialog.FILE_MODE_OPEN_DIR: + editor_file_dialog.dir_selected.connect(callable) + elif mode == EditorFileDialog.FILE_MODE_OPEN_ANY: + editor_file_dialog.dir_selected.connect(callable) + editor_file_dialog.file_selected.connect(callable) + return editor_file_dialog diff --git a/addons/dialogic/Editor/editor_main.tscn b/addons/dialogic/Editor/editor_main.tscn new file mode 100644 index 0000000000000000000000000000000000000000..47825d7e1abca0b53c33e51a86e5516d4e05ae81 --- /dev/null +++ b/addons/dialogic/Editor/editor_main.tscn @@ -0,0 +1,154 @@ +[gd_scene load_steps=16 format=3 uid="uid://de6yhw4r8jqb3"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/editor_main.gd" id="1_x88ov"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/editors_manager.gd" id="2_pe2tl"] +[ext_resource type="Texture2D" uid="uid://dybg3l5pwetne" path="res://addons/dialogic/Editor/Images/plugin-icon.svg" id="2_scwcl"] +[ext_resource type="PackedScene" uid="uid://cwe3r2tbh2og1" path="res://addons/dialogic/Editor/Common/side_bar.tscn" id="3_lp6hj"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/toolbar.gd" id="4_6cx8s"] +[ext_resource type="Texture2D" uid="uid://bbea0efx0ybu7" path="res://addons/dialogic/Editor/Images/Resources/character.svg" id="6_8yp76"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/directory_holder.gd" id="7_1xvr0"] +[ext_resource type="Texture2D" uid="uid://b5xwnxdb7064n" path="res://addons/dialogic/Modules/Glossary/icon.svg" id="7_45ytg"] +[ext_resource type="Texture2D" uid="uid://1mccycya6eua" path="res://addons/dialogic/Modules/LayoutEditor/styles_icon.svg" id="8_jj1i6"] +[ext_resource type="Texture2D" uid="uid://ckilxvwc34s84" path="res://addons/dialogic/Modules/Variable/variable.svg" id="9_k4reh"] +[ext_resource type="PackedScene" uid="uid://c7lmt5cp7bxcm" path="res://addons/dialogic/Editor/Common/broken_reference_manager.tscn" id="10_l1rf8"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/reference_manager_window.gd" id="10_xbkrt"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7lxu4"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 6.0 +content_margin_bottom = 6.0 +bg_color = Color(0.1155, 0.132, 0.1595, 1) +border_width_left = 2 +border_width_bottom = 2 +border_color = Color(0.21, 0.24, 0.29, 1) +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 +expand_margin_left = 2.0 +expand_margin_top = 2.0 + +[sub_resource type="Image" id="Image_7vepm"] +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_drcn6"] +image = SubResource("Image_7vepm") + +[node name="EditorView" type="ColorRect"] +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 +color = Color(0, 0, 0, 1) +script = ExtResource("1_x88ov") +editor_tab_bg = SubResource("StyleBoxFlat_7lxu4") + +[node name="Margin" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 2 + +[node name="EditorsManager" type="Control" parent="Margin"] +layout_mode = 2 +script = ExtResource("2_pe2tl") + +[node name="HSplit" type="HSplitContainer" parent="Margin/EditorsManager"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 0 +split_offset = 150 + +[node name="Sidebar" parent="Margin/EditorsManager/HSplit" instance=ExtResource("3_lp6hj")] +unique_name_in_owner = true +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 +split_offset = 0 + +[node name="VBox" type="VBoxContainer" parent="Margin/EditorsManager/HSplit"] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="Toolbar" type="HBoxContainer" parent="Margin/EditorsManager/HSplit/VBox"] +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 2 +alignment = 2 +script = ExtResource("4_6cx8s") + +[node name="EditorTabBar" type="TabBar" parent="Margin/EditorsManager/HSplit/VBox/Toolbar"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 8 +tab_count = 7 +tab_0/title = "" +tab_0/icon = ExtResource("2_scwcl") +tab_1/title = "Timeline" +tab_1/icon = SubResource("ImageTexture_drcn6") +tab_2/title = "Character" +tab_2/icon = ExtResource("6_8yp76") +tab_3/title = "Glossary" +tab_3/icon = ExtResource("7_45ytg") +tab_4/title = "Layouts" +tab_4/icon = ExtResource("8_jj1i6") +tab_5/title = "Variables" +tab_5/icon = ExtResource("9_k4reh") +tab_6/title = "Settings" +tab_6/icon = SubResource("ImageTexture_drcn6") + +[node name="CustomButtons" type="HBoxContainer" parent="Margin/EditorsManager/HSplit/VBox/Toolbar"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Editors" type="PanelContainer" parent="Margin/EditorsManager/HSplit/VBox"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ResourceHelper" type="Node" parent="Margin/EditorsManager"] +script = ExtResource("7_1xvr0") + +[node name="SaveConfirmationDialog" type="AcceptDialog" parent="."] +size = Vector2i(207, 100) + +[node name="ResourceRenameWarning" type="AcceptDialog" parent="."] +initial_position = 5 +title = "Dialogic resource renamed!" +size = Vector2i(494, 135) +ok_button_text = "Check for broken references" +dialog_text = "You renamed a dialogic resource. If this resource was referenced in timelines, you should check that no references where broken." +dialog_autowrap = true + +[node name="ReferenceManager" type="Window" parent="."] +disable_3d = true +initial_position = 2 +title = "Reference Manager" +size = Vector2i(858, 442) +visible = false +wrap_controls = true +content_scale_mode = 1 +content_scale_aspect = 4 +script = ExtResource("10_xbkrt") + +[node name="Manager" parent="ReferenceManager" instance=ExtResource("10_l1rf8")] + +[connection signal="confirmed" from="SaveConfirmationDialog" to="." method="_on_SaveConfirmationDialog_confirmed"] +[connection signal="custom_action" from="SaveConfirmationDialog" to="." method="_on_SaveConfirmationDialog_custom_action"] +[connection signal="close_requested" from="ReferenceManager" to="ReferenceManager" method="_on_close_requested"] diff --git a/addons/dialogic/Editor/editors_manager.gd b/addons/dialogic/Editor/editors_manager.gd new file mode 100644 index 0000000000000000000000000000000000000000..79830d8dbec7132057b2017814c1eafa7335991d --- /dev/null +++ b/addons/dialogic/Editor/editors_manager.gd @@ -0,0 +1,262 @@ +@tool +extends Control + +## Node that manages editors, the toolbar and the sidebar. + +signal resource_opened(resource) +signal editor_changed(previous, current) + +### References +@onready var sidebar = $HSplit/Sidebar +@onready var editors_holder = $HSplit/VBox/Editors +@onready var toolbar = $HSplit/VBox/Toolbar +@onready var tabbar = $HSplit/VBox/Toolbar/EditorTabBar +var resource_helper: Node: + get: + return get_node("ResourceHelper") +var reference_manager: Node: + get: + return get_node("../../ReferenceManager") +## Information on supported resources and registered editors +var current_editor: DialogicEditor = null +var previous_editor: DialogicEditor = null +var editors := {} +var resources := [] +var used_resources_cache : Array = [] + +################################################################################ +## REGISTERING EDITORS +################################################################################ + +## Asks all childs of the editor holder to register +func _ready() -> void: + if owner.get_parent() is SubViewport: + return + + tabbar.clear_tabs() + + # Load base editors + _add_editor("res://addons/dialogic/Editor/HomePage/home_page.tscn") + _add_editor("res://addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn") + _add_editor("res://addons/dialogic/Editor/CharacterEditor/character_editor.tscn") + + + # Load custom editors + for indexer in DialogicUtil.get_indexers(): + for editor_path in indexer._get_editors(): + _add_editor(editor_path) + _add_editor("res://addons/dialogic/Editor/Settings/settings_editor.tscn") + + tabbar.tab_clicked.connect(_on_editors_tab_changed) + + # Needs to be done here to make sure this node is ready when doing the register calls + for editor in editors_holder.get_children(): + editor.editors_manager = self + editor._register() + + await get_parent().get_parent().ready + await get_tree().process_frame + load_saved_state() + used_resources_cache = DialogicUtil.get_editor_setting('last_resources', []) + for res in used_resources_cache: + if !FileAccess.file_exists(res): + used_resources_cache.erase(res) + sidebar.update_resource_list(used_resources_cache) + + find_parent('EditorView').plugin_reference.get_editor_interface().get_file_system_dock().files_moved.connect(_on_file_moved) + + +func _add_editor(path:String) -> void: + var editor :DialogicEditor = load(path).instantiate() + editors_holder.add_child(editor) + editor.hide() + tabbar.add_tab(editor._get_title(), editor._get_icon()) + + +## Call to register an editor/tab that edits a resource with a custom ending. +func register_resource_editor(resource_extension:String, editor:DialogicEditor) -> void: + editors[editor.name] = {'node':editor, 'buttons':[], 'extension': resource_extension} + resources.append(resource_extension) + editor.resource_saved.connect(_on_resource_saved.bind(editor)) + editor.resource_unsaved.connect(_on_resource_unsaved.bind(editor)) + + +## Call to register an editor/tab that doesn't edit a resource +func register_simple_editor(editor:DialogicEditor) -> void: + editors[editor.name] = {'node': editor, 'buttons':[]} + + +## Call to add an icon button. These buttons are always visible. +func add_icon_button(icon:Texture, tooltip:String, editor:DialogicEditor=null) -> Node: + var button: Button = toolbar.add_icon_button(icon, tooltip) + if editor != null: + editors[editor.name]['buttons'].append(button) + return button + + +## Call to add a custom action button. Only visible if editor is visible. +func add_custom_button(label:String, icon:Texture, editor:DialogicEditor) -> Node: + var button: Button = toolbar.add_custom_button(label, icon) + editors[editor.name]['buttons'].append(button) + return button + + +func can_edit_resource(resource:Resource) -> bool: + return resource.resource_path.get_extension() in resources + + +################################################################################ +## OPENING/CLOSING +################################################################################ + +func _on_editors_tab_changed(tab:int) -> void: + open_editor(editors_holder.get_child(tab)) + + +func edit_resource(resource:Resource, save_previous:bool = true, silent:= false) -> void: + if resource: + if current_editor and save_previous: + current_editor._save() + + if !resource.resource_path in used_resources_cache: + used_resources_cache.append(resource.resource_path) + sidebar.update_resource_list(used_resources_cache) + + ## Open the correct editor + var extension: String = resource.resource_path.get_extension() + for editor in editors.values(): + if editor.get('extension', '') == extension: + editor['node']._open_resource(resource) + if !silent: + open_editor(editor['node'], false) + if !silent: + resource_opened.emit(resource) + else: + # The resource doesn't exists, show an error + print('[Dialogic] The resource you are trying to edit doesn\'t exists any more.') + + +## Only works if there was a different editor opened previously +func toggle_editor(editor) -> void: + if editor.visible: + open_editor(previous_editor, true) + else: + open_editor(editor, true) + + +## Shows the given editor +func open_editor(editor:DialogicEditor, save_previous: bool = true, extra_info:Variant = null) -> void: + if current_editor and save_previous: + current_editor._save() + + if current_editor: + current_editor._close() + current_editor.hide() + + if current_editor != previous_editor: + previous_editor = current_editor + + editor._open(extra_info) + current_editor = editor + editor.show() + tabbar.current_tab = editor.get_index() + + if editor.current_resource: + var text:String = editor.current_resource.resource_path.get_file() + if editor.current_resource_state == DialogicEditor.ResourceStates.UNSAVED: + text += "(*)" + + ## This makes custom button editor-specific + ## I think it's better without. +# toolbar.hide_all_custom_buttons() +# for button in editors[current_editor.name]['buttons']: +# button.show() + + save_current_state() + editor_changed.emit(previous_editor, current_editor) + + +## Rarely used to completely clear a editor. +func clear_editor(editor:DialogicEditor, save:bool = false) -> void: + if save: + editor._save() + + editor._clear() + +## Shows a file selector. Calls [accept_callable] once accepted +func show_add_resource_dialog(accept_callable:Callable, filter:String = "*", title = "New resource", default_name = "new_character", mode = EditorFileDialog.FILE_MODE_SAVE_FILE) -> void: + find_parent('EditorView').godot_file_dialog( + accept_callable, + filter, + mode, + title, + default_name, + true, + "Do not use \"'()!;:/\\*# in character or timeline names!" + ) + + +## Called by the plugin.gd script on CTRL+S or Debug Game start +func save_current_resource() -> void: + current_editor._save() + + +## Change the resource state +func _on_resource_saved(editor:DialogicEditor): + sidebar.set_unsaved_indicator(true) + + +## Change the resource state +func _on_resource_unsaved(editor:DialogicEditor): + sidebar.set_unsaved_indicator(false) + + +## Tries opening the last resource +func load_saved_state() -> void: + var current_resources: Dictionary = DialogicUtil.get_editor_setting('current_resources', {}) + for editor in current_resources.keys(): + editors[editor]['node']._open_resource(load(current_resources[editor])) + + var current_editor: String = DialogicUtil.get_editor_setting('current_editor', 'HomePage') + open_editor(editors[current_editor]['node']) + + +func save_current_state() -> void: + DialogicUtil.set_editor_setting('current_editor', current_editor.name) + var current_resources: Dictionary = {} + for editor in editors.values(): + if editor['node'].current_resource != null: + current_resources[editor['node'].name] = editor['node'].current_resource.resource_path + DialogicUtil.set_editor_setting('current_resources', current_resources) + + +func _on_file_moved(old_name:String, new_name:String) -> void: + if !old_name.get_extension() in resources: + return + + used_resources_cache = DialogicUtil.get_editor_setting('last_resources', []) + if old_name in used_resources_cache: + used_resources_cache.insert(used_resources_cache.find(old_name), new_name) + used_resources_cache.erase(old_name) + + sidebar.update_resource_list(used_resources_cache) + + for editor in editors: + if editors[editor].node.current_resource != null and editors[editor].node.current_resource.resource_path == old_name: + editors[editor].node.current_resource.take_over_path(new_name) + edit_resource(load(new_name), true, true) + + save_current_state() + + +################################################################################ +## HELPERS +################################################################################ + + +func get_current_editor() -> DialogicEditor: + return current_editor + + +func _exit_tree(): + DialogicUtil.set_editor_setting('last_resources', used_resources_cache) diff --git a/addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf b/addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d998cf5b468413ca1c950096dc9d0f5dfdb1359f Binary files /dev/null and b/addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf differ diff --git a/addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf.import b/addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf.import new file mode 100644 index 0000000000000000000000000000000000000000..694a2aeb61bd1128fb67e1f1389e0b130df5ebfe --- /dev/null +++ b/addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://cc4xli25271fd" +path="res://.godot/imported/Roboto-Bold.ttf-a0c3395776dbc11ee676c5f1ea9c0579.fontdata" + +[deps] + +source_file="res://addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf" +dest_files=["res://.godot/imported/Roboto-Bold.ttf-a0c3395776dbc11ee676c5f1ea9c0579.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf b/addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5b390ff950e6539bdfd4d84265fdb01e0c35f291 Binary files /dev/null and b/addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf differ diff --git a/addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf.import b/addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf.import new file mode 100644 index 0000000000000000000000000000000000000000..d7c809a856c7ab5b4d1bda77898ad6f81832f726 --- /dev/null +++ b/addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://b5c0p00x6g6u5" +path="res://.godot/imported/Roboto-Italic.ttf-844485a0171d6031f98f4829003a881a.fontdata" + +[deps] + +source_file="res://addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf" +dest_files=["res://.godot/imported/Roboto-Italic.ttf-844485a0171d6031f98f4829003a881a.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf b/addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2b6392ffe8712b9c5450733320cd220d6c0f4bce Binary files /dev/null and b/addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf differ diff --git a/addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf.import b/addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf.import new file mode 100644 index 0000000000000000000000000000000000000000..16d8db10f69a39d654e113f40c73897e7b764c21 --- /dev/null +++ b/addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://vrrmdx83skor" +path="res://.godot/imported/Roboto-Regular.ttf-d9ce0640effe9e93230b445b37d8e692.fontdata" + +[deps] + +source_file="res://addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf" +dest_files=["res://.godot/imported/Roboto-Regular.ttf-d9ce0640effe9e93230b445b37d8e692.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/dialogic/Example Assets/already_read_indicator.gd b/addons/dialogic/Example Assets/already_read_indicator.gd new file mode 100644 index 0000000000000000000000000000000000000000..926aa4ced5da30885519977f748bcffe852d4f07 --- /dev/null +++ b/addons/dialogic/Example Assets/already_read_indicator.gd @@ -0,0 +1,12 @@ +extends Control + +func _ready(): + if Dialogic.has_subsystem('History'): + Dialogic.History.already_read_event_reached.connect(_on_already_read_event) + Dialogic.History.not_read_event_reached.connect(_on_not_read_event) + +func _on_already_read_event() -> void: + show() + +func _on_not_read_event() -> void: + hide() diff --git a/addons/dialogic/Example Assets/backgrounds/BubbleEnd.png b/addons/dialogic/Example Assets/backgrounds/BubbleEnd.png new file mode 100644 index 0000000000000000000000000000000000000000..c0ff16161436f2bdeb104073c0143d26f7fcb017 Binary files /dev/null and b/addons/dialogic/Example Assets/backgrounds/BubbleEnd.png differ diff --git a/addons/dialogic/Example Assets/backgrounds/BubbleEnd.png.import b/addons/dialogic/Example Assets/backgrounds/BubbleEnd.png.import new file mode 100644 index 0000000000000000000000000000000000000000..0e575987eedd552ba05f779fee1989319256a5ab --- /dev/null +++ b/addons/dialogic/Example Assets/backgrounds/BubbleEnd.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3sccqj6l42w6" +path="res://.godot/imported/BubbleEnd.png-a2bd812e4aeb33a7c97291d41dcc1793.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/backgrounds/BubbleEnd.png" +dest_files=["res://.godot/imported/BubbleEnd.png-a2bd812e4aeb33a7c97291d41dcc1793.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/backgrounds/new-default-dialog.png.import b/addons/dialogic/Example Assets/backgrounds/new-default-dialog.png.import new file mode 100644 index 0000000000000000000000000000000000000000..7cbe695e763c6381d0f2a47bb30c0cb1731d67a4 --- /dev/null +++ b/addons/dialogic/Example Assets/backgrounds/new-default-dialog.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3mgfla25qml3" +path="res://.godot/imported/new-default-dialog.png-1bc90907f2427bd6cdb905ff375cdb22.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/backgrounds/new-default-dialog.png" +dest_files=["res://.godot/imported/new-default-dialog.png-1bc90907f2427bd6cdb905ff375cdb22.ctex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/backgrounds/rpg_box.webp b/addons/dialogic/Example Assets/backgrounds/rpg_box.webp new file mode 100644 index 0000000000000000000000000000000000000000..e1d2a109251813d35f42156f70841fe063dbaa7e Binary files /dev/null and b/addons/dialogic/Example Assets/backgrounds/rpg_box.webp differ diff --git a/addons/dialogic/Example Assets/backgrounds/rpg_box.webp.import b/addons/dialogic/Example Assets/backgrounds/rpg_box.webp.import new file mode 100644 index 0000000000000000000000000000000000000000..3f5f23942e30e6d02f21657e4a76fd06b352e839 --- /dev/null +++ b/addons/dialogic/Example Assets/backgrounds/rpg_box.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dch8fuekijffp" +path="res://.godot/imported/rpg_box.webp-6ea0804b52e01599dbc94ffacc31d433.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/backgrounds/rpg_box.webp" +dest_files=["res://.godot/imported/rpg_box.webp-6ea0804b52e01599dbc94ffacc31d433.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/default_event.gd b/addons/dialogic/Example Assets/default_event.gd new file mode 100644 index 0000000000000000000000000000000000000000..8bbebc3ca3c41d7528d4f00dead58ad56dedab64 --- /dev/null +++ b/addons/dialogic/Example Assets/default_event.gd @@ -0,0 +1,48 @@ +@tool +extends DialogicEvent + + +# DEFINE ALL PROPERTIES OF THE EVENT +# var MySetting :String = "" + +func _execute() -> void: + # I have no idea how this event works + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +# SET ALL VALUES THAT SHOULD NEVER CHANGE HERE +func _init() -> void: + event_name = "Default" + event_color = Color("#ffffff") + event_category = "Main" + event_sorting_index = 0 + + + +################################################################################ +## SAVING/LOADING +################################################################################ +func get_shortcode() -> String: + return "default_shortcode" + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_name + #"arg_name" : "NameOfProperty", + } + +# You can alternatively overwrite these 3 functions: +# - to_text(), +# - from_text(), +# - is_valid_event() + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor() -> void: + pass diff --git a/addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png b/addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f26ed34293eca439250bbefdbcaf84bec370c10d Binary files /dev/null and b/addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png differ diff --git a/addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png.import b/addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png.import new file mode 100644 index 0000000000000000000000000000000000000000..90ac6460a347a64223fa43f0ef65d5664e405553 --- /dev/null +++ b/addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bgn2ci6nu85t5" +path="res://.godot/imported/next-indicator-dialogic-1.png-694f122eff55e969b54cc43e62eb4758.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/next-indicator/next-indicator-dialogic-1.png" +dest_files=["res://.godot/imported/next-indicator-dialogic-1.png-694f122eff55e969b54cc43e62eb4758.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/next-indicator/next-indicator.png b/addons/dialogic/Example Assets/next-indicator/next-indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..896d3cfc0fc6426518645409ff223c15b9f67bc4 Binary files /dev/null and b/addons/dialogic/Example Assets/next-indicator/next-indicator.png differ diff --git a/addons/dialogic/Example Assets/next-indicator/next-indicator.png.import b/addons/dialogic/Example Assets/next-indicator/next-indicator.png.import new file mode 100644 index 0000000000000000000000000000000000000000..fa458c5811aedad809d31273d82385327d6a2097 --- /dev/null +++ b/addons/dialogic/Example Assets/next-indicator/next-indicator.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://u0o8qgccs5df" +path="res://.godot/imported/next-indicator.png-e3b7b80d9da791a1d0a061a728b6f781.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/next-indicator/next-indicator.png" +dest_files=["res://.godot/imported/next-indicator.png-e3b7b80d9da791a1d0a061a728b6f781.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png new file mode 100644 index 0000000000000000000000000000000000000000..167c215be3032853b84e66f529e1daea19b81e5c Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png.import new file mode 100644 index 0000000000000000000000000000000000000000..4056cb273d076fe284120d5fbc9c4a264f2b6772 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://s2jsr1aqiu84" +path="res://.godot/imported/pl5 blink.png-dd40283850366d49ae61df7b137ffd77.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png" +dest_files=["res://.godot/imported/pl5 blink.png-dd40283850366d49ae61df7b137ffd77.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png new file mode 100644 index 0000000000000000000000000000000000000000..75d7519fc1292c7794bc6016c4e77f4fd969e8b2 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png.import new file mode 100644 index 0000000000000000000000000000000000000000..bfd6ca9182570e95f266e81563b07c3bfd614407 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bl38l2bv5ny4h" +path="res://.godot/imported/pl5 doubt.png-c657bfaf88fd5c06956ec703146704c8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 doubt.png" +dest_files=["res://.godot/imported/pl5 doubt.png-c657bfaf88fd5c06956ec703146704c8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png new file mode 100644 index 0000000000000000000000000000000000000000..a4eb434b9ce317f1f62d5f940c96d643b79ecb79 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png.import new file mode 100644 index 0000000000000000000000000000000000000000..4ac4f9ef22af00be4e4fd7cabcfc4496053aeed8 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ch4hvrgmq3j7t" +path="res://.godot/imported/pl5 hate.png-004951da12b71d275d61f3fe7af6c760.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 hate.png" +dest_files=["res://.godot/imported/pl5 hate.png-004951da12b71d275d61f3fe7af6c760.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png new file mode 100644 index 0000000000000000000000000000000000000000..4763b8236b106af13bd0f248d26645b4f124c4b4 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png.import new file mode 100644 index 0000000000000000000000000000000000000000..cb22c007379c7d545aaf1ff0877bd7afd058fa11 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1k1be4cqjj4t" +path="res://.godot/imported/pl5 plot.png-7c5bbb51327eb4b7b1b78f4597ed6c60.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 plot.png" +dest_files=["res://.godot/imported/pl5 plot.png-7c5bbb51327eb4b7b1b78f4597ed6c60.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png new file mode 100644 index 0000000000000000000000000000000000000000..97845b26de3074550b80633f0b2122b339043237 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png.import new file mode 100644 index 0000000000000000000000000000000000000000..8770dc68867fdd408dd4c546487d0b78bf2674a5 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://x7yafneltdfy" +path="res://.godot/imported/pl5 sad.png-778e9490c4f77059d6c87f720b2bbff7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 sad.png" +dest_files=["res://.godot/imported/pl5 sad.png-778e9490c4f77059d6c87f720b2bbff7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png new file mode 100644 index 0000000000000000000000000000000000000000..d54b591a2e6896dc78ba1323f3988cd964f9c744 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png.import new file mode 100644 index 0000000000000000000000000000000000000000..730a905adf4e64e79a8f3f8300c72f233beb0791 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://675isgfym2tw" +path="res://.godot/imported/pl5 scoff.png-f0b3e5d0a8895f55d2377978a0992a32.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 scoff.png" +dest_files=["res://.godot/imported/pl5 scoff.png-f0b3e5d0a8895f55d2377978a0992a32.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png new file mode 100644 index 0000000000000000000000000000000000000000..a131cfaf0511bc6c4eeb07be39a9d93c2d0e8c44 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png.import new file mode 100644 index 0000000000000000000000000000000000000000..dd695934e448dcb7a4528baa2016375d775fd7f6 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gaol7fi1ifkx" +path="res://.godot/imported/pl5 shy.png-db66f14e608e1c150163af82c8a9f341.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 shy.png" +dest_files=["res://.godot/imported/pl5 shy.png-db66f14e608e1c150163af82c8a9f341.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png new file mode 100644 index 0000000000000000000000000000000000000000..c780781c7a2d21db38e434eca6b6e10869310723 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png.import new file mode 100644 index 0000000000000000000000000000000000000000..273fa0061bfeff2f023f9d954e3d121fe932159b --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dpvtdr1itkbd7" +path="res://.godot/imported/pl5 surprise.png-9f07d67f3c68589bb2cfec738d68b9a7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 surprise.png" +dest_files=["res://.godot/imported/pl5 surprise.png-9f07d67f3c68589bb2cfec738d68b9a7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5.png b/addons/dialogic/Example Assets/portraits/Antonio/pl5.png new file mode 100644 index 0000000000000000000000000000000000000000..f676f779799e9a9e556416b5a3e9496700f14c6d Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Antonio/pl5.png differ diff --git a/addons/dialogic/Example Assets/portraits/Antonio/pl5.png.import b/addons/dialogic/Example Assets/portraits/Antonio/pl5.png.import new file mode 100644 index 0000000000000000000000000000000000000000..3dc08d176f3c1e746b2e398a65b9a843ce57fcbd --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Antonio/pl5.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bfkpn7mrd786b" +path="res://.godot/imported/pl5.png-0e78d740b51df476d423c20a3850d39a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Antonio/pl5.png" +dest_files=["res://.godot/imported/pl5.png-0e78d740b51df476d423c20a3850d39a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd new file mode 100644 index 0000000000000000000000000000000000000000..c6c0e8a6e7e73b373154d317bd1dcbe9db7a0c10 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd @@ -0,0 +1,18 @@ +@tool +extends Node2D + +# If the custom portrait accepts a change, then accept it here +func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: + if passed_portrait == "": + passed_portrait = passed_character['default_portrait'] + + if $Sprite.sprite_frames.has_animation(passed_portrait): + $Sprite.play(passed_portrait) + +func _on_animated_sprite_2d_animation_finished(): + $Sprite.frame = randi()%$Sprite.sprite_frames.get_frame_count($Sprite.animation) + $Sprite.play() + + +func _get_covered_rect() -> Rect2: + return Rect2($Sprite.position, $Sprite.sprite_frames.get_frame_texture($Sprite.animation, 0).get_size()*$Sprite.scale) diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.tscn b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.tscn new file mode 100644 index 0000000000000000000000000000000000000000..bdad81b2e64806c5537c9b71bd43f00ea3757f2f --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=5 format=3 uid="uid://cyns86lydp1tl"] + +[ext_resource type="Script" path="res://addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd" id="1_63c5k"] +[ext_resource type="Texture2D" uid="uid://bfkpn7mrd786b" path="res://addons/dialogic/Example Assets/portraits/Antonio/pl5.png" id="2_15o4t"] +[ext_resource type="Texture2D" uid="uid://s2jsr1aqiu84" path="res://addons/dialogic/Example Assets/portraits/Antonio/pl5 blink.png" id="3_qen6e"] + +[sub_resource type="SpriteFrames" id="SpriteFrames_yaycq"] +animations = [{ +"frames": [{ +"duration": 10.0, +"texture": ExtResource("2_15o4t") +}, { +"duration": 1.0, +"texture": ExtResource("3_qen6e") +}, { +"duration": 5.0, +"texture": ExtResource("2_15o4t") +}, { +"duration": 4.0, +"texture": ExtResource("2_15o4t") +}, { +"duration": 1.0, +"texture": ExtResource("3_qen6e") +}, { +"duration": 1.0, +"texture": ExtResource("2_15o4t") +}, { +"duration": 1.0, +"texture": ExtResource("3_qen6e") +}, { +"duration": 5.0, +"texture": ExtResource("2_15o4t") +}, { +"duration": 1.0, +"texture": ExtResource("3_qen6e") +}, { +"duration": 10.0, +"texture": ExtResource("2_15o4t") +}], +"loop": false, +"name": &"default", +"speed": 10.0 +}] + +[node name="CustomCharacterScene" type="Node2D"] +position = Vector2(160, 580) +script = ExtResource("1_63c5k") + +[node name="Sprite" type="AnimatedSprite2D" parent="."] +position = Vector2(-161, -580) +scale = Vector2(0.751953, 0.751953) +sprite_frames = SubResource("SpriteFrames_yaycq") +autoplay = "default" +centered = false + +[connection signal="animation_finished" from="Sprite" to="." method="_on_animated_sprite_2d_animation_finished"] diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd new file mode 100644 index 0000000000000000000000000000000000000000..2d6f7c288966930de3d76d9a33dfcad51e47387b --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd @@ -0,0 +1,56 @@ +@tool +extends Node2D + +enum Faces {BASED_ON_PORTRAIT_NAME, NEUTRAL, HAPPY, SAD, JOY, SHOCK, ANGRY} + +var portrait + +@export var emotion : Faces = Faces.BASED_ON_PORTRAIT_NAME +@export var portrait_width: int +@export var portrait_height: int +@export var alien = true + +var does_custom_portrait_change = true + +func _ready(): + $Alien.hide() + +# Function to accept and use the extra data, if the custom portrait wants to accept it +func _set_extra_data(data: String) -> void: + if data == "alien": + $Alien.show() + elif data == "no_alien": + $Alien.hide() + +# This function can be overridden. Defaults to true, if not overridden! +func _should_do_portrait_update(character:DialogicCharacter, portrait:String) -> bool: + return true + +# If the custom portrait accepts a change, then accept it here +func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: + for face in $Faces.get_children(): + face.hide() + if emotion == Faces.BASED_ON_PORTRAIT_NAME: + if 'happy' in passed_portrait.to_lower(): $Faces/Smile.show() + elif 'sad' in passed_portrait.to_lower(): $Faces/Frown.show() + elif 'joy' in passed_portrait.to_lower(): $Faces/Joy.show() + elif 'shock' in passed_portrait.to_lower(): $Faces/Shock.show() + elif 'angry' in passed_portrait.to_lower(): $Faces/Anger.show() + else: $Faces/Neutral.show() + else: + if emotion == Faces.HAPPY: $Faces/Smile.show() + elif emotion == Faces.SAD: $Faces/Frown.show() + elif emotion == Faces.JOY: $Faces/Joy.show() + elif emotion == Faces.SHOCK: $Faces/Shock.show() + elif emotion == Faces.ANGRY: $Faces/Anger.show() + else: $Faces/Neutral.show() + + $Alien.visible = alien + +func _set_mirror(mirror:bool) -> void: + if mirror: scale.x *= -1 + +# if implemented, this is used by the editor for the "full view" mode +func _get_covered_rect() -> Rect2: + #return Rect2($Faces/Anger.position+$Faces.position, $Faces/Anger.get_rect().size*$Faces/Anger.scale*$Faces.scale) # will fcus on the face + return Rect2($Body.position, $Body.get_rect().size*$Body.scale) diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn new file mode 100644 index 0000000000000000000000000000000000000000..5c22306f2b22269839d2fa660ebf0e96bd9ddf4f --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn @@ -0,0 +1,67 @@ +[gd_scene load_steps=10 format=3 uid="uid://bgshjju5v2q0i"] + +[ext_resource type="Script" path="res://addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd" id="1_fc12l"] +[ext_resource type="Texture2D" uid="uid://djqit26f4be4f" path="res://addons/dialogic/Example Assets/portraits/Princess/princess_blank.png" id="2_igcyp"] +[ext_resource type="Texture2D" uid="uid://ndmjrpk41eo4" path="res://addons/dialogic/Example Assets/portraits/Portrait1.png" id="3_6xy1t"] +[ext_resource type="Texture2D" uid="uid://dokv225cp85ja" path="res://addons/dialogic/Example Assets/portraits/Princess/anger.png" id="3_wdpjk"] +[ext_resource type="Texture2D" uid="uid://5bruuhj5cqu4" path="res://addons/dialogic/Example Assets/portraits/Princess/frown.png" id="4_pimb3"] +[ext_resource type="Texture2D" uid="uid://dg7c4umbfsyvs" path="res://addons/dialogic/Example Assets/portraits/Princess/joy.png" id="5_2ekfy"] +[ext_resource type="Texture2D" uid="uid://bu3631ymfqxi3" path="res://addons/dialogic/Example Assets/portraits/Princess/neutral.png" id="6_5hpoa"] +[ext_resource type="Texture2D" uid="uid://c5aku2g01k6c6" path="res://addons/dialogic/Example Assets/portraits/Princess/shock.png" id="7_5xil3"] +[ext_resource type="Texture2D" uid="uid://dsid4ye0q74nl" path="res://addons/dialogic/Example Assets/portraits/Princess/smile.png" id="8_7s6tq"] + +[node name="CustomPortraitFaceAtlass" type="Node2D"] +position = Vector2(301, 598) +script = ExtResource("1_fc12l") + +[node name="Body" type="Sprite2D" parent="."] +position = Vector2(-182, -465) +scale = Vector2(0.287561, 0.287561) +texture = ExtResource("2_igcyp") +centered = false + +[node name="Alien" type="Sprite2D" parent="."] +visible = false +position = Vector2(-58, -378) +rotation = -0.523598 +scale = Vector2(0.84236, 0.875348) +texture = ExtResource("3_6xy1t") + +[node name="Faces" type="Node2D" parent="."] +position = Vector2(2, -397) + +[node name="Anger" type="Sprite2D" parent="Faces"] +position = Vector2(-38, -41) +scale = Vector2(0.290393, 0.288066) +texture = ExtResource("3_wdpjk") +centered = false + +[node name="Frown" type="Sprite2D" parent="Faces"] +position = Vector2(-38, -41) +scale = Vector2(0.290393, 0.288066) +texture = ExtResource("4_pimb3") +centered = false + +[node name="Joy" type="Sprite2D" parent="Faces"] +position = Vector2(-38, -41) +scale = Vector2(0.290393, 0.288066) +texture = ExtResource("5_2ekfy") +centered = false + +[node name="Neutral" type="Sprite2D" parent="Faces"] +position = Vector2(-38, -41) +scale = Vector2(0.290393, 0.288066) +texture = ExtResource("6_5hpoa") +centered = false + +[node name="Shock" type="Sprite2D" parent="Faces"] +position = Vector2(-38, -41) +scale = Vector2(0.290393, 0.288066) +texture = ExtResource("7_5xil3") +centered = false + +[node name="Smile" type="Sprite2D" parent="Faces"] +position = Vector2(-38, -41) +scale = Vector2(0.290393, 0.288066) +texture = ExtResource("8_7s6tq") +centered = false diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png new file mode 100644 index 0000000000000000000000000000000000000000..258af79487c4edbb32d5c333471a56fd2afc48fb Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png.import new file mode 100644 index 0000000000000000000000000000000000000000..5278433ddca84dca67f5fcd34823023dcfb71182 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cb3tpn3u3wtis" +path="res://.godot/imported/pl3 avoid.png-f8f5fd2a91f270ef9417e3b4bda0f35d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 avoid.png" +dest_files=["res://.godot/imported/pl3 avoid.png-f8f5fd2a91f270ef9417e3b4bda0f35d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png new file mode 100644 index 0000000000000000000000000000000000000000..0ccfc285db53a7877ba25d2ca91faf61aa6db218 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png.import new file mode 100644 index 0000000000000000000000000000000000000000..b70a67aace25baf5de6758c0d1724a6d073bbf27 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://khlo6wd16qcd" +path="res://.godot/imported/pl3 blink.png-bc002e72d459c371c5ebb5d3d237500e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 blink.png" +dest_files=["res://.godot/imported/pl3 blink.png-bc002e72d459c371c5ebb5d3d237500e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png new file mode 100644 index 0000000000000000000000000000000000000000..a14954b38349e70591b3fc620d146dbecea542c0 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png.import new file mode 100644 index 0000000000000000000000000000000000000000..9236dd7c9af7953ce32e774340f3db741cb5f247 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cmshygun2cd0j" +path="res://.godot/imported/pl3 concept.png-baa2419b24f73cd7e47554567e865964.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 concept.png" +dest_files=["res://.godot/imported/pl3 concept.png-baa2419b24f73cd7e47554567e865964.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png new file mode 100644 index 0000000000000000000000000000000000000000..7463f0bbe136ad2ac484d901de117e034da26e29 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png.import new file mode 100644 index 0000000000000000000000000000000000000000..98bbb6ef8573df38ef8f8d118ad177eb427a1447 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://r23r6iywkw0n" +path="res://.godot/imported/pl3 confusion.png-447505e4db69107e418a56eb99214d80.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 confusion.png" +dest_files=["res://.godot/imported/pl3 confusion.png-447505e4db69107e418a56eb99214d80.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png new file mode 100644 index 0000000000000000000000000000000000000000..d34166f222657e96a74318d4ab1640e9c2d1e449 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png.import new file mode 100644 index 0000000000000000000000000000000000000000..fa7850c1e6e9af0484e8fcb0eee522e7ce8a02e0 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d0xc8omj2n6h2" +path="res://.godot/imported/pl3 doubt.png-ad639761c380e37b578d46414772df73.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 doubt.png" +dest_files=["res://.godot/imported/pl3 doubt.png-ad639761c380e37b578d46414772df73.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png new file mode 100644 index 0000000000000000000000000000000000000000..221ab1869ada7330c2d5d07cce64a516a25e7d3b Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png.import new file mode 100644 index 0000000000000000000000000000000000000000..6959b105049cf25ff44c503a872476785f999c7b --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dgteot3g2xw78" +path="res://.godot/imported/pl3 happy.png-7a49313244ae7097b2150a258b29afbc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 happy.png" +dest_files=["res://.godot/imported/pl3 happy.png-7a49313244ae7097b2150a258b29afbc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png new file mode 100644 index 0000000000000000000000000000000000000000..f11cbe41aaa30af470de9803e7eb1f33b80dfc49 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png.import new file mode 100644 index 0000000000000000000000000000000000000000..f847ad40f63655d6c5ab5b3394d266219e21d396 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://uqa1ygtex8sj" +path="res://.godot/imported/pl3 plot.png-92e4eb96f6aac50f2afc301dcb1954fb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 plot.png" +dest_files=["res://.godot/imported/pl3 plot.png-92e4eb96f6aac50f2afc301dcb1954fb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png new file mode 100644 index 0000000000000000000000000000000000000000..d4cae6c2ec2533f96e80409a2ecf482b433d70a2 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png.import new file mode 100644 index 0000000000000000000000000000000000000000..e383481ea9b1efa076d355b561cbc017c1e2e419 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://be07it6s721a0" +path="res://.godot/imported/pl3 sad.png-4ce7b7a2f701590bc444115bab6e0c4e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 sad.png" +dest_files=["res://.godot/imported/pl3 sad.png-4ce7b7a2f701590bc444115bab6e0c4e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png new file mode 100644 index 0000000000000000000000000000000000000000..fa21ed2c7417ab5e0e6928781fe8c3c085f738ec Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png.import new file mode 100644 index 0000000000000000000000000000000000000000..cf74c11f0e36e52962737610e0ebc753e0ae8443 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bx6037ennf2im" +path="res://.godot/imported/pl3 shy.png-7dc343f1ee98343c9fe2c9cf93a8d574.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 shy.png" +dest_files=["res://.godot/imported/pl3 shy.png-7dc343f1ee98343c9fe2c9cf93a8d574.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png b/addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png new file mode 100644 index 0000000000000000000000000000000000000000..ab66ecb25aaadda7247574f3ecb785a021f9efea Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png differ diff --git a/addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png.import b/addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png.import new file mode 100644 index 0000000000000000000000000000000000000000..5c2d3719e029b81f2a0ab2018a2d1b862846805b --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c8ctomgkvxnta" +path="res://.godot/imported/pl3 surprise.png-92cfd8f7846a35eec2d62e62d532d7e6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Jane/pl3 surprise.png" +dest_files=["res://.godot/imported/pl3 surprise.png-92cfd8f7846a35eec2d62e62d532d7e6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Portrait1.png b/addons/dialogic/Example Assets/portraits/Portrait1.png new file mode 100644 index 0000000000000000000000000000000000000000..4bac6af722a6a96a340df019541f8e9380068909 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Portrait1.png differ diff --git a/addons/dialogic/Example Assets/portraits/Portrait1.png.import b/addons/dialogic/Example Assets/portraits/Portrait1.png.import new file mode 100644 index 0000000000000000000000000000000000000000..25984338b6c099680e8937c689263d6130147243 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Portrait1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ndmjrpk41eo4" +path="res://.godot/imported/Portrait1.png-c609e542fb60d6627e07ca0a12ddd868.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Portrait1.png" +dest_files=["res://.godot/imported/Portrait1.png-c609e542fb60d6627e07ca0a12ddd868.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Portrait2.png b/addons/dialogic/Example Assets/portraits/Portrait2.png new file mode 100644 index 0000000000000000000000000000000000000000..9bc4398b62d3c612d6443bcdd5c77483575328d8 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Portrait2.png differ diff --git a/addons/dialogic/Example Assets/portraits/Portrait2.png.import b/addons/dialogic/Example Assets/portraits/Portrait2.png.import new file mode 100644 index 0000000000000000000000000000000000000000..205d8f0c275c423a02073e929f072808614e97d1 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Portrait2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2u3n3yp222uh" +path="res://.godot/imported/Portrait2.png-c9d044982430f12029c2193cba14c11f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Portrait2.png" +dest_files=["res://.godot/imported/Portrait2.png-c9d044982430f12029c2193cba14c11f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/anger.png b/addons/dialogic/Example Assets/portraits/Princess/anger.png new file mode 100644 index 0000000000000000000000000000000000000000..e63cfac6f19969cddd1589c3cc1bb2ccb6e3b425 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/anger.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/anger.png.import b/addons/dialogic/Example Assets/portraits/Princess/anger.png.import new file mode 100644 index 0000000000000000000000000000000000000000..17c4eafc1a49a654166239a073662d1138c6f0bb --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/anger.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dokv225cp85ja" +path="res://.godot/imported/anger.png-dbcae35ced97cd8763301ede55f7634a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/anger.png" +dest_files=["res://.godot/imported/anger.png-dbcae35ced97cd8763301ede55f7634a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/frown.png b/addons/dialogic/Example Assets/portraits/Princess/frown.png new file mode 100644 index 0000000000000000000000000000000000000000..33b45f5337e4249a31b25cb330432e8ee83dcfe7 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/frown.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/frown.png.import b/addons/dialogic/Example Assets/portraits/Princess/frown.png.import new file mode 100644 index 0000000000000000000000000000000000000000..d7534522c8a6903ee2b175da4a8a3fdd4908f9a5 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/frown.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://5bruuhj5cqu4" +path="res://.godot/imported/frown.png-2ea012492bc6286b36736d621adfd96a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/frown.png" +dest_files=["res://.godot/imported/frown.png-2ea012492bc6286b36736d621adfd96a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/joy.png b/addons/dialogic/Example Assets/portraits/Princess/joy.png new file mode 100644 index 0000000000000000000000000000000000000000..8425066ef0579cc09d904e5b640e8e57d180e0ea Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/joy.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/joy.png.import b/addons/dialogic/Example Assets/portraits/Princess/joy.png.import new file mode 100644 index 0000000000000000000000000000000000000000..2f45c81c5125c727c86c99acc9a96b98b2828be1 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/joy.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dg7c4umbfsyvs" +path="res://.godot/imported/joy.png-a06db3f0763984942582106f69acd2ac.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/joy.png" +dest_files=["res://.godot/imported/joy.png-a06db3f0763984942582106f69acd2ac.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/neutral.png b/addons/dialogic/Example Assets/portraits/Princess/neutral.png new file mode 100644 index 0000000000000000000000000000000000000000..cd1441adaef0933eb72e8bdd4ae45d3d76da430c Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/neutral.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/neutral.png.import b/addons/dialogic/Example Assets/portraits/Princess/neutral.png.import new file mode 100644 index 0000000000000000000000000000000000000000..0c16fb44638ed3d6758cd30e694ac4f28028de12 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/neutral.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bu3631ymfqxi3" +path="res://.godot/imported/neutral.png-b67f36561d5798f6bdf0e487c71053f7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/neutral.png" +dest_files=["res://.godot/imported/neutral.png-b67f36561d5798f6bdf0e487c71053f7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/princess_blank.png b/addons/dialogic/Example Assets/portraits/Princess/princess_blank.png new file mode 100644 index 0000000000000000000000000000000000000000..4b408879ccff772ceec4b4508885bd715e03f42c Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/princess_blank.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/princess_blank.png.import b/addons/dialogic/Example Assets/portraits/Princess/princess_blank.png.import new file mode 100644 index 0000000000000000000000000000000000000000..e2be550cc3699fedf312332abc68067b225c50f3 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/princess_blank.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djqit26f4be4f" +path="res://.godot/imported/princess_blank.png-fb2f5b52f38dc68c3bb1b4bf7bd4d155.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/princess_blank.png" +dest_files=["res://.godot/imported/princess_blank.png-fb2f5b52f38dc68c3bb1b4bf7bd4d155.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/shock.png b/addons/dialogic/Example Assets/portraits/Princess/shock.png new file mode 100644 index 0000000000000000000000000000000000000000..2e7f3069a3eba9001060d03ba07b5607ad00b23e Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/shock.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/shock.png.import b/addons/dialogic/Example Assets/portraits/Princess/shock.png.import new file mode 100644 index 0000000000000000000000000000000000000000..5a561b8ba1f41e10208dfb12f5962ed7044a8d2e --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/shock.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5aku2g01k6c6" +path="res://.godot/imported/shock.png-8c83d26226ef9a4e882afabd3875355f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/shock.png" +dest_files=["res://.godot/imported/shock.png-8c83d26226ef9a4e882afabd3875355f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/Princess/smile.png b/addons/dialogic/Example Assets/portraits/Princess/smile.png new file mode 100644 index 0000000000000000000000000000000000000000..45247e474b77043e855ef5504ce51e5c2f74de72 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/Princess/smile.png differ diff --git a/addons/dialogic/Example Assets/portraits/Princess/smile.png.import b/addons/dialogic/Example Assets/portraits/Princess/smile.png.import new file mode 100644 index 0000000000000000000000000000000000000000..b99f2ddd44c2cff24f9bb95a8c314043d1572904 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/Princess/smile.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dsid4ye0q74nl" +path="res://.godot/imported/smile.png-763f387a68c52e40326ccdf00129e290.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/Princess/smile.png" +dest_files=["res://.godot/imported/smile.png-763f387a68c52e40326ccdf00129e290.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png b/addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png new file mode 100644 index 0000000000000000000000000000000000000000..f77329f3eb83713fdfd3366f4acfb1f50a33dc69 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png differ diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png.import b/addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png.import new file mode 100644 index 0000000000000000000000000000000000000000..6570c35bb1aa25cf110dd2fd10a0893dba609960 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1es1ixchfied" +path="res://.godot/imported/base1.png-d5d7d1c85b1cab665dc0b54194bbd33b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/rpg_portraits/base1.png" +dest_files=["res://.godot/imported/base1.png-d5d7d1c85b1cab665dc0b54194bbd33b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png b/addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png new file mode 100644 index 0000000000000000000000000000000000000000..015798fcafadb8183bd8611faa190d0dcef510a4 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png differ diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png.import b/addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png.import new file mode 100644 index 0000000000000000000000000000000000000000..0d2af4239f8cd0b60e447423625be4e37d8e5214 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0asqyjv6ea0h" +path="res://.godot/imported/base2.png-4cf3c53a4d499097fe6532e4b778d0b3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/rpg_portraits/base2.png" +dest_files=["res://.godot/imported/base2.png-4cf3c53a4d499097fe6532e4b778d0b3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png b/addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png new file mode 100644 index 0000000000000000000000000000000000000000..e7feaa9f53d882cf00fd5c3c1e1b66baa82e8f66 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png differ diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png.import b/addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png.import new file mode 100644 index 0000000000000000000000000000000000000000..e867c507315c1594b7cc7b15d2a480cd0e851cb6 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vtajb1cdcso" +path="res://.godot/imported/base3.png-65e60d03716a9b546a46a38772fc2ace.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/rpg_portraits/base3.png" +dest_files=["res://.godot/imported/base3.png-65e60d03716a9b546a46a38772fc2ace.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png b/addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png new file mode 100644 index 0000000000000000000000000000000000000000..035d6b4152c0913455f499d69a786a25434e1a14 Binary files /dev/null and b/addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png differ diff --git a/addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png.import b/addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png.import new file mode 100644 index 0000000000000000000000000000000000000000..a62c6620ddd23c1f5cf3ad2e281bfb3099fb2fc1 --- /dev/null +++ b/addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcm3eo5syiln0" +path="res://.godot/imported/base4.png-ea3084656f4403d3ff87bdd890f73843.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Example Assets/portraits/rpg_portraits/base4.png" +dest_files=["res://.godot/imported/base4.png-ea3084656f4403d3ff87bdd890f73843.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Example Assets/sound-effects/LICENSE.txt b/addons/dialogic/Example Assets/sound-effects/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..14b8ff5a27d0601d92d85cc0fe3514ef5498f61e --- /dev/null +++ b/addons/dialogic/Example Assets/sound-effects/LICENSE.txt @@ -0,0 +1,4 @@ +Copyright (c) 2020 Tim Krief. + +Typing sound effects by Tim Krief are licensed under a Creative +Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) License. diff --git a/addons/dialogic/Example Assets/sound-effects/typing1.wav b/addons/dialogic/Example Assets/sound-effects/typing1.wav new file mode 100644 index 0000000000000000000000000000000000000000..bcb9c87f7c4dc297904cf503bf17dbfb56171449 Binary files /dev/null and b/addons/dialogic/Example Assets/sound-effects/typing1.wav differ diff --git a/addons/dialogic/Example Assets/sound-effects/typing1.wav.import b/addons/dialogic/Example Assets/sound-effects/typing1.wav.import new file mode 100644 index 0000000000000000000000000000000000000000..df5f0531f49aeeba0c47e4def2c1601e663b43c0 --- /dev/null +++ b/addons/dialogic/Example Assets/sound-effects/typing1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b6c1p14bc20p1" +path="res://.godot/imported/typing1.wav-b241c6aab4ce82bf04caf8687873cae0.sample" + +[deps] + +source_file="res://addons/dialogic/Example Assets/sound-effects/typing1.wav" +dest_files=["res://.godot/imported/typing1.wav-b241c6aab4ce82bf04caf8687873cae0.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/dialogic/Example Assets/sound-effects/typing2.wav b/addons/dialogic/Example Assets/sound-effects/typing2.wav new file mode 100644 index 0000000000000000000000000000000000000000..aff48fab775e1ccd46bf3921cd802cb111ff5f2e Binary files /dev/null and b/addons/dialogic/Example Assets/sound-effects/typing2.wav differ diff --git a/addons/dialogic/Example Assets/sound-effects/typing2.wav.import b/addons/dialogic/Example Assets/sound-effects/typing2.wav.import new file mode 100644 index 0000000000000000000000000000000000000000..7286fad15f049c068c0fff8da5621abefff26f2f --- /dev/null +++ b/addons/dialogic/Example Assets/sound-effects/typing2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c3uw4nft0de13" +path="res://.godot/imported/typing2.wav-64cf50045b34db7d5ef5da984070e0a7.sample" + +[deps] + +source_file="res://addons/dialogic/Example Assets/sound-effects/typing2.wav" +dest_files=["res://.godot/imported/typing2.wav-64cf50045b34db7d5ef5da984070e0a7.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/dialogic/Example Assets/sound-effects/typing3.wav b/addons/dialogic/Example Assets/sound-effects/typing3.wav new file mode 100644 index 0000000000000000000000000000000000000000..91f353b2c8abf2e05dd59a06b5d3da1ef78a5e70 Binary files /dev/null and b/addons/dialogic/Example Assets/sound-effects/typing3.wav differ diff --git a/addons/dialogic/Example Assets/sound-effects/typing3.wav.import b/addons/dialogic/Example Assets/sound-effects/typing3.wav.import new file mode 100644 index 0000000000000000000000000000000000000000..de1c70db8cb3eb1b8967dad42468ec7c38548100 --- /dev/null +++ b/addons/dialogic/Example Assets/sound-effects/typing3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dnboblpkf0fqi" +path="res://.godot/imported/typing3.wav-083712282583242958aaa68128694f95.sample" + +[deps] + +source_file="res://addons/dialogic/Example Assets/sound-effects/typing3.wav" +dest_files=["res://.godot/imported/typing3.wav-083712282583242958aaa68128694f95.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/dialogic/Example Assets/sound-effects/typing4.wav b/addons/dialogic/Example Assets/sound-effects/typing4.wav new file mode 100644 index 0000000000000000000000000000000000000000..071ba816540e1cb9c1e64ba7f5329d4768175eb3 Binary files /dev/null and b/addons/dialogic/Example Assets/sound-effects/typing4.wav differ diff --git a/addons/dialogic/Example Assets/sound-effects/typing4.wav.import b/addons/dialogic/Example Assets/sound-effects/typing4.wav.import new file mode 100644 index 0000000000000000000000000000000000000000..23800d30c6e754025120eaa40dc9a3931084f2d4 --- /dev/null +++ b/addons/dialogic/Example Assets/sound-effects/typing4.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://c2viukvbub6v6" +path="res://.godot/imported/typing4.wav-4e7a00fb19b7dd0bdfd8e323401b6162.sample" + +[deps] + +source_file="res://addons/dialogic/Example Assets/sound-effects/typing4.wav" +dest_files=["res://.godot/imported/typing4.wav-4e7a00fb19b7dd0bdfd8e323401b6162.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/dialogic/Example Assets/sound-effects/typing5.wav b/addons/dialogic/Example Assets/sound-effects/typing5.wav new file mode 100644 index 0000000000000000000000000000000000000000..9143081af8435590ebe20e6f063c0f6395e2091e Binary files /dev/null and b/addons/dialogic/Example Assets/sound-effects/typing5.wav differ diff --git a/addons/dialogic/Example Assets/sound-effects/typing5.wav.import b/addons/dialogic/Example Assets/sound-effects/typing5.wav.import new file mode 100644 index 0000000000000000000000000000000000000000..4b17d98c2e6f63ebb1149d87795b2352f3f122b1 --- /dev/null +++ b/addons/dialogic/Example Assets/sound-effects/typing5.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dwcre3fjf3cj8" +path="res://.godot/imported/typing5.wav-5315fa96c84bd3957d157e8c978c1f04.sample" + +[deps] + +source_file="res://addons/dialogic/Example Assets/sound-effects/typing5.wav" +dest_files=["res://.godot/imported/typing5.wav-5315fa96c84bd3957d157e8c978c1f04.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/addons/dialogic/Modules/Audio/event_music.gd b/addons/dialogic/Modules/Audio/event_music.gd new file mode 100644 index 0000000000000000000000000000000000000000..95156f783fbb8a1f35d460fdb217e2841409f0aa --- /dev/null +++ b/addons/dialogic/Modules/Audio/event_music.gd @@ -0,0 +1,85 @@ +@tool +class_name DialogicMusicEvent +extends DialogicEvent + +## Event that can change the currently playing background music. + + +### Settings + +## The file to play. If empty, the previous music will be faded out. +var file_path: String = "" +## The length of the fade. If 0 (by default) it's an instant change. +var fade_length: float = 0 +## The volume the music will be played at. +var volume: float = 0 +## The audio bus the music will be played at. +var audio_bus: String = "Master" +## If true, the audio will loop, otherwise only play once. +var loop: bool = true + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + dialogic.Audio.update_music(file_path, volume, audio_bus, fade_length, loop) + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Music" + set_default_color('Color7') + event_category = "Audio" + event_sorting_index = 2 + expand_by_default = false + + +func _get_icon() -> Resource: + return load(self.get_script().get_path().get_base_dir().path_join('icon_music.png')) + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "music" + + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_info + "path" : {"property": "file_path", "default": ""}, + "fade" : {"property": "fade_length", "default": 0}, + "volume" : {"property": "volume", "default": 0}, + "bus" : {"property": "audio_bus", "default": "Master", + "suggestions": get_bus_suggestions}, + "loop" : {"property": "loop", "default": true}, + } + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('file_path', ValueType.FILE, 'Play', '', + {'file_filter' : "*.mp3, *.ogg, *.wav; Supported Audio Files", + 'placeholder' : "No music", + 'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]}) + add_body_edit('fade_length', ValueType.FLOAT, 'Fade Time:') + add_body_edit('volume', ValueType.DECIBEL, 'Volume:', '', {}, '!file_path.is_empty()') + add_body_edit('audio_bus', ValueType.SINGLELINE_TEXT, 'Audio Bus:', '', {}, '!file_path.is_empty()') + add_body_edit('loop', ValueType.BOOL, 'Loop:', '', {}, '!file_path.is_empty()') + + +func get_bus_suggestions() -> Dictionary: + var bus_name_list := {} + for i in range(AudioServer.bus_count): + bus_name_list[AudioServer.get_bus_name(i)] = {'value':AudioServer.get_bus_name(i)} + return bus_name_list diff --git a/addons/dialogic/Modules/Audio/event_sound.gd b/addons/dialogic/Modules/Audio/event_sound.gd new file mode 100644 index 0000000000000000000000000000000000000000..0375ef49029930383cbb860d366abccc02abd5f6 --- /dev/null +++ b/addons/dialogic/Modules/Audio/event_sound.gd @@ -0,0 +1,80 @@ +@tool +class_name DialogicSoundEvent +extends DialogicEvent + +## Event that allows to play a sound effect. Requires the Audio subsystem! + + +### Settings + +## The path to the file to play. +var file_path: String = "" +## The volume to play the sound at. +var volume: float = 0 +## The bus to play the sound on. +var audio_bus: String = "Master" +## If true, the sound will loop infinitely. Not recommended (as there is no way to stop it). +var loop: bool = false + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + dialogic.Audio.play_sound(file_path, volume, audio_bus, loop) + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Sound" + set_default_color('Color7') + event_category = "Audio" + event_sorting_index = 3 + expand_by_default = false + help_page_path = "https://dialogic.coppolaemilio.com" + + +func _get_icon() -> Resource: + return load(self.get_script().get_path().get_base_dir().path_join('icon_sound.png')) + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "sound" + + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_name + "path" : {"property": "file_path", "default": "",}, + "volume" : {"property": "volume", "default": 0}, + "bus" : {"property": "audio_bus", "default": "Master", + "suggestions": get_bus_suggestions}, + "loop" : {"property": "loop", "default": false}, + } + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('file_path', ValueType.FILE, 'Play', '', + {'file_filter' : '*.mp3, *.ogg, *.wav; Supported Audio Files', + 'placeholder' : "Select file", + 'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]}) + add_body_edit('volume', ValueType.DECIBEL, 'Volume:', '', {}, '!file_path.is_empty()') + add_body_edit('audio_bus', ValueType.SINGLELINE_TEXT, 'Audio Bus:', '', {}, '!file_path.is_empty()') + +func get_bus_suggestions() -> Dictionary: + var bus_name_list := {} + for i in range(AudioServer.bus_count): + bus_name_list[AudioServer.get_bus_name(i)] = {'value':AudioServer.get_bus_name(i)} + return bus_name_list diff --git a/addons/dialogic/Modules/Audio/icon_music.png b/addons/dialogic/Modules/Audio/icon_music.png new file mode 100644 index 0000000000000000000000000000000000000000..c94600f56dbb46444b3c4001c9ea2700a0c9b579 Binary files /dev/null and b/addons/dialogic/Modules/Audio/icon_music.png differ diff --git a/addons/dialogic/Modules/Audio/icon_music.png.import b/addons/dialogic/Modules/Audio/icon_music.png.import new file mode 100644 index 0000000000000000000000000000000000000000..093a0082317ea030dc33677d59b49cbd05c54a6d --- /dev/null +++ b/addons/dialogic/Modules/Audio/icon_music.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://buvpjsvdt4evk" +path="res://.godot/imported/icon_music.png-ffc971ba1265164a55f745186974be5f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Audio/icon_music.png" +dest_files=["res://.godot/imported/icon_music.png-ffc971ba1265164a55f745186974be5f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Audio/icon_sound.png b/addons/dialogic/Modules/Audio/icon_sound.png new file mode 100644 index 0000000000000000000000000000000000000000..c117397f8182c181217ae11c96bb5024f70c84be Binary files /dev/null and b/addons/dialogic/Modules/Audio/icon_sound.png differ diff --git a/addons/dialogic/Modules/Audio/icon_sound.png.import b/addons/dialogic/Modules/Audio/icon_sound.png.import new file mode 100644 index 0000000000000000000000000000000000000000..3e35ee660e7b7a6f369a2832b82c47a02f375ae4 --- /dev/null +++ b/addons/dialogic/Modules/Audio/icon_sound.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d3ookrkto0yh6" +path="res://.godot/imported/icon_sound.png-7a1a8a5533773d97969b6311b6a9133f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Audio/icon_sound.png" +dest_files=["res://.godot/imported/icon_sound.png-7a1a8a5533773d97969b6311b6a9133f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Audio/index.gd b/addons/dialogic/Modules/Audio/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..a75bc3d4c3c64fd7bc8d83e2e1bcb18e7f735224 --- /dev/null +++ b/addons/dialogic/Modules/Audio/index.gd @@ -0,0 +1,10 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_music.gd'), this_folder.path_join('event_sound.gd')] + + +func _get_subsystems() -> Array: + return [{'name':'Audio', 'script':this_folder.path_join('subsystem_audio.gd')}] diff --git a/addons/dialogic/Modules/Audio/subsystem_audio.gd b/addons/dialogic/Modules/Audio/subsystem_audio.gd new file mode 100644 index 0000000000000000000000000000000000000000..0c238cbb7975c3c7ba365fdd528cb422bad3bea4 --- /dev/null +++ b/addons/dialogic/Modules/Audio/subsystem_audio.gd @@ -0,0 +1,109 @@ +extends DialogicSubsystem + +## Subsystem that manages music and sounds. + +signal music_started(info:Dictionary) +signal sound_started(info:Dictionary) + +var base_music_player := AudioStreamPlayer.new() +var base_sound_player := AudioStreamPlayer.new() + +#################################################################################################### +## STATE +#################################################################################################### + +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR): + update_music() + stop_all_sounds() + +func load_game_state(): + var info = dialogic.current_state_info.get('music') + if info == null or info.path.is_empty(): + update_music() + else: + update_music(info.path, info.volume, info.audio_bus, 0, info.loop) + +func pause() -> void: + for child in get_children(): + child.stream_paused = true + +func resume() -> void: + for child in get_children(): + child.stream_paused = false + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +func _ready() -> void: + base_music_player.name = "Music" + add_child(base_music_player) + + base_sound_player.name = "Sound" + add_child(base_sound_player) + + +## Updates the background music. Will fade out previous music. +func update_music(path:String = '', volume:float = 0.0, audio_bus:String = "Master", fade_time:float = 0.0, loop:bool = true) -> void: + dialogic.current_state_info['music'] = {'path':path, 'volume':volume, 'audio_bus':audio_bus, 'loop':loop} + music_started.emit(dialogic.current_state_info['music']) + var fader: Tween = null + if base_music_player.playing or !path.is_empty(): + fader = create_tween() + var prev_node = null + if base_music_player.playing: + prev_node = base_music_player.duplicate() + add_child(prev_node) + prev_node.play(base_music_player.get_playback_position()) + prev_node.remove_from_group('dialogic_music_player') + fader.tween_method(interpolate_volume_linearly.bind(prev_node), db_to_linear(prev_node.volume_db),0.0,fade_time) + if path: + base_music_player.stream = load(path) + base_music_player.volume_db = volume + base_music_player.bus = audio_bus + if "loop" in base_music_player.stream: + base_music_player.stream.loop = loop + elif "loop_mode" in base_music_player.stream: + if loop: + base_music_player.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD + else: + base_music_player.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED + + base_music_player.play() + fader.parallel().tween_method(interpolate_volume_linearly.bind(base_music_player), 0.0,db_to_linear(volume),fade_time) + else: + base_music_player.stop() + if prev_node: + fader.tween_callback(prev_node.queue_free) + + +## Plays a given sound file. +func play_sound(path:String, volume:float = 0.0, audio_bus:String = "Master", loop :bool= false) -> void: + if base_sound_player != null and !path.is_empty(): + sound_started.emit({'path':path, 'volume':volume, 'audio_bus':audio_bus, 'loop':loop}) + var new_sound_node := base_sound_player.duplicate() + new_sound_node.name = "Sound" + new_sound_node.stream = load(path) + if "loop" in new_sound_node.stream: + new_sound_node.stream.loop = loop + elif "loop_mode" in new_sound_node.stream: + if loop: + new_sound_node.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD + else: + new_sound_node.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED + new_sound_node.volume_db = volume + new_sound_node.bus = audio_bus + add_child(new_sound_node) + new_sound_node.play() + new_sound_node.finished.connect(new_sound_node.queue_free) + + +func stop_all_sounds() -> void: + for node in get_children(): + if node == base_sound_player: + continue + if "Sound" in node.name: + node.queue_free() + +func interpolate_volume_linearly(value :float, node:Node) -> void: + node.volume_db = linear_to_db(value) diff --git a/addons/dialogic/Modules/Background/default_background.gd b/addons/dialogic/Modules/Background/default_background.gd new file mode 100644 index 0000000000000000000000000000000000000000..cb873b9e697fc4efd1fa85aa7817332c692704ba --- /dev/null +++ b/addons/dialogic/Modules/Background/default_background.gd @@ -0,0 +1,20 @@ +extends DialogicBackground + +## The default background scene. +## Extend the DialogicBackground class to create your own background scene. + + +func _ready() -> void: + $Image.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + $Image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED + + $Image.anchor_right = 1 + $Image.anchor_bottom = 1 + + +func _update_background(argument:String, time:float) -> void: + $Image.texture = load(argument) + + +func _should_do_background_update(argument:String) -> bool: + return false diff --git a/addons/dialogic/Modules/Background/default_background.tscn b/addons/dialogic/Modules/Background/default_background.tscn new file mode 100644 index 0000000000000000000000000000000000000000..1869647609dab848c131001e2b6a51089c43e90d --- /dev/null +++ b/addons/dialogic/Modules/Background/default_background.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=3 uid="uid://cl6g6ymkhjven"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Background/default_background.gd" id="1_nkdrp"] + +[node name="DefaultBackground" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_nkdrp") + +[node name="Image" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 0 diff --git a/addons/dialogic/Modules/Background/dialogic_background.gd b/addons/dialogic/Modules/Background/dialogic_background.gd new file mode 100644 index 0000000000000000000000000000000000000000..2f0a258891897f69bbf5b70b92643d1db5419683 --- /dev/null +++ b/addons/dialogic/Modules/Background/dialogic_background.gd @@ -0,0 +1,31 @@ +extends Node +class_name DialogicBackground + +## This is the base class for dialogic backgrounds. +## Extend it and override it's methods when you create a custom background. +## You can take a look at the default background to get an idea of how it's working. + + +## Load the new background in here. +## The time argument is given for when [_should_do_background_update] returns true +## (then you have to do a transition in here) +func _update_background(argument:String, time:float) -> void: + pass + + +## If a background event with this scene is encountered while this background is used, +## this decides whether to create a new instance and call fade_out or just call [_update_background] # on this scene. Default is false +func _should_do_background_update(argument:String) -> bool: + return false + + +## Called by dialogic when first created. +## If you return false (by default) it will attempt to animate the "modulate" property. +func _fade_in(time:float) -> bool: + return false + + +## Called by dialogic before removing (done by dialogic). +## If you return false (by default) it will attempt to animate the "modulate" property. +func _fade_out(time:float) -> bool: + return false diff --git a/addons/dialogic/Modules/Background/event_background.gd b/addons/dialogic/Modules/Background/event_background.gd new file mode 100644 index 0000000000000000000000000000000000000000..a21ac9856796cd78dd39e2e62dcab67ced96b71c --- /dev/null +++ b/addons/dialogic/Modules/Background/event_background.gd @@ -0,0 +1,74 @@ +@tool +class_name DialogicBackgroundEvent +extends DialogicEvent + +## Event to show scenes in the background and switch between them. + +### Settings + +## The scene to use. If empty, this will default to the DefaultBackground.gd scene. +## This scene supports images and fading. +## If you set it to a scene path, then that scene will be instanced. +## Learn more about custom backgrounds in the Subsystem_Background.gd docs. +var scene: String = "" +## The argument that is passed to the background scene. +## For the default scene it's the path to the image to show. +var argument: String = "" +## The time the fade animation will take. Leave at 0 for instant change. +var fade: float = 0.0 + + +################################################################################ +## EXECUTION +################################################################################ + +func _execute() -> void: + dialogic.Backgrounds.update_background(scene, argument, fade) + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Background" + set_default_color('Color8') + event_category = "Visuals" + event_sorting_index = 0 + expand_by_default = false + + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "background" + + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_info + "scene" : {"property": "scene", "default": ""}, + "arg" : {"property": "argument", "default": ""}, + "fade" : {"property": "fade", "default": 0}, + } + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('argument', ValueType.FILE, 'Show', '', + {'file_filter':'*.jpg, *.jpeg, *.png, *.webp, *.tga, *svg, *.bmp, *.dds, *.exr, *.hdr; Supported Image Files', + 'placeholder': "No background", + 'editor_icon':["Image", "EditorIcons"]}, + 'scene == ""') + add_header_edit('argument', ValueType.SINGLELINE_TEXT, 'Argument:', '', {}, 'scene != ""') + add_body_edit("fade", ValueType.FLOAT, "Fade Time: ") + add_body_edit("scene", ValueType.FILE, 'Scene:', '', + {'file_filter':'*.tscn, *.scn; Scene Files', + 'placeholder': "Default scene", + 'editor_icon':["PackedScene", "EditorIcons"]}) diff --git a/addons/dialogic/Modules/Background/icon.png b/addons/dialogic/Modules/Background/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d4cf970b5ae561798c7ecbd48f1d241f540ca37f Binary files /dev/null and b/addons/dialogic/Modules/Background/icon.png differ diff --git a/addons/dialogic/Modules/Background/icon.png.import b/addons/dialogic/Modules/Background/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..a601e4763a118090485ec10740d14da7723e3fc2 --- /dev/null +++ b/addons/dialogic/Modules/Background/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://517mp8gfj811" +path="res://.godot/imported/icon.png-cab4c78f59b171335e340ba590cf5991.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Background/icon.png" +dest_files=["res://.godot/imported/icon.png-cab4c78f59b171335e340ba590cf5991.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Background/index.gd b/addons/dialogic/Modules/Background/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..97c05b58f57d4a498c48386d3319492ea8cb4c9e --- /dev/null +++ b/addons/dialogic/Modules/Background/index.gd @@ -0,0 +1,10 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_background.gd')] + + +func _get_subsystems() -> Array: + return [{'name':'Backgrounds', 'script':this_folder.path_join('subsystem_backgrounds.gd')}] diff --git a/addons/dialogic/Modules/Background/node_background_holder.gd b/addons/dialogic/Modules/Background/node_background_holder.gd new file mode 100644 index 0000000000000000000000000000000000000000..f3aab044a5f8fc26049ef19ae6f706d824af707b --- /dev/null +++ b/addons/dialogic/Modules/Background/node_background_holder.gd @@ -0,0 +1,6 @@ +class_name DialogicNode_BackgroundHolder +extends CanvasLayer + + +func _ready(): + add_to_group('dialogic_background_holders') diff --git a/addons/dialogic/Modules/Background/subsystem_backgrounds.gd b/addons/dialogic/Modules/Background/subsystem_backgrounds.gd new file mode 100644 index 0000000000000000000000000000000000000000..0ad02dcc5b65dacb86f6fd03f2f5314677d07f9e --- /dev/null +++ b/addons/dialogic/Modules/Background/subsystem_backgrounds.gd @@ -0,0 +1,78 @@ +extends DialogicSubsystem + +## Subsystem for managing backgrounds. + +signal background_changed(info:Dictionary) + + +var default_background_scene :PackedScene = load(get_script().resource_path.get_base_dir().path_join('default_background.tscn')) +#################################################################################################### +## STATE +#################################################################################################### + +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR): + update_background() + + +func load_game_state(): + update_background(dialogic.current_state_info.get('background_scene', ''), dialogic.current_state_info.get('background_argument', '')) + + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +## Method that adds a given scene as child of the DialogicNode_BackgroundHolder. +## It will call [_update_background()] on that scene with the given argument [argument]. +## It will call [_fade_in()] on that scene with the given fade time. +## Will call fade_out on previous backgrounds scene. +## +## If the scene is the same as the last background you can bypass another instantiating +## and use the same scene. +## To do so implement [_should_do_background_update()] on the custom background scene. +## Then [_update_background()] will be called directly on that previous scene. +func update_background(scene:String = '', argument:String = '', fade_time:float = 0.0) -> void: + var info := {'scene':scene, 'argument':argument, 'fade_time':fade_time, 'same_scene':false} + for node in get_tree().get_nodes_in_group('dialogic_background_holders'): + if node.visible: + var bg_set: bool = false + if scene == dialogic.current_state_info['background_scene']: + for old_bg in node.get_children(): + if old_bg.has_method("_should_do_background_update") and old_bg._should_do_background_update(argument): + if old_bg.has_method('_update_background'): + old_bg._update_background(argument, fade_time) + bg_set = true + info['same_scene'] = true + if !bg_set: + # remove previous backgrounds + for old_bg in node.get_children(): + + if !old_bg._fade_out(fade_time) and "modulate" in old_bg: + var tween := old_bg.create_tween() + tween.tween_property(old_bg, "modulate", Color.TRANSPARENT, fade_time) + tween.tween_callback(old_bg.queue_free) + else: + old_bg.queue_free() + + var new_node:Node + if scene.ends_with('.tscn'): + new_node = load(scene).instantiate() + elif argument: + new_node = default_background_scene.instantiate() + else: + new_node = null + + if new_node: + node.add_child(new_node) + + if new_node.has_method('_update_background'): + new_node._update_background(argument, fade_time) + + if !new_node._fade_in(fade_time) and "modulate" in new_node: + new_node.modulate = Color.TRANSPARENT + var tween := new_node.create_tween() + tween.tween_property(new_node, "modulate", Color.WHITE, fade_time) + + dialogic.current_state_info['background_scene'] = scene + dialogic.current_state_info['background_argument'] = argument + background_changed.emit(info) diff --git a/addons/dialogic/Modules/CallNode/event_call_node.gd b/addons/dialogic/Modules/CallNode/event_call_node.gd new file mode 100644 index 0000000000000000000000000000000000000000..dfbb37cdf8343bc07e0119a71b4335ec012f6bbb --- /dev/null +++ b/addons/dialogic/Modules/CallNode/event_call_node.gd @@ -0,0 +1,114 @@ +@tool +class_name DialogicCallNodeEvent +extends DialogicEvent + +## Event that allows calling a method in a node or autoload. + +### Settings + +## The path to the node you want to call. You can access autoloads directly. No need to add /root. +var path: String = "" +## The name of the method to call on the given node. +var method: String = "" +## A list of arguments to give to the call. +var arguments: Array = [] +## If wait is true, the dialog will only continue once the called method is finished. +var wait: bool = false +## If this is true, the method will not be called. +## Instead you can activate it during a following text event with the [signal] command. +## Use together with [signal_name] property. +var inline: bool = false +## Only necessary if [inline] is true. Sets the argument to listen for. +var inline_signal_argument: String +## Only usefull if [inline] is true. If true, the command can only be used once. +## If false it will not stop listening to the signal (with that argument). +var inline_single_use: bool = true + + +################################################################################ +## EXECUTION +################################################################################ + +func _execute() -> void: + if inline: + dialogic.timeline_ended.connect(_disconnect_signal) + + if path.begins_with('root'): + path = "/"+path + if not "/" in path and dialogic.get_node('/root').has_node(path): + path = "/root/"+path + + var n :Node = dialogic.get_node_or_null(path) + if n: + if n.has_method(method): + if inline: + dialogic.text_signal.connect(_call_on_signal, CONNECT_PERSIST) + elif wait: + await n.callv(method, arguments) + else: + n.callv(method, arguments) + else: + printerr('[Dialogic] Call node event failed because of invalid path.') + finish() + + +func _call_on_signal(arg:String) -> void: + if arg != inline_signal_argument: + return + if inline_single_use: + dialogic.disconnect("text_signal", _call_on_signal) + var n = dialogic.get_node_or_null(path) + n.callv(method, arguments) + + +func _disconnect_signal(): + if dialogic.text_signal.is_connected(_call_on_signal): + dialogic.text_signal.disconnect(_call_on_signal) + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Call Node" + set_default_color('Color6') + event_category = "Logic" + event_sorting_index = 10 + expand_by_default = false + + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "call_node" + + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_info + "path" : {"property": "path", "default": ""}, + "method" : {"property": "method", "default": ""}, + "args" : {"property": "arguments", "default": []}, + "wait" : {"property": "wait", "default": false}, + "inline" : {"property": "inline", "default": false}, + "signal" : {"property": "inline_signal_argument", "default": ""}, + "single_use": {"property": "inline_single_use", "default": false} + } + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('method', ValueType.SINGLELINE_TEXT, 'Call method') + add_header_edit('path', ValueType.SINGLELINE_TEXT, 'in object') + add_body_edit('inline', ValueType.BOOL, 'Inline Command:', '', {'tooltip':"If enabled, the method won't be called instantly. Only when a signal is emmited inside the following text event will it be called."}) + add_body_edit('inline_signal_argument', ValueType.SINGLELINE_TEXT, 'Inline Signal Argument', '', {'tooltip':"For example if set to 'Hello' the method can be called with [signal=Hello] in the next text event."}, 'inline == true') + add_body_edit('inline_single_use', ValueType.BOOL, 'Single Use:', '', {'tooltip':"By default calling via in-text signal only works once. Uncheck this to make the event keep listening. \nThis only stays valid during this dialog."}, 'inline == true') + add_body_edit('wait', ValueType.BOOL, 'Wait:', '', {'tooltip':'Will wait for the method to finish. Only relevant for methods with `await` in them.'}, 'inline == false') + add_body_line_break() + add_body_edit('arguments', ValueType.STRING_ARRAY, 'Arguments:') diff --git a/addons/dialogic/Modules/CallNode/icon.png b/addons/dialogic/Modules/CallNode/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0bda4de2da2c8d25a50869ae2e4961f88083e25a Binary files /dev/null and b/addons/dialogic/Modules/CallNode/icon.png differ diff --git a/addons/dialogic/Modules/CallNode/icon.png.import b/addons/dialogic/Modules/CallNode/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..ede4e66db6ad115d5ed498e4c6cdb570f5d1192b --- /dev/null +++ b/addons/dialogic/Modules/CallNode/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://duvcdvtgy4h4b" +path="res://.godot/imported/icon.png-1b2ea8f74160fa92023fd73fef2fba71.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/CallNode/icon.png" +dest_files=["res://.godot/imported/icon.png-1b2ea8f74160fa92023fd73fef2fba71.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/CallNode/index.gd b/addons/dialogic/Modules/CallNode/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..f7d3da30bd9d4cbe8b15b80991ed19227548dcf3 --- /dev/null +++ b/addons/dialogic/Modules/CallNode/index.gd @@ -0,0 +1,6 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_call_node.gd')] diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd new file mode 100644 index 0000000000000000000000000000000000000000..2d006a540ef48985a6a27103f686ed8e1e9024ff --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd @@ -0,0 +1,11 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT) + + tween.tween_property(node, 'position:y', orig_pos.y-node.get_viewport().size.y/10, time*0.4).set_trans(Tween.TRANS_EXPO) + tween.parallel().tween_property(node, 'scale:y', 1.05, time*0.4).set_trans(Tween.TRANS_EXPO) + tween.tween_property(node, 'position:y', orig_pos.y, time*0.6).set_trans(Tween.TRANS_BOUNCE) + tween.parallel().tween_property(node, 'scale:y', 1, time*0.6).set_trans(Tween.TRANS_BOUNCE) + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in.gd new file mode 100644 index 0000000000000000000000000000000000000000..941d1e4e944a457c11b08fb41df0bde2eb243bdb --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in.gd @@ -0,0 +1,13 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + node.scale = Vector2() + node.modulate.a = 0 + + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.set_parallel() + tween.tween_property(node, 'scale', Vector2(1,1), time).set_trans(Tween.TRANS_SPRING).set_ease(Tween.EASE_OUT) + tween.tween_property(node, 'modulate:a', 1.0, time) + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/bounce_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_out.gd new file mode 100644 index 0000000000000000000000000000000000000000..bc9764282878a5e33f4e3b95b25b03d5b941cf71 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_out.gd @@ -0,0 +1,15 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + node.scale = Vector2(1,1) + node.modulate.a = 1 + + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_LINEAR) + tween.set_parallel() + + tween.tween_property(node, 'scale', Vector2(), time).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_IN) + tween.tween_property(node, 'modulate:a', 0.0, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_up.gd b/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_up.gd new file mode 100644 index 0000000000000000000000000000000000000000..4ee09eca240af36fd2dc04c5724eb94be00235a0 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_up.gd @@ -0,0 +1,14 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + node.position.y = orig_pos.y + node.get_viewport().size.y/5 + node.modulate.a = 0 + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.set_parallel() + + tween.tween_property(node, 'position', orig_pos, time) + tween.tween_property(node, 'modulate:a', 1.0, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/fade_out_down.gd b/addons/dialogic/Modules/Character/DefaultAnimations/fade_out_down.gd new file mode 100644 index 0000000000000000000000000000000000000000..45adeef1fd8591911b3e43924288fc970ca1a3ec --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/fade_out_down.gd @@ -0,0 +1,12 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.set_parallel() + + tween.tween_property(node, 'position:y', orig_pos.y + node.get_viewport().size.y/5, time) + tween.tween_property(node, 'modulate:a', 0.0, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd b/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd new file mode 100644 index 0000000000000000000000000000000000000000..f83a8ee800f4c6f4f9c80a297713eba4daf288d8 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd @@ -0,0 +1,7 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.tween_property(node, 'scale', Vector2(1,1)*1.2, time*0.5).set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT) + tween.tween_property(node, 'scale', Vector2(1,1), time*0.5).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT) + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_or_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_or_out.gd new file mode 100644 index 0000000000000000000000000000000000000000..873225bb55522e509cb5205d3ddd19ee272ef965 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_or_out.gd @@ -0,0 +1,5 @@ +extends DialogicAnimation + +func animate(): + await node.get_tree().process_frame + emit_signal('finished') diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd b/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd new file mode 100644 index 0000000000000000000000000000000000000000..6b22c9974311dd9ac4e6a9cb88f73d421124021b --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd @@ -0,0 +1,17 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE) + + var strength :float = node.get_viewport().size.x/60 + tween.tween_property(node, 'position:x', orig_pos.x+strength, time*0.2) + tween.tween_property(node, 'position:x', orig_pos.x-strength, time*0.1) + tween.tween_property(node, 'position:x', orig_pos.x+strength, time*0.1) + tween.tween_property(node, 'position:x', orig_pos.x-strength, time*0.1) + tween.tween_property(node, 'position:x', orig_pos.x+strength, time*0.1) + tween.tween_property(node, 'position:x', orig_pos.x-strength, time*0.1) + tween.tween_property(node, 'position:x', orig_pos.x+strength, time*0.1) + tween.tween_property(node, 'position:x', orig_pos.x, time*0.2) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd b/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd new file mode 100644 index 0000000000000000000000000000000000000000..63f78ba5ca619578cabe53f44a8d255e52e5a348 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd @@ -0,0 +1,17 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE) + + var strength :float = node.get_viewport().size.y/40 + tween.tween_property(node, 'position:y', orig_pos.y+strength, time*0.2) + tween.tween_property(node, 'position:y', orig_pos.y-strength, time*0.1) + tween.tween_property(node, 'position:y', orig_pos.y+strength, time*0.1) + tween.tween_property(node, 'position:y', orig_pos.y-strength, time*0.1) + tween.tween_property(node, 'position:y', orig_pos.y+strength, time*0.1) + tween.tween_property(node, 'position:y', orig_pos.y-strength, time*0.1) + tween.tween_property(node, 'position:y', orig_pos.y+strength, time*0.1) + tween.tween_property(node, 'position:y', orig_pos.y, time*0.2) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_down.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_down.gd new file mode 100644 index 0000000000000000000000000000000000000000..d448e12846a92fd4eae7bc03fd1d06703dc2c485 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_down.gd @@ -0,0 +1,10 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + node.position.y = -node.get_viewport().size.y + tween.tween_property(node, 'position:y', end_position.y, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_left.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_left.gd new file mode 100644 index 0000000000000000000000000000000000000000..77bf984f3aecc16839d1b7b16343d06aacda4c25 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_left.gd @@ -0,0 +1,10 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + node.position.x = -node.get_viewport().size.x/5 + tween.tween_property(node, 'position:x', end_position.x, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_right.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_right.gd new file mode 100644 index 0000000000000000000000000000000000000000..7df50cdc2a2fd0fba21bdeec634d132e40a9d287 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_right.gd @@ -0,0 +1,10 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + node.position.x = node.get_viewport().size.x+node.get_viewport().size.x/5 + tween.tween_property(node, 'position:x', end_position.x, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_up.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_up.gd new file mode 100644 index 0000000000000000000000000000000000000000..9c48695a4267eb5aae746c4ffc60721c15ce1536 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_up.gd @@ -0,0 +1,10 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + node.position.y = node.get_viewport().size.y*2 + tween.tween_property(node, 'position:y', end_position.y, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_down.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_down.gd new file mode 100644 index 0000000000000000000000000000000000000000..66de11015698ec5acb8be4232b86e3a1be11e1ff --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_down.gd @@ -0,0 +1,9 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_EXPO) + + tween.tween_property(node, 'position:y', node.get_viewport().size.y*2, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_left.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_left.gd new file mode 100644 index 0000000000000000000000000000000000000000..123b84412f380b6a7c1b80d4ea7a4fd8b4fe9fab --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_left.gd @@ -0,0 +1,9 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_EXPO) + + tween.tween_property(node, 'position:x', -node.get_viewport().size.x/5, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_right.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_right.gd new file mode 100644 index 0000000000000000000000000000000000000000..102412470a9affea7a71cca55b2e0e1eb757fad7 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_right.gd @@ -0,0 +1,9 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_EXPO) + + tween.tween_property(node, 'position:x', node.get_viewport().size.x+node.get_viewport().size.x/5, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_up.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_up.gd new file mode 100644 index 0000000000000000000000000000000000000000..1eeeeeffdf4a7e4d29fddf8f62afbbc895853182 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_up.gd @@ -0,0 +1,9 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_EXPO) + + tween.tween_property(node, 'position:y', -node.get_viewport().size.y, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd b/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd new file mode 100644 index 0000000000000000000000000000000000000000..ee9f001ecf5196b05153e9f888194fae8e161253 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd @@ -0,0 +1,19 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) + + var strength :float = 0.01 + + tween.set_parallel(true) + tween.tween_property(node, 'scale', Vector2(1,1)*(1+strength), time*0.3) + tween.tween_property(node, 'rotation', -strength, time*0.1).set_delay(time*0.2) + tween.tween_property(node, 'rotation', strength, time*0.1).set_delay(time*0.3) + tween.tween_property(node, 'rotation', -strength, time*0.1).set_delay(time*0.4) + tween.tween_property(node, 'rotation', strength, time*0.1).set_delay(time*0.5) + tween.tween_property(node, 'rotation', -strength, time*0.1).set_delay(time*0.6) + tween.chain().tween_property(node, 'scale', Vector2(1,1), time*0.3) + tween.parallel().tween_property(node, 'rotation', 0.0, time*0.3) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in.gd new file mode 100644 index 0000000000000000000000000000000000000000..749c2db7aef8596ff4dc45eb653c8bbcd2a7eec9 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in.gd @@ -0,0 +1,15 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + node.scale = Vector2(0,0) + node.modulate.a = 0 + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_EXPO) + tween.set_parallel(true) + +# node.position.y = node.get_viewport().size.y/2 + tween.tween_property(node, 'scale', Vector2(1,1), time) +# tween.tween_property(node, 'position:y', end_position.y, time) + tween.tween_property(node, 'modulate:a', 1, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in_center.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in_center.gd new file mode 100644 index 0000000000000000000000000000000000000000..7472a196cce484327f6f4696110c5da259e2f83c --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in_center.gd @@ -0,0 +1,15 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + node.scale = Vector2(0,0) + node.modulate.a = 0 + node.position = node.get_parent().size/2 + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_EXPO) + tween.set_parallel(true) + + tween.tween_property(node, 'scale', Vector2(1,1), time) + tween.tween_property(node, 'position', end_position, time) + tween.tween_property(node, 'modulate:a', 1, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out.gd new file mode 100644 index 0000000000000000000000000000000000000000..dedf61900f8caaee12cc78a70a7dc8c12742e3ab --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out.gd @@ -0,0 +1,13 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_EXPO) + tween.set_parallel(true) + +# node.position.y = node.get_viewport().size.y/2 + tween.tween_property(node, 'scale', Vector2(0,0), time) +# tween.tween_property(node, 'position:y', end_position.y, time) + tween.tween_property(node, 'modulate:a', 0, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out_center.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out_center.gd new file mode 100644 index 0000000000000000000000000000000000000000..1860259e32fc82ef284561bff23c4da682d1aed9 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out_center.gd @@ -0,0 +1,12 @@ +extends DialogicAnimation + +func animate(): + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN).set_trans(Tween.TRANS_EXPO) + tween.set_parallel(true) + + tween.tween_property(node, 'scale', Vector2(0,0), time) + tween.tween_property(node, 'position', node.get_parent().size/2, time) + tween.tween_property(node, 'modulate:a', 0, time) + + tween.finished.connect(emit_signal.bind('finished_once')) diff --git a/addons/dialogic/Modules/Character/class_dialogic_animation.gd b/addons/dialogic/Modules/Character/class_dialogic_animation.gd new file mode 100644 index 0000000000000000000000000000000000000000..83c5d198f2071b5686a534b385c030a6b89cee15 --- /dev/null +++ b/addons/dialogic/Modules/Character/class_dialogic_animation.gd @@ -0,0 +1,47 @@ +class_name DialogicAnimation +extends Node + +## Class that can be used to animate portraits. Can be extended to create animations. + +signal finished_once +signal finished + +## Set at runtime, will be the node to animate. +var node :Node +## Set at runtime, will be the length of the animation. +var time : float +## Set at runtime, will be the position at which to end the animation. +var end_position : Vector2 +## Set at runtime. The position the node started at. +var orig_pos : Vector2 + +## Used to repeate the animation for a number of times. +var repeats : int + + +func _ready(): + connect('finished_once', finished_one_loop) + + +## To be overridden. Do the actual animating/tweening in here. +## Use the properties [node], [time], [end_position], [orig_pos]. +func animate(): + pass + + +func finished_one_loop(): + repeats -= 1 + if repeats > 0: + animate() + elif repeats == 0: + emit_signal("finished") + + +func pause(): + if node: + node.process_mode = Node.PROCESS_MODE_DISABLED + + +func resume(): + if node: + node.process_mode = Node.PROCESS_MODE_INHERIT diff --git a/addons/dialogic/Modules/Character/default_portrait.gd b/addons/dialogic/Modules/Character/default_portrait.gd new file mode 100644 index 0000000000000000000000000000000000000000..1aa943de100aaa16dd2e06457e14e2c8e5932297 --- /dev/null +++ b/addons/dialogic/Modules/Character/default_portrait.gd @@ -0,0 +1,29 @@ +@tool +extends DialogicPortrait + +## Default portrait scene. + +## The parent class has a character and portrait variable. + +## If the custom portrait accepts a change, then accept it here +func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: + super._update_portrait(passed_character, passed_portrait) + if character.portraits.has(portrait): + var path :String = character.portraits[portrait].get('image', '') + $Portrait.texture = null + if !path.is_empty(): $Portrait.texture = load(path) + $Portrait.centered = false + $Portrait.scale = Vector2.ONE + $Portrait.position = $Portrait.get_rect().size * Vector2(-0.5, -1) + + +## This is called when the mirror changes +func _set_mirror(mirror:bool) -> void: + $Portrait.flip_h = mirror + + +## This is used by the editor preview and portrait containers +func _get_covered_rect() -> Rect2: + if $Portrait.texture == null: + return Rect2() + return Rect2($Portrait.position, $Portrait.get_rect().size) diff --git a/addons/dialogic/Modules/Character/default_portrait.tscn b/addons/dialogic/Modules/Character/default_portrait.tscn new file mode 100644 index 0000000000000000000000000000000000000000..524db9a6439e4bf369e2e5e73a6cbfb2ce3d552a --- /dev/null +++ b/addons/dialogic/Modules/Character/default_portrait.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=3 format=3 uid="uid://b32paf0ll6um8"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Character/default_portrait.gd" id="1_wn77n"] + +[sub_resource type="Texture2D" id="Texture2D_sny5f"] +resource_local_to_scene = false +resource_name = "" + +[node name="DefaultPortrait" type="Node2D"] +script = ExtResource("1_wn77n") + +[node name="Portrait" type="Sprite2D" parent="."] +texture = SubResource("Texture2D_sny5f") +centered = false diff --git a/addons/dialogic/Modules/Character/dialogic_portrait.gd b/addons/dialogic/Modules/Character/dialogic_portrait.gd new file mode 100644 index 0000000000000000000000000000000000000000..8dad86384cfba3047ac31e0c01ee240c1745ed80 --- /dev/null +++ b/addons/dialogic/Modules/Character/dialogic_portrait.gd @@ -0,0 +1,53 @@ +class_name DialogicPortrait +extends Node + +## Default portrait class. Should be extended by custom portraits. + +## Stores the character that this scene displays. +var character: DialogicCharacter +## Stores the name of the current portrait. +var portrait: String + + +## This function can be overridden. +## If this returns true, it won't insatnce a new scene, but call _update_portrait on this one. +## This is only relevant if the next portrait uses the same scene. +## This allows implmenting transitions between portraits that use the same scene. +func _should_do_portrait_update(character:DialogicCharacter, portrait:String) -> bool: + return true + + +## If the custom portrait accepts a change, then accept it here +## You should position your portrait so that the root node is at the pivot point*. +## For example for a simple sprite this code would work: +## >>> $Sprite.position = $Sprite.get_rect().size * Vector2(-0.5, -1) +## +## * this depends on the portrait containers, but it will most likely be the bottom center (99% of cases) +func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: + if passed_portrait == "" or not passed_portrait in passed_character.portraits.keys(): + passed_portrait = passed_character.default_portrait + + portrait = passed_portrait + character = passed_character + + +## This should be implemented. It is used for sizing in the +## character editor preview and in portrait containers. +## Scale and offset will be applied by dialogic. +## For example for a simple sprite this should work: +## >>> return Rect2($Sprite.position, $Sprite.get_rect().size) +## +## This will only work as expected if the portrait is positioned so that the root is at the pivot point. +func _get_covered_rect() -> Rect2: + return Rect2() + + +## If implemented, this is called when the mirror changes +func _set_mirror(mirror:bool) -> void: + pass + + +## Function to accept and use the extra data, if the custom portrait wants to accept it +func _set_extra_data(data: String) -> void: + pass + diff --git a/addons/dialogic/Modules/Character/event_character.gd b/addons/dialogic/Modules/Character/event_character.gd new file mode 100644 index 0000000000000000000000000000000000000000..995d53c03b8099f20fa0db79c3b0227e31f5d1df --- /dev/null +++ b/addons/dialogic/Modules/Character/event_character.gd @@ -0,0 +1,533 @@ +@tool +class_name DialogicCharacterEvent +extends DialogicEvent +## Event that allows to manipulate character portraits. + +enum Actions {JOIN, LEAVE, UPDATE} + + +### Settings + +## The type of action of this event (JOIN/LEAVE/UPDATE). See [Actions]. +var action : int = Actions.JOIN +## The character that will join/leave/update. +var character : DialogicCharacter = null +## For Join/Update, this will be the portrait of the character that is shown. +## Not used on Leave. +## If empty, the default portrait will be used. +var portrait: String = "" +## The index of the position this character should move to +var position: int = 1 +## Path to an animation script (extending DialogicAnimation). +## On Join/Leave empty (default) will fallback to the animations set in the settings. +## On Update empty will mean no animation. +var animation_name: String = "" +## Length of the animation. +var animation_length: float = 0.5 +## How often the animation is repeated. Only for Update events. +var animation_repeats: int = 1 +## If true, the events waits for the animation to finish before the next event starts. +var animation_wait: bool = false +## For Update only. If bigger then 0, the portrait will tween to the +## new position (if changed) in this time (in seconds). +var position_move_time: float = 0.0 +## The z_index that the portrait should have. +var z_index: int = 0 +## If true, the portrait will be set to mirrored. +var mirrored: bool = false +## If set, will be passed to the portrait scene. +var extra_data: String = "" + + +### Helpers + +## Indicates if the z_index should be updated. +var _update_zindex: bool = false +## Used to set the character resource from the unique name identifier and vice versa +var _character_from_directory: String: + get: + if _character_from_directory == '--All--': + return '--All--' + for item in _character_directory.keys(): + if _character_directory[item]['resource'] == character: + return item + break + return _character_from_directory + set(value): + _character_from_directory = value + if value in _character_directory.keys(): + character = _character_directory[value]['resource'] + else: + character = null +## Used by [_character_from_directory] +var _character_directory: Dictionary = {} +# Reference regex without Godot escapes: (?Join|Update|Leave)\s*(")?(?(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(\W*\((?.*)\))?(\s*(?\d))?(\s*\[(?.*)\])? +var regex := RegEx.create_from_string("(?Join|Update|Leave)\\s*(\")?(?(?(2)[^\"\\n]*|[^(: \\n]*))(?(2)\"|)(\\W*\\((?.*)\\))?(\\s*(?\\d))?(\\s*\\[(?.*)\\])?") + +################################################################################ +## EXECUTION +################################################################################ + +func _execute() -> void: + match action: + Actions.JOIN: + if character: + if dialogic.has_subsystem('History') and !dialogic.Portraits.is_character_joined(character): + dialogic.History.store_simple_history_entry(character.display_name + " joined", event_name, {'character': character.display_name, 'mode':'Join'}) + + await dialogic.Portraits.join_character(character, portrait, position, mirrored, z_index, extra_data, animation_name, animation_length, animation_wait) + + Actions.LEAVE: + if _character_from_directory == '--All--': + if dialogic.has_subsystem('History') and len(dialogic.Portraits.get_joined_characters()): + dialogic.History.store_simple_history_entry("Everyone left", event_name, {'character': "All", 'mode':'Leave'}) + + await dialogic.Portraits.leave_all_characters(animation_name, animation_length, animation_wait) + + elif character: + if dialogic.has_subsystem('History') and dialogic.Portraits.is_character_joined(character): + dialogic.History.store_simple_history_entry(character.display_name+" left", event_name, {'character': character.display_name, 'mode':'Leave'}) + + await dialogic.Portraits.leave_character(character, animation_name, animation_length, animation_wait) + + Actions.UPDATE: + if !character or !dialogic.Portraits.is_character_joined(character): + finish() + return + + dialogic.Portraits.change_character_portrait(character, portrait, false) + dialogic.Portraits.change_character_mirror(character, mirrored) + + if _update_zindex: + dialogic.Portraits.change_character_z_index(character, z_index) + + if position != 0: + dialogic.Portraits.move_character(character, position, position_move_time) + + if animation_name: + var anim :DialogicAnimation = dialogic.Portraits.animate_character(character, animation_name, animation_length, animation_repeats) + + if animation_wait: + dialogic.current_state = Dialogic.States.ANIMATING + await anim.finished + dialogic.current_state = Dialogic.States.IDLE + + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Character" + set_default_color('Color2') + event_category = "Main" + event_sorting_index = 2 + continue_at_end = true + expand_by_default = false + + +func _get_icon() -> Resource: + return load(self.get_script().get_path().get_base_dir().path_join('icon_character.png')) + +################################################################################ +## SAVING/LOADING +################################################################################ + +func to_text() -> String: + var result_string := "" + + match action: + Actions.JOIN: result_string += "Join " + Actions.LEAVE: result_string += "Leave " + Actions.UPDATE: result_string += "Update " + + var default_values := DialogicUtil.get_custom_event_defaults(event_name) + + if character or _character_from_directory == '--All--': + if action == Actions.LEAVE and _character_from_directory == '--All--': + result_string += "--All--" + else: + var name := "" + for path in _character_directory.keys(): + if _character_directory[path]['resource'] == character: + name = path + break + if name.count(" ") > 0: + name = '"' + name + '"' + result_string += name + if portrait.strip_edges() != default_values.get('portrait', '') and action != Actions.LEAVE: + result_string+= " ("+portrait+")" + + if action != Actions.LEAVE: + result_string += " "+str(position) + + if animation_name != "" or z_index != default_values.get('z_index', 0) or mirrored != default_values.get('mirrored', false) or position_move_time != default_values.get('position_move_time', 0) or extra_data != default_values.get('extra_data', ""): + result_string += " [" + if animation_name: + result_string += 'animation="'+DialogicUtil.pretty_name(animation_name)+'"' + + if animation_length != 0.5: + result_string += ' length="'+str(animation_length)+'"' + + if animation_wait: + result_string += ' wait="'+str(animation_wait)+'"' + + if animation_repeats != 1: + result_string += ' repeat="'+str(animation_repeats)+'"' + if z_index != 0: + result_string += ' z_index="' + str(z_index) + '"' + + if mirrored: + result_string += ' mirrored="' + str(mirrored) + '"' + + if position_move_time != 0: + result_string += ' move_time="' + str(position_move_time) + '"' + + if extra_data != "": + result_string += ' extra_data="' + extra_data + '"' + + result_string += "]" + return result_string + + +func from_text(string:String) -> void: + if Engine.is_editor_hint() == false: + _character_directory = Dialogic.character_directory + else: + _character_directory = self.get_meta("editor_character_directory") + + # load default character + if !_character_from_directory.is_empty() and _character_directory != null and _character_directory.size() > 0: + if _character_from_directory in _character_directory.keys(): + character = _character_directory[_character_from_directory]['resource'] + + + + var result := regex.search(string) + + match result.get_string('type'): + "Join": + action = Actions.JOIN + "Leave": + action = Actions.LEAVE + "Update": + action = Actions.UPDATE + + if result.get_string('name').strip_edges(): + if action == Actions.LEAVE and result.get_string('name').strip_edges() == "--All--": + _character_from_directory = '--All--' + else: + var name := result.get_string('name').strip_edges() + + if _character_directory != null and _character_directory.size() > 0: + character = null + if _character_directory.has(name): + character = _character_directory[name]['resource'] + else: + name = name.replace('"', "") + # First do a full search to see if more of the path is there then necessary: + for character in _character_directory: + if name in _character_directory[character]['full_path']: + character = _character_directory[character]['resource'] + break + + # If it doesn't exist, we'll consider it a guest and create a temporary character + if character == null: + if Engine.is_editor_hint() == false: + character = DialogicCharacter.new() + character.display_name = name + var entry:Dictionary = {} + entry['resource'] = character + entry['full_path'] = "runtime://" + name + Dialogic.character_directory[name] = entry + + if !result.get_string('portrait').is_empty(): + portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')') + + if result.get_string('position'): + position = result.get_string('position').to_int() + elif action == Actions.UPDATE: + # Override the normal default if it's an Update + position = 0 + + if result.get_string('shortcode'): + var shortcode_params = parse_shortcode_parameters(result.get_string('shortcode')) + animation_name = shortcode_params.get('animation', '') + if animation_name != "": + if !animation_name.ends_with('.gd'): + animation_name = guess_animation_file(animation_name) + if !animation_name.ends_with('.gd'): + printerr("[Dialogic] Couldn't identify animation '"+animation_name+"'.") + animation_name = "" + + var animLength = shortcode_params.get('length', '0.5').to_float() + if typeof(animLength) == TYPE_FLOAT: + animation_length = animLength + else: + animation_length = animLength.to_float() + + animation_wait = DialogicUtil.str_to_bool(shortcode_params.get('wait', 'false')) + + #repeat is supported on Update, the other two should not be checking this + if action == Actions.UPDATE: + animation_repeats = int(shortcode_params.get('repeat', 1)) + position_move_time = shortcode_params.get('move_time', 0.0) + #move time is only supported on Update, but it isnt part of the animations so its separate + if action == Actions.UPDATE: + if typeof(shortcode_params.get('move_time', 0)) == TYPE_STRING: + position_move_time = shortcode_params.get('move_time', 0.0).to_float() + + if typeof(shortcode_params.get('z_index', 0)) == TYPE_STRING: + z_index = shortcode_params.get('z_index', 0).to_int() + _update_zindex = true + mirrored = DialogicUtil.str_to_bool(shortcode_params.get('mirrored', 'false')) + extra_data = shortcode_params.get('extra_data', "") + + +# this is only here to provide a list of default values +# this way the module manager can add custom default overrides to this event. +# this is also why some properties are commented out, +# because it's not recommended to overwrite them this way +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_info + "action" : {"property": "action", "default": 0, + "suggestions": func(): return {'Join': + {'value':Actions.JOIN}, + 'Leave':{'value':Actions.LEAVE}, + 'Update':{'value':Actions.UPDATE}}}, + "character" : {"property": "_character_from_directory", "default": ""}, + "portrait" : {"property": "portrait", "default": ""}, + "position" : {"property": "position", "default": 1}, + +# "animation_name" : {"property": "animation_name", "default": ""}, +# "animation_length" : {"property": "animation_length", "default": 0.5}, +# "animation_wait" : {"property": "animation_wait", "default": false}, + "animation_repeats" : {"property": "animation_repeats", "default": 1}, + + "z_index" : {"property": "z_index", "default": 0}, + "move_time" : {"property": "position_move_time", "default": 0.0}, + "mirrored" : {"property": "mirrored", "default": false}, + "extra_data" : {"property": "extra_data", "default": ""}, + } + + +func is_valid_event(string:String) -> bool: + if string.begins_with("Join") or string.begins_with("Leave") or string.begins_with("Update"): + return true + return false + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor() -> void: + add_header_edit('action', ValueType.FIXED_OPTION_SELECTOR, '', '', { + 'selector_options': [ + { + 'label': 'Join', + 'value': Actions.JOIN, + 'icon': load("res://addons/dialogic/Editor/Images/Dropdown/join.svg") + }, + { + 'label': 'Leave', + 'value': Actions.LEAVE, + 'icon': load("res://addons/dialogic/Editor/Images/Dropdown/leave.svg") + }, + { + 'label': 'Update', + 'value': Actions.UPDATE, + 'icon': load("res://addons/dialogic/Editor/Images/Dropdown/update.svg") + } + ] + }) + add_header_edit('_character_from_directory', ValueType.COMPLEX_PICKER, '', '', + {'placeholder' : 'Character', + 'file_extension' : '.dch', + 'suggestions_func' : get_character_suggestions, + 'icon' : load("res://addons/dialogic/Editor/Images/Resources/character.svg"), + 'autofocus' : true}) +# add_header_button('', _on_character_edit_pressed, 'Edit character', ["ExternalLink", "EditorIcons"], 'character != null and _character_from_directory != "--All--"') + + add_header_edit('portrait', ValueType.COMPLEX_PICKER, '', '', + {'placeholder' : 'Default', + 'collapse_when_empty':true, + 'suggestions_func' : get_portrait_suggestions, + 'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")}, + 'should_show_portrait_selector()') + add_header_edit('position', ValueType.INTEGER, ' at position', '', {}, + 'character != null and !has_no_portraits() and action != %s' %Actions.LEAVE) + + # Body + add_body_edit('animation_name', ValueType.COMPLEX_PICKER, 'Animation:', '', + {'suggestions_func' : get_animation_suggestions, + 'editor_icon' : ["Animation", "EditorIcons"], + 'placeholder' : 'Default', + 'enable_pretty_name' : true}, + 'should_show_animation_options()') + add_body_edit('animation_length', ValueType.FLOAT, 'Length:', '', {}, + 'should_show_animation_options() and !animation_name.is_empty()') + add_body_edit('animation_wait', ValueType.BOOL, 'Await end:', '', {}, + 'should_show_animation_options() and !animation_name.is_empty()') + add_body_edit('animation_repeats', ValueType.INTEGER, 'Repeat:', '', {}, + 'should_show_animation_options() and !animation_name.is_empty() and action == %s)' %Actions.UPDATE) + add_body_edit('z_index', ValueType.INTEGER, 'Z-index:', "",{}, + 'action != %s' %Actions.LEAVE) + add_body_edit('mirrored', ValueType.BOOL, 'Mirrored:', "",{}, + 'action != %s' %Actions.LEAVE) + add_body_edit('position_move_time', ValueType.FLOAT, 'Movement duration:', '', {}, + 'action == %s' %Actions.UPDATE) + + +func should_show_animation_options() -> bool: + return (character != null and !character.portraits.is_empty()) or _character_from_directory == '--All--' + +func should_show_portrait_selector() -> bool: + return character != null and len(character.portraits) > 1 and action != Actions.LEAVE + +func has_no_portraits() -> bool: + return character and character.portraits.is_empty() + + +func get_character_suggestions(search_text:String) -> Dictionary: + var suggestions := {} + #override the previous _character_directory with the meta, specifically for searching otherwise new nodes wont work + _character_directory = Engine.get_main_loop().get_meta('dialogic_character_directory') + + var icon = load("res://addons/dialogic/Editor/Images/Resources/character.svg") + + suggestions['(No one)'] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + if action == Actions.LEAVE: + suggestions['ALL'] = {'value':'--All--', 'tooltip':'All currently joined characters leave', 'editor_icon':["GuiEllipsis", "EditorIcons"]} + for resource in _character_directory.keys(): + suggestions[resource] = {'value': resource, 'tooltip': _character_directory[resource]['full_path'], 'icon': icon.duplicate()} + return suggestions + + +func get_portrait_suggestions(search_text:String) -> Dictionary: + var suggestions := {} + var icon = load("res://addons/dialogic/Editor/Images/Resources/portrait.svg") + if action == Actions.UPDATE: + suggestions["Don't Change"] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + if action == Actions.JOIN: + suggestions["Default portrait"] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + if character != null: + for portrait in character.portraits: + suggestions[portrait] = {'value':portrait, 'icon':icon.duplicate()} + return suggestions + + +func get_animation_suggestions(search_text:String) -> Dictionary: + var suggestions := {} + + match action: + Actions.JOIN, Actions.LEAVE: + suggestions['Default'] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + Actions.UPDATE: + suggestions['None'] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + + + match action: + Actions.JOIN: + for anim in DialogicUtil.get_portrait_animation_scripts(DialogicUtil.AnimationType.IN): + suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'editor_icon':["Animation", "EditorIcons"]} + Actions.LEAVE: + for anim in DialogicUtil.get_portrait_animation_scripts(DialogicUtil.AnimationType.OUT): + suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'editor_icon':["Animation", "EditorIcons"]} + Actions.UPDATE: + for anim in DialogicUtil.get_portrait_animation_scripts(DialogicUtil.AnimationType.ACTION): + suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'editor_icon':["Animation", "EditorIcons"]} + + return suggestions + + +func guess_animation_file(animation_name: String) -> String: + for file in DialogicUtil.get_portrait_animation_scripts(): + if DialogicUtil.pretty_name(animation_name) == DialogicUtil.pretty_name(file): + return file + return animation_name + + +func _on_character_edit_pressed() -> void: + var editor_manager := _editor_node.find_parent('EditorsManager') + if editor_manager: + editor_manager.edit_resource(character) + + +####################### CODE COMPLETION ######################################## +################################################################################ + +#var completion_character_getter_regex := RegEx.new() +func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: + if symbol == ' ' and line.count(' ') == 1: + CodeCompletionHelper.suggest_characters(TextNode, CodeEdit.KIND_MEMBER) + if line.begins_with('Leave'): + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'All', '--All-- ', event_color, TextNode.get_theme_icon("GuiEllipsis", "EditorIcons")) + +# if completion_character_getter_regex.get_pattern().is_empty(): +# completion_character_getter_regex.compile("(?Join|Update|Leave)\\s*(\")?(?(?(2)[^\"\\n]*|[^(: \\n]*))(?(2)\"|)(\\W*\\((?.*)\\))?(\\s*(?\\d))?(\\s*\\[(?.*)\\])?") + if symbol == '(': + var character:= regex.search(line).get_string('name') + CodeCompletionHelper.suggest_portraits(TextNode, character) + + if '[' in line and (symbol == "[" or symbol == " "): + if !'animation=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'animation', 'animation="', TextNode.syntax_highlighter.normal_color) + if !'length=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'length', 'length="', TextNode.syntax_highlighter.normal_color) + if !'wait=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'wait', 'wait="', TextNode.syntax_highlighter.normal_color) + if line.begins_with('Update'): + if !'repeat=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'repeat', 'repeat="', TextNode.syntax_highlighter.normal_color) + if !'move_time=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'move_time', 'move_time="', TextNode.syntax_highlighter.normal_color) + if !line.begins_with('Leave'): + if !'mirrored=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'mirrored', 'mirrored="', TextNode.syntax_highlighter.normal_color) + if !'z_index=' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'z_index', 'z_index="', TextNode.syntax_highlighter.normal_color) + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'extra_data', 'extra_data="', TextNode.syntax_highlighter.normal_color) + + if '[' in line: + if CodeCompletionHelper.get_line_untill_caret(line).ends_with('animation="'): + var animations := [] + if line.begins_with('Join'): + animations = DialogicUtil.get_portrait_animation_scripts(DialogicUtil.AnimationType.IN) + if line.begins_with('Update'): + animations = DialogicUtil.get_portrait_animation_scripts(DialogicUtil.AnimationType.ACTION) + if line.begins_with('Leave'): + animations = DialogicUtil.get_portrait_animation_scripts(DialogicUtil.AnimationType.ALL) + for script in animations: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, DialogicUtil.pretty_name(script), DialogicUtil.pretty_name(script)+'" ', TextNode.syntax_highlighter.normal_color) + elif CodeCompletionHelper.get_line_untill_caret(line).ends_with('wait="') or CodeCompletionHelper.get_line_untill_caret(line).ends_with('mirrored="'): + CodeCompletionHelper.suggest_bool(TextNode, TextNode.syntax_highlighter.normal_color) + + +func _get_start_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit) -> void: + TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'Join', 'Join ', event_color, load('res://addons/dialogic/Editor/Images/Dropdown/join.svg')) + TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'Leave', 'Leave ', event_color, load('res://addons/dialogic/Editor/Images/Dropdown/leave.svg')) + TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'Update', 'Update ', event_color, load('res://addons/dialogic/Editor/Images/Dropdown/update.svg')) + + +#################### SYNTAX HIGHLIGHTING ####################################### +################################################################################ + +func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: + var word := line.get_slice(' ', 0) + + dict[line.find(word)] = {"color":event_color} + dict[line.find(word)+len(word)] = {"color":Highlighter.normal_color} + var result := regex.search(line) + if result.get_string('name'): + dict[result.get_start('name')] = {"color":event_color.lerp(Highlighter.normal_color, 0.5)} + dict[result.get_end('name')] = {"color":Highlighter.normal_color} + if result.get_string('portrait'): + dict[result.get_start('portrait')] = {"color":event_color.lerp(Highlighter.normal_color, 0.6)} + dict[result.get_end('portrait')] = {"color":Highlighter.normal_color} + if result.get_string('shortcode'): + dict = Highlighter.color_shortcode_content(dict, line, result.get_start('shortcode'), result.get_end('shortcode'), event_color) + return dict diff --git a/addons/dialogic/Modules/Character/event_position.gd b/addons/dialogic/Modules/Character/event_position.gd new file mode 100644 index 0000000000000000000000000000000000000000..2ce5b4ba28378c30fd4d65e3ec417ac67f1612f3 --- /dev/null +++ b/addons/dialogic/Modules/Character/event_position.gd @@ -0,0 +1,106 @@ +@tool +class_name DialogicPositionEvent +extends DialogicEvent + +## Event that allows moving of positions (and characters that are on that position). +## Requires the Portraits subsystem to be present! + +enum Actions {SET_RELATIVE, SET_ABSOLUTE, RESET, RESET_ALL} + + +### Settings + +## The type of action: SetRelative, SetAbsolute, Reset, ResetAll +var action := Actions.SET_RELATIVE +## The position that should be affected +var position: int = 0 +## A vector representing a relative change or an absolute position (for SetRelative and SetAbsolute) +var vector: Vector2 = Vector2() +## The time the tweening will take. +var movement_time: float = 0 + + +################################################################################ +## EXECUTE +################################################################################ +func _execute() -> void: + match action: + Actions.SET_RELATIVE: + dialogic.Portraits.move_portrait_position(position, vector, true, movement_time) + Actions.SET_ABSOLUTE: + dialogic.Portraits.move_portrait_position(position, vector, false, movement_time) + Actions.RESET_ALL: + dialogic.Portraits.reset_portrait_positions(movement_time) + Actions.RESET: + dialogic.Portraits.reset_portrait_position(position, movement_time) + + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Position" + set_default_color('Color2') + event_category = "Other" + event_sorting_index = 2 + continue_at_end = true + expand_by_default = false + + +func _get_icon() -> Resource: + return load(self.get_script().get_path().get_base_dir().path_join('icon_position.png')) + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "update_position" + + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_info + "action" : {"property": "action", "default": Actions.SET_RELATIVE, + "suggestions": func(): return {"Set Relative":{'value':'0'}, "Set Absolute":{'value':'1'}, "Reset":{'value':'2'}, "Reset All":{'value':'3'}}}, + "position" : {"property": "position", "default": 0}, + "vector" : {"property": "vector", "default": Vector2()}, + "time" : {"property": "movement_time", "default": 0}, + } + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('action', ValueType.FIXED_OPTION_SELECTOR, '', '', { + 'selector_options': [ + { + 'label': 'Change', + 'value': Actions.SET_RELATIVE, + }, + { + 'label': 'Set', + 'value': Actions.SET_ABSOLUTE, + }, + { + 'label': 'Reset', + 'value': Actions.RESET, + }, + { + 'label': 'Reset All', + 'value': Actions.RESET_ALL, + } + ] + }) + add_header_edit("position", ValueType.INTEGER, "position", '', {}, + 'action != Actions.RESET_ALL') + add_header_label('to (absolute)', 'action == Actions.SET_ABSOLUTE') + add_header_label('by (relative)', 'action == Actions.SET_RELATIVE') + add_header_edit("vector", ValueType.VECTOR2, "", '', {}, + 'action != Actions.RESET and action != Actions.RESET_ALL') + add_body_edit("movement_time", ValueType.FLOAT, "AnimationTime:", "(0 for instant)") diff --git a/addons/dialogic/Modules/Character/icon.png.import b/addons/dialogic/Modules/Character/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..ec40123c7e59bbc6d34926b16e3ddf82b64b01a8 --- /dev/null +++ b/addons/dialogic/Modules/Character/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dn5jx2ucynfio" +path="res://.godot/imported/icon.png-a6ef7c3eeb0fb100c7d0b0c505ea4b6f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Events/Character/icon.png" +dest_files=["res://.godot/imported/icon.png-a6ef7c3eeb0fb100c7d0b0c505ea4b6f.ctex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Character/icon_character.png b/addons/dialogic/Modules/Character/icon_character.png new file mode 100644 index 0000000000000000000000000000000000000000..5858854742aa5e2f19debcb1c6310581a24ded7f Binary files /dev/null and b/addons/dialogic/Modules/Character/icon_character.png differ diff --git a/addons/dialogic/Modules/Character/icon_character.png.import b/addons/dialogic/Modules/Character/icon_character.png.import new file mode 100644 index 0000000000000000000000000000000000000000..05efb2416c5f319f4e487b26f4a32aa35777f662 --- /dev/null +++ b/addons/dialogic/Modules/Character/icon_character.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1i3ttk36kghm" +path="res://.godot/imported/icon_character.png-df3b6adf2508bd6ab91af526266b4aaa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Character/icon_character.png" +dest_files=["res://.godot/imported/icon_character.png-df3b6adf2508bd6ab91af526266b4aaa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Character/icon_position.png b/addons/dialogic/Modules/Character/icon_position.png new file mode 100644 index 0000000000000000000000000000000000000000..7f01934ecb234a9c353efb20bb6005ecbbc71de4 Binary files /dev/null and b/addons/dialogic/Modules/Character/icon_position.png differ diff --git a/addons/dialogic/Modules/Character/icon_position.png.import b/addons/dialogic/Modules/Character/icon_position.png.import new file mode 100644 index 0000000000000000000000000000000000000000..120f0f4679f8a162aa97ed02f0640483f6811330 --- /dev/null +++ b/addons/dialogic/Modules/Character/icon_position.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cu6cj55m6dr78" +path="res://.godot/imported/icon_position.png-221b7c348ec45b22fd4df49fdb92a7aa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Character/icon_position.png" +dest_files=["res://.godot/imported/icon_position.png-221b7c348ec45b22fd4df49fdb92a7aa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Character/index.gd b/addons/dialogic/Modules/Character/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..feaa1a37fad774b25dc10bd8ba9b4a57f331c683 --- /dev/null +++ b/addons/dialogic/Modules/Character/index.gd @@ -0,0 +1,21 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_character.gd'), this_folder.path_join('event_position.gd')] + + +func _get_subsystems() -> Array: + return [{'name':'Portraits', 'script':this_folder.path_join('subsystem_portraits.gd')}] + + +func _get_settings_pages() -> Array: + return [this_folder.path_join('settings_portraits.tscn')] + +func _get_text_effects() -> Array[Dictionary]: + return [{'command':'portrait', 'subsystem':'Portraits', 'method':'text_effect_portrait', 'arg':true}] + + +func _get_portrait_animations() -> Array: + return list_dir('DefaultAnimations') diff --git a/addons/dialogic/Modules/Character/node_portrait_container.gd b/addons/dialogic/Modules/Character/node_portrait_container.gd new file mode 100644 index 0000000000000000000000000000000000000000..8287de350de73e8c7e9949477779fe8dc7c16761 --- /dev/null +++ b/addons/dialogic/Modules/Character/node_portrait_container.gd @@ -0,0 +1,194 @@ +@tool +class_name DialogicNode_PortraitContainer +extends Control + +## Node that defines a position for dialogic portraits and how to display portrait at that position. + +enum PositionModes { + POSITION, ## This container has an index and can be joined/moved to with the Character Event + SPEAKER, ## This container has no index and is joined/left automatically based on the speaker. + } + +@export var mode := PositionModes.POSITION + +@export_subgroup('Mode: Position') +## The position this node corresponds to. +@export var position_index := 0 + + +@export_subgroup('Mode: Speaker') +## Can be used to use a different portrait. +## E.g. "Faces/" would mean instead of "happy" it will use portrait "Faces/happy" +@export var portrait_prefix := '' + +@export_subgroup('Portrait Placement') +enum SizeModes {KEEP, FIT_STRETCH, FIT_IGNORE_SCALE, FIT_SCALE_HEIGHT} +## Defines how to affect the scale of the portrait +@export var size_mode : SizeModes = SizeModes.FIT_SCALE_HEIGHT : + set(mode): + size_mode = mode + _update_debug_portrait_size_position() + +## If true, portraits will be mirrored in this position. +@export var mirrored := false : + set(mirror): + mirrored = mirror + _update_debug_portrait_scene() + + +@export_group('Origin', 'origin') +enum OriginAnchors {TOP_LEFT, TOP_CENTER, TOP_RIGHT, LEFT_MIDDLE, CENTER, RIGHT_MIDDLE, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT} +## The portrait will be placed relative to this point in the container. +@export var origin_anchor : OriginAnchors = OriginAnchors.BOTTOM_CENTER : + set(anchor): + origin_anchor = anchor + _update_debug_origin() + +## An offset to apply to the origin. Rarely useful. +@export var origin_offset := Vector2() : + set(offset): + origin_offset = offset + _update_debug_origin() + + +@export_group('Debug', 'debug') +## A character that will be displayed in the editor, useful for getting the right size. +@export var debug_character : DialogicCharacter = null: + set(character): + debug_character = character + _update_debug_portrait_scene() +@export var debug_character_portrait :String = "": + set(portrait): + debug_character_portrait = portrait + _update_debug_portrait_scene() + +var debug_character_holder_node :Node2D = null +var debug_character_scene_node : Node = null +var debug_origin : Sprite2D = null +var default_portrait_scene :String = DialogicUtil.get_module_path('Character').path_join("default_portrait.tscn") +# Used if no debug character is specified +var default_debug_character := load(DialogicUtil.get_module_path('Character').path_join("preview_character.tres")) + + +func _ready(): + match mode: + PositionModes.POSITION: + add_to_group('dialogic_portrait_con_position') + PositionModes.SPEAKER: + add_to_group('dialogic_portrait_con_speaker') + + if Engine.is_editor_hint(): + resized.connect(_update_debug_origin) + + debug_origin = Sprite2D.new() + add_child(debug_origin) + debug_origin.texture = get_theme_icon("EditorPosition", "EditorIcons") + + _update_debug_origin() + _update_debug_portrait_scene() + else: + resized.connect(update_portrait_transforms) + + +################################################################################ +## MAIN METHODS +################################################################################ + +func update_portrait_transforms(): + for child in get_children(): + Dialogic.Portraits._update_portrait_transform(child) + +## Returns a Rect2 with the position as the position and the scale as the size. +func get_local_portrait_transform(portrait_rect:Rect2, character_scale:=1.0) -> Rect2: + var transform := Rect2() + transform.position = _get_origin_position() + + # Mode that ignores the containers size + if size_mode == SizeModes.KEEP: + transform.size = Vector2(1,1)*character_scale + + # Mode that makes sure neither height nor width go out of container + elif size_mode == SizeModes.FIT_IGNORE_SCALE: + if size.x/size.y < portrait_rect.size.x/portrait_rect.size.y: + transform.size = Vector2(1,1) * size.x/portrait_rect.size.x + else: + transform.size = Vector2(1,1) * size.y/portrait_rect.size.y + + # Mode that stretches the portrait to fill the whole container + elif size_mode == SizeModes.FIT_STRETCH: + transform.size = size/portrait_rect.size + + # Mode that size the character so 100% size fills the height + elif size_mode == SizeModes.FIT_SCALE_HEIGHT: + transform.size = Vector2(1,1) * size.y/portrait_rect.size.y*character_scale + + return transform + + +## Returns the current origin position +func _get_origin_position() -> Vector2: + return size*Vector2(origin_anchor%3/2.0, floor(origin_anchor/3.0)/2.0) + origin_offset + +################################################################################ +## DEBUG METHODS +################################################################################ + +## Loads the debug_character with the debug_character_portrait +## Creates a holder node and applies mirror +func _update_debug_portrait_scene() -> void: + if !Engine.is_editor_hint(): + return + if is_instance_valid(debug_character_holder_node): + for child in get_children(): + if child != debug_origin: + child.free() + + var character := _get_debug_character() + if not character is DialogicCharacter or character.portraits.is_empty(): + return + var debug_portrait := debug_character_portrait + if debug_portrait.is_empty(): debug_portrait = character.default_portrait + if mode == PositionModes.SPEAKER and !portrait_prefix.is_empty(): + if portrait_prefix+debug_portrait in character.portraits: + debug_portrait = portrait_prefix+debug_portrait + var portrait_info :Dictionary = character.get_portrait_info(debug_portrait) + var portrait_scene_path :String = portrait_info.get('scene', default_portrait_scene) + if portrait_scene_path.is_empty(): portrait_scene_path = default_portrait_scene + debug_character_scene_node = load(portrait_scene_path).instantiate() + if !is_instance_valid(debug_character_scene_node): + return + debug_character_scene_node._update_portrait(character, debug_portrait) + if !is_instance_valid(debug_character_holder_node): + debug_character_holder_node = Node2D.new() + add_child(debug_character_holder_node) + debug_character_holder_node.add_child(debug_character_scene_node) + move_child(debug_character_holder_node, 0) + debug_character_scene_node._set_mirror(character.mirror != mirrored != portrait_info.get('mirror', false)) + _update_debug_portrait_size_position() + + +## Set's the size and position of the holder and scene node +## according to the size_mode +func _update_debug_portrait_size_position() -> void: + if !Engine.is_editor_hint() or !is_instance_valid(debug_character_scene_node) or !is_instance_valid(debug_origin): + return + var character := _get_debug_character() + var portrait_info := character.get_portrait_info(debug_character_portrait) + var transform := get_local_portrait_transform(debug_character_scene_node._get_covered_rect(), character.scale*portrait_info.get('scale', 1)) + debug_character_holder_node.position = transform.position + debug_character_scene_node.position = portrait_info.get('offset', Vector2())+character.offset + + debug_character_holder_node.scale = transform.size + +## Updates the debug origins position. Also calls _update_debug_portrait_size_position() +func _update_debug_origin() -> void: + if !Engine.is_editor_hint() or !is_instance_valid(debug_origin): + return + debug_origin.position = _get_origin_position() + _update_debug_portrait_size_position() + + + +## Returns the debug character or the default debug character +func _get_debug_character() -> DialogicCharacter: + return debug_character if debug_character != null else default_debug_character diff --git a/addons/dialogic/Modules/Character/preview_character.tres b/addons/dialogic/Modules/Character/preview_character.tres new file mode 100644 index 0000000000000000000000000000000000000000..d40edd87bf8d6884cef7631495c3b9c6e6019e68 --- /dev/null +++ b/addons/dialogic/Modules/Character/preview_character.tres @@ -0,0 +1,30 @@ +[gd_resource type="Resource" script_class="DialogicCharacter" load_steps=2 format=3 uid="uid://dykf1j17ct5mo"] + +[ext_resource type="Script" path="res://addons/dialogic/Resources/character.gd" id="1_qsljv"] + +[resource] +script = ExtResource("1_qsljv") +display_name = "Preview" +nicknames = [""] +color = Color(1, 1, 1, 1) +description = "" +scale = 1.0 +offset = Vector2(0, 0) +mirror = false +default_portrait = "character" +portraits = { +"character": { +"image": "res://addons/dialogic/Editor/Images/preview_character.png", +"offset": Vector2(0, 0) +}, +"speaker": { +"image": "res://addons/dialogic/Editor/Images/preview_character_speaker.png", +"offset": Vector2(0, 0) +} +} +custom_info = { +"sound_mood_default": "", +"sound_moods": {}, +"style": "" +} +metadata/timeline_not_saved = true diff --git a/addons/dialogic/Modules/Character/settings_portraits.gd b/addons/dialogic/Modules/Character/settings_portraits.gd new file mode 100644 index 0000000000000000000000000000000000000000..4acacae6f5b055dfc327ee713cd9a7305d09c145 --- /dev/null +++ b/addons/dialogic/Modules/Character/settings_portraits.gd @@ -0,0 +1,70 @@ +@tool +extends DialogicSettingsPage + + +func _ready(): + %JoinDefault.get_suggestions_func = get_join_animation_suggestions + %JoinDefault.enable_pretty_name = true + %LeaveDefault.get_suggestions_func = get_leave_animation_suggestions + %LeaveDefault.enable_pretty_name = true + + +func _refresh(): + %JoinDefault.resource_icon = get_theme_icon("Animation", "EditorIcons") + %LeaveDefault.resource_icon = get_theme_icon("Animation", "EditorIcons") + %JoinDefault.set_value(DialogicUtil.pretty_name(ProjectSettings.get_setting('dialogic/animations/join_default', + get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_in_up.gd')))) + %LeaveDefault.set_value(ProjectSettings.get_setting('dialogic/animations/leave_default', + get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_out_down.gd'))) + %JoinDefaultLength.set_value(ProjectSettings.get_setting('dialogic/animations/join_default_length', 0.5)) + %LeaveDefaultLength.set_value(ProjectSettings.get_setting('dialogic/animations/leave_default_length', 0.5)) + %LeaveDefaultWait.button_pressed = ProjectSettings.get_setting('dialogic/animations/leave_default_wait', true) + %JoinDefaultWait.button_pressed = ProjectSettings.get_setting('dialogic/animations/join_default_wait', true) + + +func _on_LeaveDefault_value_changed(property_name, value): + ProjectSettings.set_setting('dialogic/animations/leave_default', value) + ProjectSettings.save() + + +func _on_JoinDefault_value_changed(property_name, value): + ProjectSettings.set_setting('dialogic/animations/join_default', value) + ProjectSettings.save() + + +func _on_JoinDefaultLength_value_changed(value): + ProjectSettings.set_setting('dialogic/animations/join_default_length', value) + ProjectSettings.save() + + +func _on_LeaveDefaultLength_value_changed(value): + ProjectSettings.set_setting('dialogic/animations/leave_default_length', value) + ProjectSettings.save() + +func _on_JoinDefaultWait_toggled(button_pressed): + ProjectSettings.set_setting('dialogic/animations/join_default_wait', button_pressed) + ProjectSettings.save() + +func _on_LeaveDefaultWait_toggled(button_pressed): + ProjectSettings.set_setting('dialogic/animations/leave_default_wait', button_pressed) + ProjectSettings.save() + + +func get_join_animation_suggestions(search_text:String) -> Dictionary: + var suggestions = {} + for anim in list_animations(): + if '_in' in anim.get_file(): + suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'icon':get_theme_icon('Animation', 'EditorIcons')} + return suggestions + +func get_leave_animation_suggestions(search_text:String) -> Dictionary: + var suggestions = {} + for anim in list_animations(): + if '_out' in anim.get_file(): + suggestions[DialogicUtil.pretty_name(anim)] = {'value':anim, 'icon':get_theme_icon('Animation', 'EditorIcons')} + return suggestions + +func list_animations() -> Array: + var list = DialogicUtil.listdir(get_script().resource_path.get_base_dir().path_join('DefaultAnimations'), true, false, true) + list.append_array(DialogicUtil.listdir(ProjectSettings.get_setting('dialogic/animations/custom_folder', 'res://addons/dialogic_additions/Animations'), true, false, true)) + return list diff --git a/addons/dialogic/Modules/Character/settings_portraits.tscn b/addons/dialogic/Modules/Character/settings_portraits.tscn new file mode 100644 index 0000000000000000000000000000000000000000..48f53e181523804ae2a226d18c50e0951c1d7951 --- /dev/null +++ b/addons/dialogic/Modules/Character/settings_portraits.tscn @@ -0,0 +1,95 @@ +[gd_scene load_steps=6 format=3 uid="uid://cp463rpri5j8a"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Character/settings_portraits.gd" id="2"] +[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_dce78"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3"] + +[sub_resource type="Image" id="Image_wuu7a"] +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_hg5vp"] +image = SubResource("Image_wuu7a") + +[node name="Portraits" type="VBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("2") + +[node name="Animations2" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.5 + +[node name="Title2" type="Label" parent="Animations2"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Default Animations +" + +[node name="HintTooltip" parent="Animations2" instance=ExtResource("2_dce78")] +layout_mode = 2 +tooltip_text = "These settings are used for Leave and Join events if no animation is selected." +texture = SubResource("ImageTexture_hg5vp") +hint_text = "These settings are used for Leave and Join events if no animation is selected." + +[node name="GridContainer" type="GridContainer" parent="."] +layout_mode = 2 +columns = 2 + +[node name="Label3" type="Label" parent="GridContainer"] +layout_mode = 2 +text = "Join:" + +[node name="DefaultIn" type="HBoxContainer" parent="GridContainer"] +layout_mode = 2 + +[node name="JoinDefault" parent="GridContainer/DefaultIn" instance=ExtResource("3")] +unique_name_in_owner = true +layout_mode = 2 +enable_pretty_name = true + +[node name="JoinDefaultLength" type="SpinBox" parent="GridContainer/DefaultIn"] +unique_name_in_owner = true +layout_mode = 2 +step = 0.1 + +[node name="JoinDefaultWait" type="CheckButton" parent="GridContainer/DefaultIn"] +unique_name_in_owner = true +layout_mode = 2 +text = "Wait:" + +[node name="Label4" type="Label" parent="GridContainer"] +layout_mode = 2 +text = "Leave:" + +[node name="DefaultOut" type="HBoxContainer" parent="GridContainer"] +layout_mode = 2 + +[node name="LeaveDefault" parent="GridContainer/DefaultOut" instance=ExtResource("3")] +unique_name_in_owner = true +layout_mode = 2 +enable_pretty_name = true + +[node name="LeaveDefaultLength" type="SpinBox" parent="GridContainer/DefaultOut"] +unique_name_in_owner = true +layout_mode = 2 +step = 0.1 + +[node name="LeaveDefaultWait" type="CheckButton" parent="GridContainer/DefaultOut"] +unique_name_in_owner = true +layout_mode = 2 +text = "Wait:" + +[connection signal="value_changed" from="GridContainer/DefaultIn/JoinDefault" to="." method="_on_JoinDefault_value_changed"] +[connection signal="value_changed" from="GridContainer/DefaultIn/JoinDefaultLength" to="." method="_on_JoinDefaultLength_value_changed"] +[connection signal="toggled" from="GridContainer/DefaultIn/JoinDefaultWait" to="." method="_on_JoinDefaultWait_toggled"] +[connection signal="value_changed" from="GridContainer/DefaultOut/LeaveDefault" to="." method="_on_LeaveDefault_value_changed"] +[connection signal="value_changed" from="GridContainer/DefaultOut/LeaveDefaultLength" to="." method="_on_LeaveDefaultLength_value_changed"] +[connection signal="toggled" from="GridContainer/DefaultOut/LeaveDefaultWait" to="." method="_on_LeaveDefaultWait_toggled"] diff --git a/addons/dialogic/Modules/Character/subsystem_portraits.gd b/addons/dialogic/Modules/Character/subsystem_portraits.gd new file mode 100644 index 0000000000000000000000000000000000000000..2176efd733b421f1d313c3d3b0a36dd35ed83135 --- /dev/null +++ b/addons/dialogic/Modules/Character/subsystem_portraits.gd @@ -0,0 +1,557 @@ +extends DialogicSubsystem + +## Subsystem that manages portraits and portrait positions. + +signal character_joined(info:Dictionary) +signal character_left(info:Dictionary) +signal character_portrait_changed(info:Dictionary) +signal character_moved(info:Dictionary) +signal position_changed(info:Dictionary) + + +## The default portrait scene. +var default_portrait_scene :PackedScene = load(get_script().resource_path.get_base_dir().path_join('default_portrait.tscn')) + +## A reference to the current [DialogicNode_PortraitHolder]. +var _portrait_holder_reference: Node = null + +#################################################################################################### +## STATE +#################################################################################################### + +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR): + for character in dialogic.current_state_info.get('portraits', {}).keys(): + remove_character(load(character)) + dialogic.current_state_info['portraits'] = {} + dialogic.current_state_info['speaker_portraits'] = {} + + +func load_game_state(): + for character_path in dialogic.current_state_info.portraits: + var character_info :Dictionary = dialogic.current_state_info.portraits[character_path] + dialogic.current_state_info.portraits.erase(character_path) + add_character(load(character_path), character_info.portrait, character_info.position_index) + + +func pause() -> void: + for portrait in dialogic.current_state_info['portraits'].values(): + if portrait.has('animation_node'): + portrait.animation_node.pause() + + +func resume() -> void: + for portrait in dialogic.current_state_info['portraits'].values(): + if portrait.has('animation_node'): + portrait.animation_node.resume() + + +################### Main Methods ################################################################## +#################################################################################################### +## The following methods allow manipulating portraits. +## A portrait is made up of a character node [Node2D] that instances the portrait scene as it's child. +## The character node is then always the child of a portrait container. +## - Position (PortraitContainer) +## ---- character_node (Node2D) +## --------- portrait_node (e.g. default_portrait.tscn, or a custom portrait) +## +## Using these main methods a character can be present multiple times. +## For a VN style, the "character" methods (next section) provide access based on the character. +## - (That is what the character event uses) + +## Creates a new [character node] for the given [character], and add it to the given [portrait container]. +func _create_character_node(character:DialogicCharacter, container:DialogicNode_PortraitContainer) -> Node: + var character_node := Node2D.new() + character_node.name = character.get_character_name() + character_node.set_meta('character', character) + container.add_child(character_node) + return character_node + + +# Changes the portrait of a specific [character node]. +func _change_portrait(character_node:Node2D, portrait:String, update_transform:=true) -> Dictionary: + var character :DialogicCharacter = character_node.get_meta('character') + if portrait.is_empty(): + portrait = character.default_portrait + + var info := {'character':character, 'portrait':portrait, 'same_scene':false} + + if not portrait in character.portraits.keys(): + print_debug('[Dialogic] Change to not-existing portrait will be ignored!') + return info + + # path to the scene to use + var scene_path :String = character.portraits[portrait].get('scene', '') + + var portrait_node : Node = null + + # check if the scene is the same as the currently loaded scene + if (character_node.get_child_count() and + character_node.get_child(0).get_meta('scene', '') == scene_path and + # also check if the scene supports changing to the given portrait + (!character_node.get_child(0).has_method('_should_do_portrait_update') or character_node.get_child(0)._should_do_portrait_update(character, portrait))): + portrait_node = character_node.get_child(0) + info['same_scene'] = true + + else: + # remove previous portrait + if character_node.get_child_count(): + character_node.get_child(0).queue_free() + character_node.remove_child(character_node.get_child(0)) + + if scene_path.is_empty(): + portrait_node = default_portrait_scene.instantiate() + else: + var p :PackedScene = load(scene_path) + if p: + portrait_node = p.instantiate() + else: + push_error('Dialogic: Portrait node "' + str(scene_path) + '" for character [' + character.display_name + '] could not be loaded. Your portrait might not show up on the screen.') + + portrait_node.set_meta('scene', scene_path) + + if portrait_node: + character_node.set_meta('portrait', portrait) + + for property in character.portraits[portrait].get('export_overrides', {}).keys(): + portrait_node.set(property, str_to_var(character.portraits[portrait]['export_overrides'][property])) + + if portrait_node.has_method('_update_portrait'): + portrait_node._update_portrait(character, portrait) + + if !portrait_node.is_inside_tree(): + character_node.add_child(portrait_node) + + if update_transform: + _update_portrait_transform(character_node) + + return info + + +## Changes the mirroring of the given portrait. +## Unless @force is false, this will take into consideration the character mirror, +## portrait mirror and portrait position mirror settings. +func _change_portrait_mirror(character_node:Node2D, mirrored:=false, force:=false) -> void: + if character_node.get_child(0).has_method('_set_mirror'): + var character :DialogicCharacter= character_node.get_meta('character') + var current_portrait_info := character.get_portrait_info(character_node.get_meta('portrait')) + character_node.get_child(0)._set_mirror(force or (mirrored != character.mirror != character_node.get_parent().mirrored != current_portrait_info.get('mirror', false))) + + +func _change_portrait_extradata(character_node:Node2D, extra_data:="") -> void: + if character_node.get_child(0).has_method('_set_extra_data'): + character_node.get_child(0)._set_extra_data(extra_data) + + +func _update_portrait_transform(character_node:Node2D, time:float = 0.0) -> void: + var character :DialogicCharacter= character_node.get_meta('character') + + var portrait_node :Node = character_node.get_child(0) + var portrait_info :Dictionary = character.portraits.get(character_node.get_meta('portrait'), {}) + + # ignore the character scale on custom portraits that have 'ignore_char_scale' set to true + var apply_character_scale :bool= !portrait_info.get('ignore_char_scale', false) + var transform :Rect2 = character_node.get_parent().get_local_portrait_transform( + portrait_node._get_covered_rect(), + (character.scale * portrait_info.get('scale', 1))*int(apply_character_scale)+portrait_info.get('scale')*int(!apply_character_scale)) + + var tween : Tween + if character_node.has_meta('move_tween'): + if character_node.get_meta('move_tween').is_running(): + time = character_node.get_meta('move_time')-character_node.get_meta('move_tween').get_total_elapsed_time() + tween = character_node.get_meta('move_tween') + if time == 0: + character_node.position = transform.position + portrait_node.position = character.offset + portrait_info.get('offset', Vector2()) + portrait_node.scale = transform.size + else: + if !tween: + tween = character_node.create_tween().set_parallel().set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_SINE) + character_node.set_meta('move_tween', tween) + character_node.set_meta('move_time', time) + tween.tween_property(character_node, 'position', transform.position, time) + tween.tween_property(portrait_node, 'position',character.offset + portrait_info.get('offset', Vector2()), time) + tween.tween_property(portrait_node, 'scale', transform.size, time) + + +## Animates the portrait in the given container with the given animation. +func _animate_portrait(character_node:Node2D, animation_path:String, length:float, repeats = 1) -> DialogicAnimation: + if character_node.has_meta('animation_node') and is_instance_valid(character_node.get_meta('animation_node')): + character_node.get_meta('animation_node').queue_free() + + var anim_script :Script = load(animation_path) + var anim_node := Node.new() + anim_node.set_script(anim_script) + anim_node = (anim_node as DialogicAnimation) + anim_node.node = character_node + anim_node.orig_pos = character_node.position + anim_node.end_position = character_node.position + anim_node.time = length + anim_node.repeats = repeats + add_child(anim_node) + anim_node.animate() + character_node.set_meta('animation_node', anim_node) + + return anim_node + + +## Moves the given portrait to the given container. +func _move_portrait(character_node:Node2D, portrait_container:DialogicNode_PortraitContainer, time:float = 0.0) -> void: + + var global_pos := character_node.global_position + if character_node.get_parent(): character_node.get_parent().remove_child(character_node) + + portrait_container.add_child(character_node) + + character_node.position = global_pos-character_node.get_parent().global_position + _update_portrait_transform(character_node, time) + + +## Changes the given portraits z_index. +func _change_portrait_z_index(character_node:Node2D, z_index:int, update_zindex:= true) -> void: + if update_zindex: + character_node.z_index = z_index + + +func _remove_portrait(character_node:Node2D) -> void: + character_node.get_parent().remove_child(character_node) + character_node.queue_free() + + +################### Character Methods ############################################################# +#################################################################################################### +## The following methods are used to manage character portraits with the following rules: +## - a character can only be present once with these methods. +## Most of them will fail silently if the character isn't joined yet. + + +## Adds a character at a position and sets it's portrait. +## If the character is already joined it will only update, portrait, position, etc. +func join_character(character:DialogicCharacter, portrait:String, position_idx:int, mirrored: bool = false, z_index: int = 0, extra_data:String = "", animation_name:String = "", animation_length:float = 0, animation_wait := false) -> Node: + if is_character_joined(character): + change_character_portrait(character, portrait) + if animation_name.is_empty(): + animation_length = ProjectSettings.get_setting('dialogic/animations/join_default_length', 0.5) + if animation_wait: + dialogic.current_state = Dialogic.States.ANIMATING + await get_tree().create_timer(animation_length).timeout + dialogic.current_state = Dialogic.States.IDLE + move_character(character, position_idx, animation_length) + change_character_mirror(character, mirrored) + return + + var character_node := add_character(character, portrait, position_idx) + if character_node == null: + return null + + dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_index':position_idx, 'custom_mirror':mirrored} + + _change_portrait_mirror(character_node, mirrored) + _change_portrait_extradata(character_node, extra_data) + _change_portrait_z_index(character_node, z_index) + + var info := {'character':character} + info.merge(dialogic.current_state_info['portraits'][character.resource_path]) + character_joined.emit(info) + + if animation_name.is_empty(): + animation_name = ProjectSettings.get_setting('dialogic/animations/join_default', + get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_in_up.gd')) + animation_length = ProjectSettings.get_setting('dialogic/animations/join_default_length', 0.5) + animation_wait = ProjectSettings.get_setting('dialogic/animations/join_default_wait', true) + + + if animation_name: + var anim:DialogicAnimation = _animate_portrait(character_node, animation_name, animation_length) + + if animation_wait: + dialogic.current_state = Dialogic.States.ANIMATING + await anim.finished + dialogic.current_state = Dialogic.States.IDLE + + return character_node + + +func add_character(character:DialogicCharacter, portrait:String, position_idx:int) -> Node: + if is_character_joined(character): + printerr('[DialogicError] Cannot add a already joined character. If this is intended call _create_character_node manually.') + return null + + if portrait.is_empty(): + portrait = character.default_portrait + + if not character: + printerr('[DialogicError] Cannot call add_portrait() with null character.') + return null + + if not portrait in character.portraits: + printerr("[DialogicError] Tried joining ",character.display_name, " with not-existing portrait '", portrait, "'. Will use default portrait instead.") + portrait = character.default_portrait + if portrait.is_empty(): + printerr("[DialogicError] Character ",character.display_name, " has no default portrait to use.") + return null + + var character_node :Node = null + + for portrait_position in get_tree().get_nodes_in_group('dialogic_portrait_con_position'): + if portrait_position.is_visible_in_tree() and portrait_position.position_index == position_idx: + character_node = _create_character_node(character, portrait_position) + break + + if character_node == null: + printerr('[Dialogic] Failed to join character to position ', position_idx, ". Could not find position container.") + return null + + dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_index':position_idx} + + _change_portrait(character_node, portrait) + + return character_node + + +## Changes the portrait of a character. Only works with joined characters. +func change_character_portrait(character:DialogicCharacter, portrait:String, update_transform:=true) -> void: + if !is_character_joined(character): + return + + if dialogic.current_state_info.portraits[character.resource_path].portrait == portrait: + return + + var info := _change_portrait(dialogic.current_state_info.portraits[character.resource_path].node, portrait, update_transform) + dialogic.current_state_info.portraits[character.resource_path].portrait = info.portrait + if dialogic.current_state_info.portraits[character.resource_path].get('custom_mirror', false): + _change_portrait_mirror(dialogic.current_state_info.portraits[character.resource_path].node, true) + character_portrait_changed.emit(info) + + +## Changes the mirror of the given character. Only works with joined characters +func change_character_mirror(character:DialogicCharacter, mirrored:= false, force:= false) -> void: + if !is_character_joined(character): + return + + _change_portrait_mirror(dialogic.current_state_info.portraits[character.resource_path].node, mirrored, force) + dialogic.current_state_info.portraits[character.resource_path]['custom_mirror'] = mirrored + + +## Changes the z_index of a character. Only works with joined characters +func change_character_z_index(character:DialogicCharacter, z_index:int, update_zindex:= true) -> void: + if !is_character_joined(character): + return + _change_portrait_z_index(dialogic.current_state_info.portraits[character.resource_path].node, z_index, update_zindex) + + +## Changes the extra data on the given character. Only works with joined characters +func change_character_extradata(character:DialogicCharacter, extra_data:="") -> void: + if !is_character_joined(character): + return + _change_portrait_extradata(dialogic.current_state_info.portraits[character.resource_path].node, extra_data) + + +## Starts the given animation on the given character. Only works with joined characters +func animate_character(character:DialogicCharacter, animation_path:String, length:float, repeats = 1) -> DialogicAnimation: + if !is_character_joined(character): + return null + return _animate_portrait(dialogic.current_state_info.portraits[character.resource_path].node, animation_path, length, repeats) + + +## Moves the given character to the given position. Only works with joined characters +func move_character(character:DialogicCharacter, position_idx:int, time:float = 0.0) -> void: + if !is_character_joined(character): + return + + if dialogic.current_state_info.portraits[character.resource_path].position_index == position_idx: + return + + for portrait_position in get_tree().get_nodes_in_group('dialogic_portrait_con_position'): + if portrait_position.is_visible_in_tree() and portrait_position.position_index == position_idx: + _move_portrait(dialogic.current_state_info.portraits[character.resource_path].node, portrait_position, time) + dialogic.current_state_info.portraits[character.resource_path].position_index = position_idx + character_moved.emit({'character':character, 'position_index':position_idx, 'time':time}) + return + + printerr('[Dialogic] Unable to move character to position ', position_idx, ". Couldn't find position container.") + + +## Removes a character with a given animation or the default animation. +func leave_character(character:DialogicCharacter, animation_name :String = "", animation_length:float = 0, animation_wait := false) -> void: + if !is_character_joined(character): + return + + if animation_name.is_empty(): + animation_name = ProjectSettings.get_setting('dialogic/animations/leave_default', + get_script().resource_path.get_base_dir().path_join('DefaultAnimations/fade_out_down.gd')) + animation_length = ProjectSettings.get_setting('dialogic/animations/leave_default_length', 0.5) + animation_wait = ProjectSettings.get_setting('dialogic/animations/leave_default_wait', true) + + if !animation_name.is_empty(): + var anim := animate_character(character, animation_name, animation_length) + + anim.finished.connect(remove_character.bind(character)) + + if animation_wait: + dialogic.current_state = Dialogic.States.ANIMATING + await anim.finished + dialogic.current_state = Dialogic.States.IDLE + else: + remove_character(character) + + +## Removes all joined characters with a given animation or the default animation. +func leave_all_characters(animation_name:String="", animation_length:float= 0, animation_wait:= false) -> void: + for character in get_joined_characters(): + leave_character(character, animation_name, animation_length, false) + + if animation_name.is_empty(): + animation_length = ProjectSettings.get_setting('dialogic/animations/leave_default_length', 0.5) + animation_wait = ProjectSettings.get_setting('dialogic/animations/leave_default_wait', true) + + if animation_wait: + dialogic.current_state = Dialogic.States.ANIMATING + await get_tree().create_timer(animation_length).timeout + dialogic.current_state = Dialogic.States.IDLE + + +## Removes the given characters portrait. Only works with joined characters +func remove_character(character:DialogicCharacter) -> void: + if !is_character_joined(character): + return + if dialogic.current_state_info['portraits'][character.resource_path].node is Node: + _remove_portrait(dialogic.current_state_info['portraits'][character.resource_path].node) + character_left.emit({'character':character}) + dialogic.current_state_info['portraits'].erase(character.resource_path) + + +## Returns true if the given character is currently joined. +func is_character_joined(character:DialogicCharacter) -> bool: + return character.resource_path in dialogic.current_state_info['portraits'] + + +## Returns a list of the joined charcters (as resources) +func get_joined_characters() -> Array[DialogicCharacter]: + var chars :Array[DialogicCharacter]= [] + for char_path in dialogic.current_state_info.get('portraits', {}).keys(): + chars.append(load(char_path)) + return chars + + +## Returns a dictionary with info on a given character. +## Keys can be [joined, character, node (for the portrait node), position_index] +## Only joined is included (and false) for not joined characters +func get_character_info(character:DialogicCharacter) -> Dictionary: + if is_character_joined(character): + var info :Dictionary = dialogic.current_state_info['portraits'][character.resource_path] + info['joined'] = true + return info + else: + return {'joined':false} + + + +################### Positions ##################################################################### +#################################################################################################### + +## Creates a new portrait container node. +## It will copy it's size and most settings from the first p_container in the tree. +## It will be added as a sibling of the first p_container in the tree. +func add_portrait_position(position_index: int, position:Vector2) -> void: + var example_position := get_tree().get_first_node_in_group('dialogic_portrait_con_position') + if example_position: + var new_position := DialogicNode_PortraitContainer.new() + example_position.get_parent().add_child(new_position) + new_position.size = example_position.size + new_position.size_mode = example_position.size_mode + new_position.origin_anchor = example_position.origin_anchor + new_position.position_index = position_index + new_position.position = position-new_position._get_origin_position() + position_changed.emit({'change':'added', 'container_node':new_position, 'position_index':position_index}) + + +func move_portrait_position(position_index: int, vector:Vector2, relative:bool = false, time:float = 0.0) -> void: + for portrait_container in get_tree().get_nodes_in_group('dialogic_portrait_con_position'): + if portrait_container.is_visible_in_tree() and portrait_container.position_index == position_index: + if !portrait_container.has_meta('default_position'): + portrait_container.set_meta('default_position', portrait_container.position) + var tween := portrait_container.create_tween() + if !relative: + tween.tween_property(portrait_container, 'position', vector, time) + else: + tween.tween_property(portrait_container, 'position', vector, time).as_relative() + position_changed.emit({'change':'moved', 'container_node':portrait_container, 'position_index':position_index}) + return + + # If this is reached, no position could be found. If the position is absolute, we will add it. + if !relative: + add_portrait_position(position_index, vector) + + +func reset_portrait_positions(time:float = 0.0) -> void: + for portrait_position in get_tree().get_nodes_in_group('dialogic_portrait_con_position'): + if portrait_position.is_visible_in_tree(): + if portrait_position.has_meta('default_position'): + move_portrait_position(portrait_position.position_index, portrait_position.get_meta('default_position'), false, time) + + +func reset_portrait_position(position_index:int, time:float = 0.0) -> void: + for portrait_position in get_tree().get_nodes_in_group('dialogic_portrait_con_position'): + if portrait_position.is_visible_in_tree() and portrait_position.position_index == position_index: + if portrait_position.has_meta('default_position'): + move_portrait_position(position_index, portrait_position.get_meta('default_position'), false, time) + + + +################## SPEAKER PORTRAIT CONTAINERS ##################################################### +#################################################################################################### + +## Updates all portrait containers set to SPEAKER. +func change_speaker(speaker:DialogicCharacter= null, portrait:= ""): + for con in get_tree().get_nodes_in_group('dialogic_portrait_con_speaker'): + for character_node in con.get_children(): + if character_node.get_meta('character') != speaker: + _remove_portrait(character_node) + + if speaker == null: + continue + + if con.get_children().is_empty(): + _create_character_node(speaker, con) + elif portrait.is_empty(): + return + + if portrait.is_empty(): portrait = speaker.default_portrait + if con.portrait_prefix+portrait in speaker.portraits: + _change_portrait(con.get_child(0), con.portrait_prefix+portrait) + else: + _change_portrait(con.get_child(0), portrait) + + # if the character has no portraits _change_portrait won't actually add a child node + if con.get_child(0).get_child_count() == 0: + return + + _change_portrait_mirror(con.get_child(0)) + + +################### TEXT EFFECTS ################################################################### +#################################################################################################### + +## Called from the [portrait=something] text effect. +func text_effect_portrait(text_node:Control, skipped:bool, argument:String) -> void: + if argument: + if Dialogic.current_state_info.get('character', null): + change_character_portrait(load(Dialogic.current_state_info.character), argument) + + +################### HELPERS ######################################################################## +#################################################################################################### + +## Returns a character resource based on the name +func get_character_resource(character_name:String) -> DialogicCharacter: + if Dialogic.character_directory.has(character_name): + return Dialogic.character_directory[character_name].resource + else: + for key in Dialogic.character_directory.keys(): + if Dialogic.character_directory[key].unique_short_path == character_name: + return Dialogic.character_directory[key].resource + + var path :String = DialogicUtil.guess_resource('.dch', character_name) + if ResourceLoader.exists(path): + return load(path) + return null diff --git a/addons/dialogic/Modules/Choice/event_choice.gd b/addons/dialogic/Modules/Choice/event_choice.gd new file mode 100644 index 0000000000000000000000000000000000000000..eca97646dc67b3252203fa04c5e00fd209810abc --- /dev/null +++ b/addons/dialogic/Modules/Choice/event_choice.gd @@ -0,0 +1,195 @@ +@tool +class_name DialogicChoiceEvent +extends DialogicEvent + +## Event that allows adding choices. Needs to go after a text event (or another choices EndBranch). + +enum ElseActions {HIDE, DISABLE, DEFAULT} + + +### Settings +## The text that is displayed on the choice button. +var text :String = "" +## If not empty this condition will determine if this choice is active. +var condition: String = "" +## Determines what happens if [condition] is false. Default will use the action set in the settings. +var else_action: = ElseActions.DEFAULT +## The text that is displayed if [condition] is false and [else_action] is Disable. +## If empty [text] will be used for disabled button as well. +var disabled_text: String = "" + + +################################################################################ +## EXECUTION +################################################################################ + +func _execute() -> void: + # This event is mostly a placeholder that's used to indicate a position. + # Only the selected choice is reached. + # However mainly the Choices Subsystem queries the events + # to find the choices that belong to the question. + if dialogic.has_subsystem('History'): + var all_choices : Array = dialogic.Choices.last_question_info['choices'] + if dialogic.has_subsystem('VAR'): + dialogic.History.store_simple_history_entry(dialogic.VAR.parse_variables(text), event_name, {'all_choices': all_choices}) + else: + dialogic.History.store_simple_history_entry(text, event_name, {'all_choices': all_choices}) + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Choice" + set_default_color('Color3') + event_category = "Flow" + event_sorting_index = 0 + can_contain_events = true + needs_parent_event = true + expand_by_default = false + + +# if needs_parent_event is true, this needs to return true if the event is that event +func is_expected_parent_event(event:DialogicEvent) -> bool: + return event is DialogicTextEvent + + +# return a control node that should show on the END BRANCH node +func get_end_branch_control() -> Control: + return load(get_script().resource_path.get_base_dir().path_join('ui_choice_end.tscn')).instantiate() + +################################################################################ +## SAVING/LOADING +################################################################################ + +func to_text() -> String: + var result_string := "" + + result_string = "- "+text.strip_edges() + if condition: + result_string += " [if "+condition+"]" + + + var shortcode = '[' + if else_action == ElseActions.HIDE: + shortcode += 'else="hide"' + elif else_action == ElseActions.DISABLE: + shortcode += 'else="disable"' + + if disabled_text: + shortcode += " alt_text="+'"'+disabled_text+'"' + + if len(shortcode) > 1: + result_string += shortcode + "]" + return result_string + + +func from_text(string:String) -> void: + var regex = RegEx.new() + regex.compile('- (?[^\\[]*)(\\[if (?[^\\]]+)])?\\s?(\\s*\\[(?.*)\\])?') + var result = regex.search(string.strip_edges()) + if result == null: + return + text = result.get_string('text') + condition = result.get_string('condition') + if result.get_string('shortcode'): + var shortcode_params = parse_shortcode_parameters(result.get_string('shortcode')) + else_action = { + 'default':ElseActions.DEFAULT, + 'hide':ElseActions.HIDE, + 'disable':ElseActions.DISABLE}.get(shortcode_params.get('else', ''), ElseActions.DEFAULT) + + disabled_text = shortcode_params.get('alt_text', '') + + +func is_valid_event(string:String) -> bool: + if string.strip_edges().begins_with("-"): + return true + return false + + +################################################################################ +## TRANSLATIONS +################################################################################ + +func _get_translatable_properties() -> Array: + return ['text', 'disabled_text'] + + +func _get_property_original_translation(property:String) -> String: + match property: + 'text': + return text + 'disabled_text': + return disabled_text + return '' + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor() -> void: + add_header_edit("text", ValueType.SINGLELINE_TEXT, '','', {'autofocus':true}) + add_body_edit("condition", ValueType.CONDITION, 'if ') + add_body_edit("else_action", ValueType.FIXED_OPTION_SELECTOR, 'else ', '', { + 'selector_options': [ + { + 'label': 'Default', + 'value': ElseActions.DEFAULT, + }, + { + 'label': 'Hide', + 'value': ElseActions.HIDE, + }, + { + 'label': 'Disable', + 'value': ElseActions.DISABLE, + } + ]}, '!condition.is_empty()') + add_body_edit("disabled_text", ValueType.SINGLELINE_TEXT, 'Disabled text:', '', + {'placeholder':'(Empty for same)'}, 'allow_alt_text()') + + +func allow_alt_text() -> bool: + return condition and ( + else_action == ElseActions.DISABLE or + (else_action == ElseActions.DEFAULT and + ProjectSettings.get_setting("dialogic/choices/def_false_behaviour", 0) == 1)) + + +####################### CODE COMPLETION ######################################## +################################################################################ + +func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: + if !'[' in line: + return + + if symbol == '[': + if line.count('[') == 1: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'if', 'if ', TextNode.syntax_highlighter.code_flow_color) + elif line.count('[') > 1: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'else', 'else="', TextNode.syntax_highlighter.code_flow_color) + if symbol == ' ' and '[else' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'alt_text', 'alt_text="', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5)) + elif symbol == '{': + CodeCompletionHelper.suggest_variables(TextNode) + if (symbol == '=' or symbol == '"') and line.count('[') > 1 and !'" ' in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'default', "default", event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5), null, '"') + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'hide', "hide", event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5), null, '"') + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'disable', "disable", event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5), null, '"') + + +#################### SYNTAX HIGHLIGHTING ####################################### +################################################################################ + +func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: + dict[0] = {'color':event_color} + if '[' in line: + dict[line.find('[')] = {"color":Highlighter.normal_color} + dict = Highlighter.color_word(dict, Highlighter.code_flow_color, line, 'if', line.find('['), line.find(']')) + dict = Highlighter.color_condition(dict, line, line.find('['), line.find(']')) + dict = Highlighter.color_shortcode_content(dict, line, line.find(']'), 0,event_color) + return dict diff --git a/addons/dialogic/Modules/Choice/icon.svg b/addons/dialogic/Modules/Choice/icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..7a17ed965a24283626977a9c35ca9f01a4575c45 --- /dev/null +++ b/addons/dialogic/Modules/Choice/icon.svg @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/dialogic/Modules/Choice/icon.svg.import b/addons/dialogic/Modules/Choice/icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..39536fe0ff66da67a2233dd99984d07ccd0317c4 --- /dev/null +++ b/addons/dialogic/Modules/Choice/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wd4fnxjfmj5m" +path="res://.godot/imported/icon.svg-372a9bfce8bea67fff1988d7acf09a9a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Choice/icon.svg" +dest_files=["res://.godot/imported/icon.svg-372a9bfce8bea67fff1988d7acf09a9a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/dialogic/Modules/Choice/index.gd b/addons/dialogic/Modules/Choice/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..23d0ec2da7d6ce0bb3d0c2c261276600bf950562 --- /dev/null +++ b/addons/dialogic/Modules/Choice/index.gd @@ -0,0 +1,14 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_choice.gd')] + + +func _get_subsystems() -> Array: + return [{'name':'Choices', 'script':this_folder.path_join('subsystem_choices.gd')}] + + +func _get_settings_pages() -> Array: + return [this_folder.path_join('settings_choices.tscn')] diff --git a/addons/dialogic/Modules/Choice/node_button_sound.gd b/addons/dialogic/Modules/Choice/node_button_sound.gd new file mode 100644 index 0000000000000000000000000000000000000000..e6d7e521765a5dcea3fbc1be76f4084ae01c40ce --- /dev/null +++ b/addons/dialogic/Modules/Choice/node_button_sound.gd @@ -0,0 +1,52 @@ +class_name DialogicNode_ButtonSound +extends AudioStreamPlayer + +## Node that is used for playing sound effects on hover/focus/press of sibling DialogicNode_ChoiceButtons. + +## Sound to be played if one of the sibling ChoiceButtons is pressed. +## If sibling ChoiceButton has a sound_pressed set, that is prioritized. +@export var sound_pressed:AudioStream +## Sound to be played on hover. See [sound_pressed] for more. +@export var sound_hover:AudioStream +## Sound to be played on focus. See [sound_pressed] for more. +@export var sound_focus:AudioStream + +func _ready(): + add_to_group('dialogic_button_sound') + _connect_all_buttons() + +#basic play sound +func play_sound(sound) -> void: + if sound != null: + stream = sound + play() + +func _connect_all_buttons(): + for child in get_parent().get_children(): + if child is DialogicNode_ChoiceButton: + child.button_up.connect(_on_pressed.bind(child.sound_pressed)) + child.mouse_entered.connect(_on_hover.bind(child.sound_hover)) + child.focus_entered.connect(_on_focus.bind(child.sound_focus)) + + +#the custom_sound argument comes from the specifec button and get used +#if none are found, it uses the above sounds + +func _on_pressed(custom_sound) -> void: + if custom_sound != null: + play_sound(custom_sound) + else: + play_sound(sound_pressed) + +func _on_hover(custom_sound) -> void: + if custom_sound != null: + play_sound(custom_sound) + else: + play_sound(sound_hover) + +func _on_focus(custom_sound) -> void: + if custom_sound != null: + play_sound(custom_sound) + else: + play_sound(sound_focus) + diff --git a/addons/dialogic/Modules/Choice/node_choice_button.gd b/addons/dialogic/Modules/Choice/node_choice_button.gd new file mode 100644 index 0000000000000000000000000000000000000000..b05ee77583cc5824bac645f25f26579ecf6d0311 --- /dev/null +++ b/addons/dialogic/Modules/Choice/node_choice_button.gd @@ -0,0 +1,20 @@ +class_name DialogicNode_ChoiceButton +extends Button + +## Dialogic Node that displays choices. + +## Used to identify what choices to put on. If you leave it at -1, choices will be distributed automatically. +@export var choice_index:int = -1 + +## Can be set to play this sound when pressed. Requires a sibling DialogicNode_ButtonSound node. +@export var sound_pressed: AudioStream +## Can be set to play this sound when hovered. Requires a sibling DialogicNode_ButtonSound node. +@export var sound_hover: AudioStream +## Can be set to play this sound when focused. Requires a sibling DialogicNode_ButtonSound node. +@export var sound_focus: AudioStream + + +func _ready(): + add_to_group('dialogic_choice_button') + shortcut_in_tooltip = false + hide() diff --git a/addons/dialogic/Modules/Choice/settings_choices.gd b/addons/dialogic/Modules/Choice/settings_choices.gd new file mode 100644 index 0000000000000000000000000000000000000000..b40fae5b91d3046fbec5964be6bf47aa501541cd --- /dev/null +++ b/addons/dialogic/Modules/Choice/settings_choices.gd @@ -0,0 +1,67 @@ +@tool +extends DialogicSettingsPage + +func _refresh() -> void: + %Autofocus.button_pressed = ProjectSettings.get_setting('dialogic/choices/autofocus_first', true) + %Delay.value = ProjectSettings.get_setting('dialogic/choices/delay', 0.2) + %FalseBehaviour.select(ProjectSettings.get_setting('dialogic/choices/def_false_behaviour', 0)) + %HotkeyType.select(ProjectSettings.get_setting('dialogic/choices/hotkey_behaviour', 0)) + + var reveal_delay :float = ProjectSettings.get_setting('dialogic/choices/reveal_delay', 0) + var reveal_by_input :bool = ProjectSettings.get_setting('dialogic/choices/reveal_by_input', false) + if not reveal_by_input and reveal_delay == 0: + _on_appear_mode_item_selected(0) + if not reveal_by_input and reveal_delay != 0: + _on_appear_mode_item_selected(1) + if reveal_by_input and reveal_delay == 0: + _on_appear_mode_item_selected(2) + if reveal_by_input and reveal_delay != 0: + _on_appear_mode_item_selected(3) + + %RevealDelay.value = reveal_delay + +func _on_Autofocus_toggled(button_pressed: bool) -> void: + ProjectSettings.set_setting('dialogic/choices/autofocus_first', button_pressed) + ProjectSettings.save() + + +func _on_FalseBehaviour_item_selected(index) -> void: + ProjectSettings.set_setting('dialogic/choices/def_false_behaviour', index) + ProjectSettings.save() + + +func _on_HotkeyType_item_selected(index) -> void: + ProjectSettings.set_setting('dialogic/choices/hotkey_behaviour', index) + ProjectSettings.save() + + +func _on_Delay_value_changed(value) -> void: + ProjectSettings.set_setting('dialogic/choices/delay', value) + ProjectSettings.save() + + +func _on_reveal_delay_value_changed(value) -> void: + ProjectSettings.set_setting('dialogic/choices/reveal_delay', value) + ProjectSettings.save() + + +func _on_appear_mode_item_selected(index:int) -> void: + %AppearMode.selected = index + match index: + 0: + ProjectSettings.set_setting('dialogic/choices/reveal_delay', 0) + ProjectSettings.set_setting('dialogic/choices/reveal_by_input', false) + %RevealDelay.hide() + 1: + ProjectSettings.set_setting('dialogic/choices/reveal_delay', %RevealDelay.value) + ProjectSettings.set_setting('dialogic/choices/reveal_by_input', false) + %RevealDelay.show() + 2: + ProjectSettings.set_setting('dialogic/choices/reveal_delay', 0) + ProjectSettings.set_setting('dialogic/choices/reveal_by_input', true) + %RevealDelay.hide() + 3: + ProjectSettings.set_setting('dialogic/choices/reveal_delay', %RevealDelay.value) + ProjectSettings.set_setting('dialogic/choices/reveal_by_input', true) + %RevealDelay.show() + ProjectSettings.save() diff --git a/addons/dialogic/Modules/Choice/settings_choices.tscn b/addons/dialogic/Modules/Choice/settings_choices.tscn new file mode 100644 index 0000000000000000000000000000000000000000..95957ac8e5c9d74afd03f391ebbc2ed1da8013e4 --- /dev/null +++ b/addons/dialogic/Modules/Choice/settings_choices.tscn @@ -0,0 +1,176 @@ +[gd_scene load_steps=5 format=3 uid="uid://uarvgnbrcltm"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Choice/settings_choices.gd" id="2"] +[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_nxutt"] + +[sub_resource type="Image" id="Image_2imc3"] +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_udy8i"] +image = SubResource("Image_2imc3") + +[node name="Choices" type="VBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_bottom = -227.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2") + +[node name="VBoxContainer2" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Title" type="Label" parent="VBoxContainer2"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Behaviour" + +[node name="VBoxContainer" type="GridContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +columns = 2 + +[node name="AutofocusLabel" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/AutofocusLabel"] +layout_mode = 2 +text = "Autofocus first choice" + +[node name="Autofocus" type="CheckBox" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="AppearModeLabel" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label2" type="Label" parent="VBoxContainer/AppearModeLabel"] +layout_mode = 2 +text = "Choices appear" + +[node name="HintTooltip" parent="VBoxContainer/AppearModeLabel" instance=ExtResource("2_nxutt")] +layout_mode = 2 +tooltip_text = "Choices can appear either instantly when the text finished, after a delay, a click or either." +texture = SubResource("ImageTexture_udy8i") +hint_text = "Choices can appear either instantly when the text finished, after a delay, a click or either." + +[node name="RevealDelayLabel" type="HBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="AppearMode" type="OptionButton" parent="VBoxContainer/RevealDelayLabel"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +item_count = 4 +selected = 0 +fit_to_longest_item = false +popup/item_0/text = "Instantly" +popup/item_0/id = 0 +popup/item_1/text = "After delay" +popup/item_1/id = 1 +popup/item_2/text = "After another click" +popup/item_2/id = 2 +popup/item_3/text = "After delay or click" +popup/item_3/id = 3 + +[node name="RevealDelay" type="SpinBox" parent="VBoxContainer/RevealDelayLabel"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Delay after which choices will appear (in seconds)." +step = 0.01 + +[node name="DelayLabel" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label2" type="Label" parent="VBoxContainer/DelayLabel"] +layout_mode = 2 +text = "Delay before choices can be pressed" + +[node name="HintTooltip2" parent="VBoxContainer/DelayLabel" instance=ExtResource("2_nxutt")] +layout_mode = 2 +tooltip_text = "Adding a small delay before choices can be activated can prevent accidentally choosing an option." +texture = SubResource("ImageTexture_udy8i") +hint_text = "Adding a small delay before choices can be activated can prevent accidentally choosing an option." + +[node name="Delay" type="SpinBox" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +step = 0.01 + +[node name="DefaultFalseBehaviourLabel" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label3" type="Label" parent="VBoxContainer/DefaultFalseBehaviourLabel"] +layout_mode = 2 +text = "Default behaviour for false choices" + +[node name="HintTooltip3" parent="VBoxContainer/DefaultFalseBehaviourLabel" instance=ExtResource("2_nxutt")] +layout_mode = 2 +tooltip_text = "Define the default behaviour (hide or disable) for choices that have a condition that isn't met. + +Choices can overwrite this setting individually." +texture = SubResource("ImageTexture_udy8i") +hint_text = "Define the default behaviour (hide or disable) for choices that have a condition that isn't met. + +Choices can overwrite this setting individually." + +[node name="FalseBehaviour" type="OptionButton" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +item_count = 2 +selected = 0 +popup/item_0/text = "Hide" +popup/item_0/id = 0 +popup/item_1/text = "Disable" +popup/item_1/id = 1 + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="HotkeySelection" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Title2" type="Label" parent="HotkeySelection"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Choice Hotkeys" + +[node name="HintTooltip4" parent="HotkeySelection" instance=ExtResource("2_nxutt")] +layout_mode = 2 +tooltip_text = "You can add more complex hotkeys (or individual ones) by editing the choice buttons of your layout scene." +texture = SubResource("ImageTexture_udy8i") +hint_text = "You can add more complex hotkeys (or individual ones) by editing the choice buttons of your layout scene." + +[node name="VBoxContainer3" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label4" type="Label" parent="VBoxContainer3"] +layout_mode = 2 +text = "Hotkey type" + +[node name="HotkeyType" type="OptionButton" parent="VBoxContainer3"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +item_count = 2 +selected = 0 +popup/item_0/text = "No Hotkeys" +popup/item_0/id = 0 +popup/item_1/text = "Default (1-9)" +popup/item_1/id = 1 + +[connection signal="toggled" from="VBoxContainer/Autofocus" to="." method="_on_Autofocus_toggled"] +[connection signal="item_selected" from="VBoxContainer/RevealDelayLabel/AppearMode" to="." method="_on_appear_mode_item_selected"] +[connection signal="value_changed" from="VBoxContainer/RevealDelayLabel/RevealDelay" to="." method="_on_reveal_delay_value_changed"] +[connection signal="value_changed" from="VBoxContainer/Delay" to="." method="_on_Delay_value_changed"] +[connection signal="item_selected" from="VBoxContainer/FalseBehaviour" to="." method="_on_FalseBehaviour_item_selected"] +[connection signal="item_selected" from="VBoxContainer3/HotkeyType" to="." method="_on_HotkeyType_item_selected"] diff --git a/addons/dialogic/Modules/Choice/subsystem_choices.gd b/addons/dialogic/Modules/Choice/subsystem_choices.gd new file mode 100644 index 0000000000000000000000000000000000000000..bb0fb4d06adaa3f8403c3d95cb26dd4355a017e0 --- /dev/null +++ b/addons/dialogic/Modules/Choice/subsystem_choices.gd @@ -0,0 +1,163 @@ +extends DialogicSubsystem + +## Subsystem that manages showing and activating of choices. + +signal choice_selected(info:Dictionary) +signal choices_shown(info:Dictionary) + + +## Used to block choices from being clicked for a couple of seconds (if delay is set in settings). +var choice_blocker := Timer.new() + +var last_question_info := {} + +func _ready(): + choice_blocker.one_shot = true + DialogicUtil.update_timer_process_callback(choice_blocker) + add_child(choice_blocker) + + +#################################################################################################### +## STATE +#################################################################################################### + +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR): + hide_all_choices() + + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +## Hides all choice buttons. +func hide_all_choices() -> void: + for node in get_tree().get_nodes_in_group('dialogic_choice_button'): + node.hide() + if node.is_connected('button_up', _on_ChoiceButton_choice_selected): + node.disconnect('button_up', _on_ChoiceButton_choice_selected) + + +## Lists all current choices and shows buttons. +func show_current_choices(instant:=true) -> void: + hide_all_choices() + choice_blocker.stop() + + var reveal_delay := float(ProjectSettings.get_setting('dialogic/choices/reveal_delay', 0.0)) + var reveal_by_input :bool = ProjectSettings.get_setting('dialogic/choices/reveal_by_input', false) + + if !instant and (reveal_delay != 0 or reveal_by_input): + if reveal_delay != 0: + choice_blocker.start(reveal_delay) + choice_blocker.timeout.connect(show_current_choices) + if reveal_by_input: + Dialogic.Text.input_handler.dialogic_action.connect(show_current_choices) + return + + if choice_blocker.timeout.is_connected(show_current_choices): + choice_blocker.timeout.disconnect(show_current_choices) + if Dialogic.Text.input_handler.dialogic_action.is_connected(show_current_choices): + Dialogic.Text.input_handler.dialogic_action.disconnect(show_current_choices) + + + var button_idx := 1 + last_question_info = {'choices':[]} + for choice_index in get_current_choice_indexes(): + var choice_event :DialogicEvent= dialogic.current_timeline_events[choice_index] + # check if condition is false + if not choice_event.condition.is_empty() and not dialogic.Expression.execute_condition(choice_event.condition): + if choice_event.else_action == DialogicChoiceEvent.ElseActions.DEFAULT: + choice_event.else_action = ProjectSettings.get_setting('dialogic/choices/def_false_behaviour', 0) + + # check what to do in this case + if choice_event.else_action == DialogicChoiceEvent.ElseActions.DISABLE: + if !choice_event.disabled_text.is_empty(): + show_choice(button_idx, choice_event.get_property_translated('disabled_text'), false, choice_index) + last_question_info['choices'].append(choice_event.get_property_translated('disabled_text')+'#disabled') + else: + show_choice(button_idx, choice_event.get_property_translated('text'), false, choice_index) + last_question_info['choices'].append(choice_event.get_property_translated('text')+'#disabled') + button_idx += 1 + # else just show it + else: + show_choice(button_idx, choice_event.get_property_translated('text'), true, choice_index) + last_question_info['choices'].append(choice_event.get_property_translated('text')) + button_idx += 1 + choices_shown.emit(last_question_info) + choice_blocker.start(float(ProjectSettings.get_setting('dialogic/choices/delay', 0.2))) + + +## Adds a button with the given text that leads to the given event. +func show_choice(button_index:int, text:String, enabled:bool, event_index:int) -> void: + var idx := 1 + for node in get_tree().get_nodes_in_group('dialogic_choice_button'): + if !node.get_parent().is_visible_in_tree(): + continue + if (node.choice_index == button_index) or (idx == button_index and node.choice_index == -1): + node.show() + if dialogic.has_subsystem('VAR'): + node.text = dialogic.VAR.parse_variables(text) + else: + node.text = text + + if idx == 1 and ProjectSettings.get_setting('dialogic/choices/autofocus_first', true): + node.grab_focus() + + if ProjectSettings.get_setting('dialogic/choices/hotkey_behaviour', 0) == 1 and idx < 10: + var shortcut := Shortcut.new() + var input_key := InputEventKey.new() + input_key.scancode = OS.find_keycode_from_string(str(idx)) + shortcut.shortcut = input_key + node.shortcut = shortcut + + node.disabled = not enabled + node.button_up.connect(_on_ChoiceButton_choice_selected.bind(event_index, + {'button_index':button_index, 'text':text, 'enabled':enabled, 'event_index':event_index})) + + if node.choice_index > 0: + idx = node.choice_index + idx += 1 + + +func _on_ChoiceButton_choice_selected(event_index:int, choice_info:={}) -> void: + if Dialogic.paused or not choice_blocker.is_stopped(): + return + choice_selected.emit(choice_info) + hide_all_choices() + dialogic.current_state = dialogic.States.IDLE + dialogic.handle_event(event_index) + + +func get_current_choice_indexes() -> Array: + var choices := [] + var evt_idx :int= dialogic.current_event_idx + var ignore := 0 + while true: + + evt_idx += 1 + if evt_idx >= len(dialogic.current_timeline_events): + break + if dialogic.current_timeline_events[evt_idx] is DialogicChoiceEvent: + if ignore == 0: + choices.append(evt_idx) + ignore += 1 + elif dialogic.current_timeline_events[evt_idx].can_contain_events: + ignore += 1 + else: + if ignore == 0: + break + + if dialogic.current_timeline_events[evt_idx] is DialogicEndBranchEvent: + ignore -= 1 + return choices + +#################################################################################################### +## HELPERS +#################################################################################################### + +func is_question(index:int) -> bool: + if dialogic.current_timeline_events[index] is DialogicTextEvent: + if len(dialogic.current_timeline_events)-1 != index: + if dialogic.current_timeline_events[index+1] is DialogicChoiceEvent: + return true + return false + diff --git a/addons/dialogic/Modules/Choice/ui_choice_end.gd b/addons/dialogic/Modules/Choice/ui_choice_end.gd new file mode 100644 index 0000000000000000000000000000000000000000..e9e97523d80b843314dd0b54283370534e08bcfd --- /dev/null +++ b/addons/dialogic/Modules/Choice/ui_choice_end.gd @@ -0,0 +1,14 @@ +@tool +extends HBoxContainer + +var parent_resource: DialogicChoiceEvent = null + +func refresh(): + if parent_resource is DialogicChoiceEvent: + show() + if len(parent_resource.text) > 12: + $Label.text = "End of choice ("+parent_resource.text.substr(0,12)+"...)" + else: + $Label.text = "End of choice ("+parent_resource.text+")" + else: + hide() diff --git a/addons/dialogic/Modules/Choice/ui_choice_end.tscn b/addons/dialogic/Modules/Choice/ui_choice_end.tscn new file mode 100644 index 0000000000000000000000000000000000000000..bd9b0471f1995395d21f05fcfcc2dd0607275217 --- /dev/null +++ b/addons/dialogic/Modules/Choice/ui_choice_end.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Choice/ui_choice_end.gd" id="1_7qd85"] + +[node name="Choice_End" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1_7qd85") + +[node name="Label" type="Label" parent="."] +offset_top = 288.0 +offset_right = 1.0 +offset_bottom = 311.0 diff --git a/addons/dialogic/Modules/Comment/event_comment.gd b/addons/dialogic/Modules/Comment/event_comment.gd new file mode 100644 index 0000000000000000000000000000000000000000..25cbf31f6a75f562ff4705049671de86939d2e09 --- /dev/null +++ b/addons/dialogic/Modules/Comment/event_comment.gd @@ -0,0 +1,67 @@ +@tool +class_name DialogicCommentEvent +extends DialogicEvent + +## Event that does nothing but store a comment string. Will print the comment in debug builds. + + +### Settings + +## Content of the comment. +var text :String = "" + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + print("[Dialogic Comment] #", text) + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Comment" + set_default_color('Color9') + event_category = "Helpers" + event_sorting_index = 0 + continue_at_end = true + + +################################################################################ +## SAVING/LOADING +################################################################################ + +func to_text() -> String: + var result_string = "# "+text + return result_string + + +func from_text(string:String) -> void: + text = string.trim_prefix("# ") + + +func is_valid_event(string:String) -> bool: + if string.strip_edges().begins_with('#'): + return true + return false + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('text', ValueType.SINGLELINE_TEXT, '#','',{'autofocus':true}) + + +#################### SYNTAX HIGHLIGHTING ####################################### +################################################################################ + +func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: + dict[0] = {'color':event_color.lerp(Highlighter.normal_color, 0.3)} + return dict diff --git a/addons/dialogic/Modules/Comment/icon.png b/addons/dialogic/Modules/Comment/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..417537cff5dc04cecb198e38c923076b8890e3c8 Binary files /dev/null and b/addons/dialogic/Modules/Comment/icon.png differ diff --git a/addons/dialogic/Modules/Comment/icon.png.import b/addons/dialogic/Modules/Comment/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..42e8340ecf93b7d5484998fb95e64190c5551a0a --- /dev/null +++ b/addons/dialogic/Modules/Comment/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hbx10ru6sxfb" +path="res://.godot/imported/icon.png-fab76b8a5886dac0012cf73c317dbee4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Comment/icon.png" +dest_files=["res://.godot/imported/icon.png-fab76b8a5886dac0012cf73c317dbee4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Comment/index.gd b/addons/dialogic/Modules/Comment/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..53ceb327d0e365d25be7c1cbfdb3382456e390ec --- /dev/null +++ b/addons/dialogic/Modules/Comment/index.gd @@ -0,0 +1,6 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_comment.gd')] diff --git a/addons/dialogic/Modules/Condition/event_condition.gd b/addons/dialogic/Modules/Condition/event_condition.gd new file mode 100644 index 0000000000000000000000000000000000000000..e589633b08e5a68506e7a596a5c109953c43a3cf --- /dev/null +++ b/addons/dialogic/Modules/Condition/event_condition.gd @@ -0,0 +1,149 @@ +@tool +class_name DialogicConditionEvent +extends DialogicEvent + +## Event that allows branching a timeline based on a condition. + +enum ConditionTypes {IF, ELIF, ELSE} + +### Settings +## condition type (see [ConditionTypes]). Defaults to if. +var condition_type := ConditionTypes.IF +## The condition as a string. Will be executed as an Expression. +var condition: String = "" + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + if condition_type == ConditionTypes.ELSE: + finish() + return + + if condition.is_empty(): condition = "true" + + var result :bool= dialogic.Expression.execute_condition(condition) + if not result: + var idx :int= dialogic.current_event_idx + var ignore := 1 + while true: + idx += 1 + if not dialogic.current_timeline.get_event(idx) or ignore == 0: + break + elif dialogic.current_timeline.get_event(idx).can_contain_events: + ignore += 1 + elif dialogic.current_timeline.get_event(idx) is DialogicEndBranchEvent: + ignore -= 1 + + dialogic.current_event_idx = idx-1 + finish() + + +## only called if the previous event was an end-branch event +## return true if this event should be executed if the previous event was an end-branch event +func should_execute_this_branch() -> bool: + return condition_type == ConditionTypes.IF + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Condition" + set_default_color('Color3') + event_category = "Flow" + event_sorting_index = 1 + can_contain_events = true + continue_at_end = true + + +# return a control node that should show on the END BRANCH node +func get_end_branch_control() -> Control: + return load(get_script().resource_path.get_base_dir().path_join('ui_condition_end.tscn')).instantiate() + +################################################################################ +## SAVING/LOADING +################################################################################ + +func to_text() -> String: + var result_string := "" + + match condition_type: + ConditionTypes.IF: + result_string = 'if '+condition+':' + ConditionTypes.ELIF: + result_string = 'elif '+condition+':' + ConditionTypes.ELSE: + result_string = 'else:' + + return result_string + + +func from_text(string:String) -> void: + if string.strip_edges().begins_with('if'): + condition = string.strip_edges().trim_prefix('if ').trim_suffix(':').strip_edges() + condition_type = ConditionTypes.IF + elif string.strip_edges().begins_with('elif'): + condition = string.strip_edges().trim_prefix('elif ').trim_suffix(':').strip_edges() + condition_type = ConditionTypes.ELIF + elif string.strip_edges().begins_with('else'): + condition = "" + condition_type = ConditionTypes.ELSE + + +func is_valid_event(string:String) -> bool: + if string.strip_edges() in ['if', 'elif', 'else'] or (string.strip_edges().begins_with('if ') or string.strip_edges().begins_with('elif ') or string.strip_edges().begins_with('else')): + return true + return false + + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('condition_type', ValueType.FIXED_OPTION_SELECTOR, '', '', { + 'selector_options': [ + { + 'label': 'IF', + 'value': ConditionTypes.IF, + }, + { + 'label': 'ELIF', + 'value': ConditionTypes.ELIF, + }, + { + 'label': 'ELSE', + 'value': ConditionTypes.ELSE, + } + ], 'disabled':true}) + add_header_edit('condition', ValueType.CONDITION, '', '', {}, 'condition_type != %s'%ConditionTypes.ELSE) + + +####################### CODE COMPLETION ######################################## +################################################################################ + +func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: + if (line.begins_with('if') or line.begins_with('elif')) and symbol == '{': + CodeCompletionHelper.suggest_variables(TextNode) + + +func _get_start_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit) -> void: + TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'if', 'if ', TextNode.syntax_highlighter.code_flow_color) + TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'elif', 'elif ', TextNode.syntax_highlighter.code_flow_color) + TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'else', 'else:\n ', TextNode.syntax_highlighter.code_flow_color) + + +#################### SYNTAX HIGHLIGHTING ####################################### +################################################################################ + + +func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: + var word := line.get_slice(' ', 0) + dict[line.find(word)] = {"color":Highlighter.code_flow_color} + dict[line.find(word)+len(word)] = {"color":Highlighter.normal_color} + dict = Highlighter.color_condition(dict, line) + return dict diff --git a/addons/dialogic/Modules/Condition/icon.svg b/addons/dialogic/Modules/Condition/icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..c09290c112ec0695771334f742af0b932daedefa --- /dev/null +++ b/addons/dialogic/Modules/Condition/icon.svg @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/dialogic/Modules/Condition/icon.svg.import b/addons/dialogic/Modules/Condition/icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..b2b8d62b3053b08200af99d2b5a07150902f109d --- /dev/null +++ b/addons/dialogic/Modules/Condition/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bco4t1ey0fkpm" +path="res://.godot/imported/icon.svg-502d45c064cc30f576b7b105a01357ce.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Condition/icon.svg" +dest_files=["res://.godot/imported/icon.svg-502d45c064cc30f576b7b105a01357ce.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/dialogic/Modules/Condition/index.gd b/addons/dialogic/Modules/Condition/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..c84f89711ed6aaf5406e7f981f0b75ebb7a8ca14 --- /dev/null +++ b/addons/dialogic/Modules/Condition/index.gd @@ -0,0 +1,6 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_condition.gd')] diff --git a/addons/dialogic/Modules/Condition/ui_condition_end.gd b/addons/dialogic/Modules/Condition/ui_condition_end.gd new file mode 100644 index 0000000000000000000000000000000000000000..989f137a231a28b6cc4b64d5ea8351d540133231 --- /dev/null +++ b/addons/dialogic/Modules/Condition/ui_condition_end.gd @@ -0,0 +1,45 @@ +@tool +extends HBoxContainer + +var parent_resource = null + +func _ready(): + $AddElif.button_up.connect(add_elif) + $AddElse.button_up.connect(add_else) + +func refresh(): + if parent_resource is DialogicConditionEvent: + # hide add elif and add else button on ELSE event + $AddElif.visible = parent_resource.condition_type != DialogicConditionEvent.ConditionTypes.ELSE + $AddElse.visible = parent_resource.condition_type != DialogicConditionEvent.ConditionTypes.ELSE + $Label.text = "End of "+["IF", "ELIF", "ELSE"][parent_resource.condition_type]+" ("+parent_resource.condition+")" + + # hide add add else button if followed by ELIF or ELSE event + var timeline_editor = find_parent('VisualEditor') + if timeline_editor: + var next_event = null + if timeline_editor.get_block_below(get_parent()): + next_event = timeline_editor.get_block_below(get_parent()).resource + if next_event is DialogicConditionEvent: + if next_event.condition_type != DialogicConditionEvent.ConditionTypes.IF: + $AddElse.hide() + if parent_resource.condition_type == DialogicConditionEvent.ConditionTypes.ELSE: + $Label.text = "End of ELSE" + else: + hide() + +func add_elif(): + var timeline = find_parent('VisualEditor') + if timeline: + var resource = DialogicConditionEvent.new() + resource.condition_type = DialogicConditionEvent.ConditionTypes.ELIF + timeline.add_event_with_end_branch(resource, get_parent().get_index()+1) + timeline.indent_events() + +func add_else(): + var timeline = find_parent('VisualEditor') + if timeline: + var resource = DialogicConditionEvent.new() + resource.condition_type = DialogicConditionEvent.ConditionTypes.ELSE + timeline.add_event_with_end_branch(resource, get_parent().get_index()+1) + timeline.indent_events() diff --git a/addons/dialogic/Modules/Condition/ui_condition_end.tscn b/addons/dialogic/Modules/Condition/ui_condition_end.tscn new file mode 100644 index 0000000000000000000000000000000000000000..02ced22b4d7662cde9d0335f21ef104fa9af739a --- /dev/null +++ b/addons/dialogic/Modules/Condition/ui_condition_end.tscn @@ -0,0 +1,26 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Condition/ui_condition_end.gd" id="1_sh52m"] + +[node name="Condition_End" type="HBoxContainer"] +offset_right = 90.0 +offset_bottom = 23.0 +script = ExtResource("1_sh52m") + +[node name="Label" type="Label" parent="."] +offset_top = 2.0 +offset_right = 141.0 +offset_bottom = 28.0 +text = "End of condition X" + +[node name="AddElif" type="Button" parent="."] +offset_left = 145.0 +offset_right = 212.0 +offset_bottom = 31.0 +text = "Add Elif" + +[node name="AddElse" type="Button" parent="."] +offset_left = 216.0 +offset_right = 290.0 +offset_bottom = 31.0 +text = "Add Else" diff --git a/addons/dialogic/Modules/Converter/custom_event_converter.gd b/addons/dialogic/Modules/Converter/custom_event_converter.gd new file mode 100644 index 0000000000000000000000000000000000000000..c3395f1e0f23672da8265da06a5f0fb3beba9975 --- /dev/null +++ b/addons/dialogic/Modules/Converter/custom_event_converter.gd @@ -0,0 +1,18 @@ +extends Node +class_name CustomEventConverter + +# This is an extension to the Converter to allow for converting custom scenes +# This will take a 1.x event node with a custom scene, and let you convert it to the new format +# One type is provided for example. If you wish to convert them into a new format, add it here as another Match case +# Otherwise, it will just create it as a commment with the parameters in it for you to do in-editor + + +static func convertCustomEvent(event): + var returnString = "" + + match event['event_id']: + "comment_001": + # Example node, a custom event to simply store comments, as there wasn't a built-in one in 1.x + returnString += "# " + event['comment_text'] + + return returnString diff --git a/addons/dialogic/Modules/Converter/icon.png.import b/addons/dialogic/Modules/Converter/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..7b8e35aacd4bd16b52def67e9599af68fecfabc9 --- /dev/null +++ b/addons/dialogic/Modules/Converter/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d0vwotbceutf6" +path="res://.godot/imported/icon.png-1a336d53bfc8ce6e2b3ee694eb2fd062.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Events/Converter/icon.png" +dest_files=["res://.godot/imported/icon.png-1a336d53bfc8ce6e2b3ee694eb2fd062.ctex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Converter/index.gd b/addons/dialogic/Modules/Converter/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..208bd71cd3601d675cf92f0da66405327e0b4e16 --- /dev/null +++ b/addons/dialogic/Modules/Converter/index.gd @@ -0,0 +1,5 @@ +@tool +extends DialogicIndexer + +func _get_settings_pages() -> Array: + return [this_folder.path_join('settings_converter.tscn')] diff --git a/addons/dialogic/Modules/Converter/settings_converter.gd b/addons/dialogic/Modules/Converter/settings_converter.gd new file mode 100644 index 0000000000000000000000000000000000000000..aa57632da5bdc82e731a9e8393f3b09a9c851c60 --- /dev/null +++ b/addons/dialogic/Modules/Converter/settings_converter.gd @@ -0,0 +1,1041 @@ +@tool +extends DialogicSettingsPage + +var folderStructure + +var timelineFolderBreakdown:Dictionary = {} +var characterFolderBreakdown:Dictionary = {} +var definitionFolderBreakdown:Dictionary = {} +var themeFolderBreakdown:Dictionary = {} +var definitionsFile = {} +var flatDefinitionsFile = {} + +var conversionRootFolder = "res://converted-dialogic" + +var contents + +var conversionReady = false + +var varSubsystemInstalled = false +var anchorNames = {} +var prefixCharacters = false + + +func _get_title(): + return 'Converter' + +func _refresh(): + pass + +func _is_feature_tab() -> bool: + return true + +func _on_verify_pressed(): + + %OutputLog.text = "" + + if FileAccess.file_exists("res://dialogic/settings.cfg"): + %OutputLog.text += "[√] Dialogic 1.x settings data [color=green]found![/color]\r\n" + else: + %OutputLog.text += "[X] Dialogic 1.x settings data [color=red]not found![/color]\r\n" + %OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n" + return + + if FileAccess.file_exists("res://dialogic/definitions.json"): + %OutputLog.text += "[√] Dialogic 1.x definitions [color=green]found![/color]\r\n" + else: + %OutputLog.text += "[X] Dialogic 1.x definitions [color=red]not found![/color]\r\n" + %OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n" + return + + %OutputLog.text += "\r\n" + + %OutputLog.text += "Verifying data:\r\n" + var file := FileAccess.open("res://dialogic/folder_structure.json", FileAccess.READ) + var fileContent := file.get_as_text() + var json_object := JSON.new() + + var error := json_object.parse(fileContent) + + if error == OK: + folderStructure = json_object.get_data() + else: + print("JSON Parse Error: ", json_object.get_error_message(), " in ", error, " at line ", json_object.get_error_line()) + %OutputLog.text += "Dialogic 1.x folder structure [color=red]could not[/color] be read!\r\n" + %OutputLog.text += "Please check the output log for the error the JSON parser encountered.\r\n" + return + + %OutputLog.text += "Dialogic 1.x folder structure read successfully!\r\n" + + recursive_search("Timeline", folderStructure["folders"]["Timelines"], "/") + recursive_search("Character", folderStructure["folders"]["Characters"], "/") + recursive_search("Definition", folderStructure["folders"]["Definitions"], "/") + recursive_search("Theme", folderStructure["folders"]["Themes"], "/") + + + %OutputLog.text += "Timelines found: " + str(timelineFolderBreakdown.size()) + "\r\n" + %OutputLog.text += "Characters found: " + str(characterFolderBreakdown.size()) + "\r\n" + %OutputLog.text += "Definitions found: " + str(definitionFolderBreakdown.size()) + "\r\n" + %OutputLog.text += "Themes found: " + str(themeFolderBreakdown.size()) + "\r\n" + + %OutputLog.text += "\r\n" + %OutputLog.text += "Verifying count of JSON files for match with folder structure:\r\n" + + var timelinesDirectory = list_files_in_directory("res://dialogic/timelines") + if timelinesDirectory.size() == timelineFolderBreakdown.size(): + %OutputLog.text += "Timeline files found: [color=green]" + str(timelinesDirectory.size()) + "[/color]\r\n" + else: + %OutputLog.text += "Timeline files found: [color=red]" + str(timelinesDirectory.size()) + "[/color]\r\n" + %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" + + var characterDirectory = list_files_in_directory("res://dialogic/characters") + if characterDirectory.size() == characterFolderBreakdown.size(): + %OutputLog.text += "Character files found: [color=green]" + str(characterDirectory.size()) + "[/color]\r\n" + else: + %OutputLog.text += "Character files found: [color=red]" + str(characterDirectory.size()) + "[/color]\r\n" + %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" + + + + file = FileAccess.open("res://dialogic/definitions.json",FileAccess.READ) + fileContent = file.get_as_text() + json_object = JSON.new() + + error = json_object.parse(fileContent) + + if error == OK: + definitionsFile = json_object.get_data() + #print(folderStructure) + elif definitionFolderBreakdown.size() == 0: + %OutputLog.text += "[i]No definitions could be loaded, but that is probably correct as no definitions seem to exist.[/i]\n" + else: + print("JSON Parse Error: ", json_object.get_error_message(), " in ", error, " at line ", json_object.get_error_line()) + %OutputLog.text += "Dialogic 1.x definitions file [color=red]could not[/color] be read!\r\n" + %OutputLog.text += "Please check the output log for the error the JSON parser encountered.\r\n" + return + + if definitionsFile: + for variable in definitionsFile["variables"]: + var varPath = definitionFolderBreakdown[variable["id"]] + var variableInfo = {} + variableInfo["type"] = "variable" + variableInfo["path"] = varPath + variableInfo["name"] = variable["name"] + variableInfo["value"] = variable["value"] + definitionFolderBreakdown[variable["id"]] = variableInfo + + for variable in definitionsFile["glossary"]: + var varPath = definitionFolderBreakdown[variable["id"]] + var variableInfo = {} + variableInfo["type"] = "glossary" + variableInfo["path"] = varPath + variableInfo["name"] = variable["name"] + variableInfo["text"] = variable["text"] + variableInfo["title"] = variable["title"] + variableInfo["extra"] = variable["extra"] + variableInfo["glossary_type"] = variable["type"] + definitionFolderBreakdown[variable["id"]] = variableInfo + + if (definitionsFile["glossary"].size() + definitionsFile["variables"].size()) == definitionFolderBreakdown.size(): + %OutputLog.text += "Definitions found: [color=green]" + str((definitionsFile["glossary"].size() + definitionsFile["variables"].size())) + "[/color]\r\n" + %OutputLog.text += " • Glossaries found: " + str(definitionsFile["glossary"].size()) + "\r\n" + %OutputLog.text += " • Variables found: " + str(definitionsFile["variables"].size()) + "\r\n" + else: + %OutputLog.text += "Definition files found: [color=red]" + str(definitionsFile.size()) + "[/color]\r\n" + %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" + + var themeDirectory = list_files_in_directory("res://dialogic/themes") + if themeDirectory.size() == themeFolderBreakdown.size(): + %OutputLog.text += "Theme files found: [color=green]" + str(themeDirectory.size()) + "[/color]\r\n" + else: + %OutputLog.text += "Theme files found: [color=red]" + str(themeDirectory.size()) + "[/color]\r\n" + %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" + + # dirty check for the variable subsystem, as properly calling has subsystem is complicated currently + varSubsystemInstalled = file.file_exists(DialogicUtil.get_module_path('Variable').path_join("event_variable.gd")) + + if !varSubsystemInstalled: + %OutputLog.text += "[color=yellow]Variable subsystem is not present in this Dialogic! Variables will not be converted![/color]" + + %OutputLog.text += "\r\n" + + %OutputLog.text += "Initial integrity check completed!\r\n" + + if DirAccess.dir_exists_absolute(conversionRootFolder): + %OutputLog.text += "[color=yellow]Conversion folder already exists, coverting will overwrite existing files.[/color]\r\n" + else: +# %OutputLog.text += conversionRootFolder + %OutputLog.text += "[color=lightsalmon]Folders are being created in " + conversionRootFolder + ". Converted files will be located there.[/color]\r\n" + var directory = DirAccess.open("res://") + directory.make_dir(conversionRootFolder) + var sub_directory = DirAccess.open(conversionRootFolder) + sub_directory.open(conversionRootFolder) + sub_directory.make_dir("characters") + sub_directory.make_dir("timelines") + sub_directory.make_dir("themes") + + conversionReady = true + $RightPanel/Begin.disabled = false + + + +func list_files_in_directory(path): + var files = [] + var dir = DirAccess.open(path) + dir.list_dir_begin() + + while true: + var file = dir.get_next() + if file == "": + break + elif not file.begins_with("."): + if file.ends_with(".json") || file.ends_with(".cfg"): + files.append(file) + + dir.list_dir_end() + return files + +func recursive_search(currentCheck, currentDictionary, currentFolder): + for structureFile in currentDictionary["files"]: + match currentCheck: + "Timeline": timelineFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false) + "Character": characterFolderBreakdown[structureFile] = characterNameConversion(currentFolder,false) + "Definition": definitionFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false) + "Theme": themeFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false) + + for structureFolder in currentDictionary["folders"]: + recursive_search(currentCheck, currentDictionary["folders"][structureFolder], currentFolder + structureFolder + "/") + + + + + + +func _on_begin_pressed(): + %OutputLog.text += "-----------------------------------------\r\n" + %OutputLog.text += "Beginning file conversion:\r\n" + %OutputLog.text += "\r\n" + + #Variable conversion needs to be first, to build the lookup table for new style + #Character conversion needs to be before timelines, so the character names are available + convertVariables() + convertCharacters() + convertTimelines() + convertGlossaries() + convertThemes() + convertSettings() + + %OutputLog.text += "All conversions complete!\r\n" + %OutputLog.text += "\r\nPlease check to make sure your timelines all look good. After that, you can remove the /addons/dialogic/Modules/Converter folder, as it is no longer needed.\r\n\r\n" + %OutputLog.text += "Please be aware, Godot may take some time on the next project load to reload all of the Characters and Timelines. This is normal, and should only happen the one time." + + +func convertTimelines(): + %OutputLog.text += "Converting timelines: \r\n" + for item in timelineFolderBreakdown: + var folderPath = timelineFolderBreakdown[item] + %OutputLog.text += "Timeline " + folderPath + item +": " + var jsonData = {} + var file := FileAccess.open("res://dialogic/timelines/" + item, FileAccess.READ) + var fileContent = file.get_as_text() + var json_object = JSON.new() + + var error = json_object.parse(fileContent) + + if error == OK: + contents = json_object.get_data() + var fileName = contents["metadata"]["name"] + %OutputLog.text += "Name: " + fileName + ", " + str(contents["events"].size()) + " timeline events\n" + + var dir_timelines = conversionRootFolder + "/timelines" + if not DirAccess.dir_exists_absolute(dir_timelines + folderPath): + var directory = DirAccess.open(dir_timelines) + if not DirAccess.dir_exists_absolute(dir_timelines): + DirAccess.make_dir_absolute(dir_timelines) + + var progresiveDirectory = "" + for pathItem in folderPath.split('/'): + directory.open(dir_timelines + progresiveDirectory) + if pathItem!= "": + progresiveDirectory += "/" + pathItem + if !directory.dir_exists(dir_timelines + progresiveDirectory): + directory.make_dir(dir_timelines + progresiveDirectory) + + #just double check because sometimes its making double slashes at the final filename + if folderPath.right(1) == "/": + folderPath = folderPath.left(-1) + # we will save it as an intermediary file first, then on second pass cleanup make it the .dtl + var newFilePath = dir_timelines + folderPath + "/" + fileName + ".cnv" + + file = FileAccess.open(newFilePath, FileAccess.WRITE) + + # update the new location so we know where second pass items are + + timelineFolderBreakdown[item] = newFilePath + + + var processedEvents = 0 + + + var depth = [] + for event in contents["events"]: + processedEvents += 1 + var eventLine = "" + + for i in depth: + eventLine += " " + + if "dialogic_" in event["event_id"]: + match event["event_id"]: + "dialogic_001": + #Text Event + var has_character:bool = false + if event['character'] != "" && event['character']: + has_character = true + eventLine += characterFolderBreakdown[event['character']]['searchable_name'] + + if event['portrait'] != "": + eventLine += "(" + event['portrait'] + ")" + + eventLine += ": " + if '\n' in event['text']: + var splitCount = 0 + var split = event['text'].split('\n') + for splitItem in split: + if has_character == false && splitItem.find(' ') > 0 && splitItem.find(':') > 0 && (splitItem.find(' ') > splitItem.find(':')): + splitItem = splitItem.insert(splitItem.find(':'), "\\" ) + if splitCount == 0: + file.store_line(eventLine + splitItem + "\\") + else: + file.store_line(splitItem + "\\") + splitCount += 1 + else: + var text_line = variableNameConversion(event['text']) + if has_character == false && text_line.find(' ') > 0 && text_line.find(':') > 0 && (text_line.find(' ') > text_line.find(':')): + text_line = text_line.insert(text_line.find(':'), "\\" ) + file.store_string(eventLine + text_line) + "dialogic_002": + # Character event + + #For some reason this is loading as a float, and the match is failing. so hard casting as string + var eventType:String + if 'type' in event: + eventType = str(event['type']) + else: + eventType = "0" + + match eventType: + "0": + if event['character'] != "": + eventLine += "Join " + eventLine += characterFolderBreakdown[event['character']]['searchable_name'] + if (event['portrait'] != ""): + eventLine += " (" + event['portrait'] + ") " + + for i in event['position']: + if event['position'][i] == true: + #1.x uses positions 0-4, while the default 2.0 scene uses positions 1-5 + eventLine += str(i.to_int() + 1) + + if (event['animation'] != "[Default]" && event['animation'] != "") || ('z_index' in event) || ('mirror_portrait' in event): + # Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved + eventLine += " [" + if ('animation' in event && event['animation'] != "[Default]" && event['animation'] != ""): + eventLine += " [animation=\"Instant In Or Out\" " + eventLine += "length=\"" + str(event['animation_length']) + "\"" + if "animation_wait" in event: + eventLine += " wait=\"true\"" + + if 'z_index' in event: + if event['z_index'] != 0: + eventLine += ' z-index="' + str(event['z_index']) + '"' + if 'mirror_portrait' in event: + if event['mirror_portrait']: + eventLine += ' mirrored="true"' + + eventLine += "]" + + file.store_string(eventLine) + else: + eventLine += "# Character join event that did not have a selected character" + file.store_string(eventLine) + "2": + if event['character'] != "": + if event['character'] != "[All]": + + eventLine += "Update " + eventLine += characterFolderBreakdown[event['character']]['searchable_name'] + if 'portrait' in event: + if (event['portrait'] != "") && (event['portrait'] != "(Don't change)"): + eventLine += " (" + event['portrait'] + ") " + + var positionCheck = false + if 'position' in event: + for i in event['position']: + + if event['position'][i] == true: + positionCheck = true + eventLine += str(i.to_int() + 1) + + if !positionCheck: + eventLine += " 0" + + if (event['animation'] != "[Default]" && event['animation'] != "") || ('z_index' in event) || ('mirror_portrait' in event): + # Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved + eventLine += " [" + if (event['animation'] != "[Default]" && event['animation'] != ""): + eventLine += "animation=\"Heartbeat\" " + eventLine += "length=\"" + str(event['animation_length']) + "\"" + if "animation_wait" in event: + eventLine += " wait=\"true\"" + if "animation_repeat" in event: + eventLine += " repeat=\"" + str(event['animation_repeat']) + "\"" + if 'z_index' in event: if 'z_index' in event: + if event['z_index'] != 0: + eventLine += ' z-index="' + str(event['z_index']) + '"' + if 'mirror_portrait' in event: + if event['mirror_portrait']: + eventLine += ' mirrored="true"' + eventLine += "]" + + file.store_string(eventLine) + else: + file.store_string(eventLine + "# Update and Leave All not currently implemented") + else: + eventLine += "# Character Update event that did not have a selected character" + file.store_string(eventLine) + "1": + if event['character'] != "": + + eventLine += "Leave " + if event['character'] == "[All]": + eventLine += "--All--" + else: + eventLine += characterFolderBreakdown[event['character']]['searchable_name'] + + if event['animation'] != "[Default]" && event['animation'] != "": + # Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved + eventLine += " [animation=\"Instant In Or Out\" " + eventLine += "length=\"" + str(event['animation_length']) + "\"" + if "animation_wait" in event: + eventLine += " wait=\"true\"" + eventLine += "]" + file.store_string(eventLine) + else: + eventLine += " # Character Update event that did not have a selected character" + file.store_string(eventLine) + _: + file.store_string("# failed" + str(event['type'])) + + + "dialogic_010": + # Question event + # With the change in 2.0, the root of the Question block is simply text event + if event['character'] != "" && event['character']: + eventLine += characterFolderBreakdown[event['character']]['name'] + if event['portrait'] != "": + eventLine += "(" + event['portrait'] + ")" + + eventLine += ": " + if '\n' in event['question']: + var splitCount = 0 + var split = event['question'].split('\n') + for splitItem in split: + if splitCount == 0: + file.store_line(eventLine + splitItem + "\\") + else: + file.store_line(splitItem + "\\") + splitCount += 1 + else: + file.store_string(eventLine + event['question']) + + #depth.push_front("question") + + "dialogic_011": + #Choice event + + #Choice's in 1.x have depth, but they do not have matching End Nodes as Questions and Conditionals do + if depth.size() > 0: + if depth[0] == "choice": + #reset the tabs for this choice to be one tree up + + if depth.size() == 1: + eventLine = "" + else: + for i in (depth.size() - 1): + eventLine = " " + + else: + #for the next line we want to add a depth + + depth.push_front("choice") + + + eventLine += "- " + eventLine += event['choice'] + + if 'value' in event: + if event['value'] != "": + var valueLookup = variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" ) + + eventLine += " [if " + eventLine += valueLookup + if event['condition'] != "": + eventLine += " " + event['condition'] + else: + #default is true, so it may not store it + eventLine += " ==" + + # weird line due to missing type casts on String in current Godot 4 alpha + if event['value'] == str(event['value'].to_int()): + eventLine += " " + event['value'] + else: + eventLine += " \"" + event['value'] + "\"" + + eventLine += "]" + + + file.store_string(eventLine) + #print("choice node") + #print ("bracnh depth now" + str(depth)) + "dialogic_012": + #If event + var valueLookup = "broken variable" + if definitionFolderBreakdown.size(): + valueLookup = variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" ) + + eventLine += "if " + eventLine += valueLookup + if event['condition'] != "": + eventLine += " " + event['condition'] + else: + #default is true, so it may not store it + eventLine += " ==" + + # weird line due to missing type casts on String in current Godot 4 alpha + if event['value'] == str(event['value'].to_int()): + eventLine += " " + event['value'] + else: + eventLine += " \"" + event['value'] + "\"" + + eventLine += ":" + file.store_string(eventLine) + #print("if branch node") + depth.push_front("condition") + + #print ("bracnh depth now" + str(depth)) + "dialogic_013": + #End Branch event + # doesnt actually make any lines, just adjusts the tab depth + #print("end branch node") + + + var _popped = depth.pop_front() + #print ("bracnh depth now" + str(depth)) + "dialogic_014": + #Set Value event + if varSubsystemInstalled: + + + eventLine += "VAR " + if definitionFolderBreakdown.size(): + eventLine += variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" ) + else: + eventLine += "{broken_variable}" + + eventLine += " = " + + if "set_random" in event: + if event['set_random'] == true: + eventLine += "[random=\"True\"" + if "random_lower_limit" in event: + eventLine += " min=\"" + str(event['random_lower_limit']) + "\"" + if "random_upper_limit" in event: + eventLine += " max=\"" + str(event['random_upper_limit']) + "\"" + + eventLine += "]" + else: + eventLine += event['set_value'] + else: + eventLine += event['set_value'] + + file.store_string(eventLine) + else: + file.store_string(eventLine + "# Set variable function. Variables subsystem is disabled") + "dialogic_015": + #Label event + file.store_string(eventLine + "[label name=\"" + event['name'] +"\"]") + anchorNames[event['id']] = event['name'] + "dialogic_016": + #Goto event + # Dialogic 1.x only allowed jumping to labels in the same timeline + # But since it is stored as a ID reference, we will have to get it on the second pass + + file.store_string(eventLine + "[jump label=<" + event['anchor_id'] +">]") + #file.store_string(eventLine + "# jump label, just a comment for testing") + "dialogic_020": + #Change Timeline event + # we will need to come back to this one on second pass, since we may not know the new path yet + + file.store_string(eventLine + "[jump timeline=<" + event['change_timeline'] +">]") + #file.store_string(eventLine + "# jump timeline, just a comment for testing") + "dialogic_021": + #Change Background event + #in Dialogic 1.x, fade default value was 1. its now 0, so we need to always write one here + var time: float = 1.0 + if "fade_duration" in event: + time = event['fade_duration'] + file.store_string(eventLine + "[background arg=\"" + event['background'] +"\" fade=\"" + str(time) + "\"]") + "dialogic_022": + #Close Dialog event + file.store_string(eventLine + "[end_timeline]") + "dialogic_023": + #Wait event + + eventLine += "[wait time=\"" + str(event['wait_seconds']) + "\"" + if !("hide_dialogbox" in event) || (event['hide_dialogbox'] == true): + eventLine += ' hide_text="true"' + else: + eventLine += ' hide_text="false"' + eventLine += "]" + file.store_string(eventLine) + "dialogic_024": + #Change Theme event + file.store_string(eventLine + '[style name="<' + event['set_theme'] + '>"]') + "dialogic_025": + #Set Glossary event + file.store_string(eventLine + "# Set Glossary event, not currently implemented") + "dialogic_026": + #Save event + if event['use_default_slot']: + file.store_string(eventLine + "[save slot=\"Default\"]") + else: + file.store_string(eventLine + "[save slot=\"" + event['custom_slot'] + "\"]") + + "dialogic_030": + #Audio event + eventLine += "[sound" + eventLine += " path=\"" + event['file'] + "\"" + eventLine += " volume=\"" + str(event['volume']) + "\"" + eventLine += " bus=\"" + event['audio_bus'] + "\"]" + file.store_string(eventLine) + "dialogic_031": + #Background Music event + eventLine += "[music" + eventLine += " path=\"" + event['file'] + "\"" + eventLine += " volume=\"" + str(event['volume']) + "\"" + eventLine += " fade=\"" + str(event['fade_length']) + "\"" + eventLine += " bus=\"" + event['audio_bus'] + "\"" + eventLine += " loop=\"true\"]" + file.store_string(eventLine) + "dialogic_040": + #Emit Signal event + file.store_string(eventLine + "[signal arg=\"" + event['emit_signal'] +"\"]") + "dialogic_041": + #Change Scene event + file.store_string(eventLine + "# Change scene event is deprecated. Scene called was: " + event['change_scene']) + "dialogic_042": + #Call Node event + eventLine += "[call_node path=\"" + event['call_node']['target_node_path'] + "\" " + eventLine += "method=\"" + event['call_node']['method_name'] + "\" " + eventLine += "args=\"[" + for arg in event['call_node']['arguments']: + eventLine += "\"" + arg + "\", " + + #remove the last comma and space + eventLine = eventLine.left(-2) + + eventLine += "]\"]" + file.store_string(eventLine) + _: + file.store_string(eventLine + "# unimplemented Dialogic control with unknown number") + + + + + else: + var returnString = CustomEventConverter.convertCustomEvent(event) + if returnString != "": + file.store_string(eventLine + returnString) + else: + eventLine += "# Custom event: " + eventLine += str(event) + eventLine = eventLine.replace("{", "*") + eventLine = eventLine.replace("}", "*") + + file.store_string(eventLine) + + file.store_string("\r\n\r\n") + + + + %OutputLog.text += "Processed events: " + str(processedEvents) + "\r\n" + else: + %OutputLog.text += "[color=red]There was a problem parsing this file![/color]\r\n" + + %OutputLog.text += "\r\n" + + #second pass + for item in timelineFolderBreakdown: + %OutputLog.text += "Verifying file: " + timelineFolderBreakdown[item] + "\r\n" + + var oldFile = FileAccess.open(timelineFolderBreakdown[item], FileAccess.READ) + var newFile = FileAccess.open(timelineFolderBreakdown[item].replace(".cnv", ".dtl"), FileAccess.WRITE) + + var regex = RegEx.new() + regex.compile('(<.*?>)') + #var result = regex.search_all(oldText) + + + var whitespaceCount = 0 + while oldFile.get_position() < oldFile.get_length(): + var line = oldFile.get_line() + + if line.length() == 0: + #clean up any extra whitespace so theres only one line betwen each command + whitespaceCount += 1 + if whitespaceCount < 2: + newFile.store_string("\r\n\r\n") + else: + whitespaceCount = 0 + + var result = regex.search_all(line) + if result: + for res in result: + var r_string = res.get_string() + var newString = r_string.substr(1,r_string.length()-2) + + if "timeline" in line: + if newString in timelineFolderBreakdown.keys(): + newString = "\"" + timelineFolderBreakdown[newString].replace(".cnv", ".dtl") + "\"" + if "label" in line: + if newString in anchorNames.keys(): + newString = "\"" + anchorNames[newString] + "\"" + if "style" in line: + var prev = newString + var config = ConfigFile.new() + var error = config.load("res://dialogic/themes/" + newString) + if error == OK: + if config.has_section_key("settings", "name"): + newString = config.get_value("settings", "name") + + if newString == prev: + newString = "DefaultTheme" + + line = line.replace(r_string,newString) + newFile.store_string(line) + else: + newFile.store_string(line) + + oldFile = null + + var fileDirectory = timelineFolderBreakdown[item].replace(timelineFolderBreakdown[item].split("/")[-1], "") + var dir = DirAccess.open(fileDirectory) + dir.remove(timelineFolderBreakdown[item]) + + %OutputLog.text += "Completed conversion of file: " + timelineFolderBreakdown[item].replace(".cnv", ".dtl") + "\r\n" + + #print(item) + + + +func convertCharacters(): + %OutputLog.text += "Converting characters: \r\n" + for item in characterFolderBreakdown: + var original_file = item + var folderPath = characterFolderBreakdown[item] + %OutputLog.text += "Character " + folderPath + item +": " + var jsonData = {} + var file := FileAccess.open("res://dialogic/characters/" + item, FileAccess.READ) + var fileContent = file.get_as_text() + var json_object = JSON.new() + + var error = json_object.parse(fileContent) + + if error == OK: + contents = json_object.get_data() + var fileName = contents["name"] + %OutputLog.text += "Name: " + fileName + "\r\n" + + if ("[" in fileName) || ("]" in fileName) || ("?" in fileName): + %OutputLog.text += " [color=yellow]Stripping invalid characters from file name![/color]\r\n" + fileName = characterNameConversion(fileName) + + + + var dir_char = conversionRootFolder + "/characters" + if not DirAccess.dir_exists_absolute(dir_char + folderPath): + if not DirAccess.dir_exists_absolute(dir_char): + DirAccess.make_dir_absolute(dir_char) + var directory = DirAccess.open(dir_char) + + var progresiveDirectory = "" + for pathItem in folderPath.split('/'): + directory.open(dir_char + progresiveDirectory) + if pathItem!= "": + progresiveDirectory += "/" + pathItem + if !directory.dir_exists(dir_char + progresiveDirectory): + directory.make_dir(dir_char + progresiveDirectory) + + #add the prefix if the prefix option is enabled + if prefixCharacters: + var prefix = "" + for level in folderPath.split('/'): + if level != "": + prefix += level.left(2) + "-" + fileName = prefix + fileName + # using the resource constructor for this one + + var current_character = DialogicCharacter.new() + current_character.resource_path = dir_char + folderPath + "/" + fileName + ".dch" + # Everything needs to be in exact order + + current_character.color = Color(contents["color"].right(6)) + var customInfoDict = {} + customInfoDict["sound_moods"] = {} + customInfoDict["theme"] = "" + current_character.custom_info = customInfoDict + current_character.description = varNameStripSpecial(contents["description"]) + if contents["display_name"] == "": + current_character.display_name = varNameStripSpecial(contents["name"]) + else: + current_character.display_name = varNameStripSpecial(contents["display_name"]) + current_character.mirror = contents["mirror_portraits"] + #current_character.name = varNameStripSpecial(contents["name"]) + current_character.nicknames = [] + current_character.offset = Vector2(0,0) + + var portraitsList = {} + var firstPortrait = "" + for portrait in contents['portraits']: + var portraitData = {} + if portrait['path'] != "": + if ".tscn" in portrait['path']: + portraitData['scene'] = portrait['path'] + else: + portraitData['image'] = portrait['path'] + + #use the global offset, scale, and mirror setting from the origianl character file + portraitData['offset'] = Vector2(contents['offset_x'], contents['offset_y']) + portraitData['scale'] = contents['scale'] / 100 + portraitData['mirror'] = contents['mirror_portraits'] + + #discard it if there's an empty Default, so it doesn't throw an error + if !((portrait['name'] == "Default") && (portrait['path'] == "")) && !((portrait['name'] == "") && (portrait['path'] == "")): + portraitsList[portrait['name']] = portraitData + if firstPortrait == "": + firstPortrait = portrait['name'] + + + + current_character.portraits = portraitsList + if firstPortrait != "": + current_character.default_portrait = firstPortrait + current_character.scale = 1.0 + + ResourceSaver.save(current_character, current_character.resource_path) + + # Before we're finished here, update the folder breakdown so it has the proper character name + var infoDict = {} + infoDict["original_file"] = original_file + infoDict["path"] = characterFolderBreakdown[item] + infoDict["name"] = fileName + var name_breakdown:Array = ( folderPath + fileName).split("/") + name_breakdown.reverse() + infoDict["name_breakdown"] = name_breakdown + + infoDict["searchable_name"] = infoDict["name_breakdown"][0] + + characterFolderBreakdown[item] = infoDict + + %OutputLog.text += "\r\n" + else: + %OutputLog.text += "[color=red]There was a problem parsing this file![/color]\r\n" + + # Second pass, shorten all of the paths, so they match the character dictionary in Dialogic itself + + # Temporarily need an array to be able to sort + var sorting_array:Array = [] + + for item in characterFolderBreakdown: + sorting_array.append(characterFolderBreakdown[item]) + + sorting_array.sort_custom(func(a, b):return a['name_breakdown'].count("/") < b['name_breakdown'].count("/")) + + var clean_search_path:bool = false + var depth = 1 + + while !clean_search_path: + var interim_array:Array = [] + clean_search_path = true + + for i in sorting_array.size(): + if sorting_array.filter(func(val): return val['searchable_name'] == sorting_array[i]['searchable_name']).size() > 1: + clean_search_path = false + var replace_dict:Dictionary = sorting_array[i] + replace_dict["searchable_name"] = replace_dict["name_breakdown"][depth] + "/" + replace_dict["searchable_name"] + interim_array.append(replace_dict) + else: + interim_array.append(sorting_array[i]) + depth += 1 + sorting_array = interim_array + + characterFolderBreakdown.clear() + + for item in sorting_array: + if item["searchable_name"].count(" ") > 0: + item["searchable_name"] = '"' + item["searchable_name"] + '"' + characterFolderBreakdown[item['original_file']] = item + %OutputLog.text += "Final character name for " + item['original_file'] + ": " + item['searchable_name'] + "\r\n" + + %OutputLog.text += "\r\n" + +func convertVariables(): + %OutputLog.text += "Converting variables: \r\n" + + var convertedVariables = 0 + # Creating a file with a format identical to how the variables are stored in project settings + if varSubsystemInstalled: + var newVariableDictionary = {} + for varItem in definitionFolderBreakdown: + if "type" in definitionFolderBreakdown[varItem]: + if definitionFolderBreakdown[varItem]["type"] == "variable": + if definitionFolderBreakdown[varItem]["path"] == "/": + newVariableDictionary[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = definitionFolderBreakdown[varItem]["value"] + flatDefinitionsFile[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = varItem + convertedVariables += 1 + else: + # I will fill this one in later, need to figure out the recursion for it + var dictRef = newVariableDictionary + var flatNameBuilder = "" + + for pathItem in varNameStripSpecial(definitionFolderBreakdown[varItem]["path"]).split("/"): + + if pathItem != "": + if pathItem in dictRef: + dictRef = dictRef[pathItem] + flatNameBuilder += pathItem + "." + else: + dictRef[pathItem] = {} + dictRef = dictRef[pathItem] + flatNameBuilder += pathItem + "." + + dictRef[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = definitionFolderBreakdown[varItem]["value"] + convertedVariables +=1 + var flatName = flatNameBuilder + varNameStripSpecial(definitionFolderBreakdown[varItem]["name"]) + flatDefinitionsFile[flatName] = varItem + + + ProjectSettings.set_setting('dialogic/variables', null) + ProjectSettings.set_setting('dialogic/variables', newVariableDictionary) + ProjectSettings.save() + + #rebuild the data in the other tabs, so it doesnt override it + _refresh() + %OutputLog.text += str(convertedVariables) + " variables converted, and saved to project!\r\n" + else: + %OutputLog.text += "[color=yellow]Variable subsystem is not present! Variables were not converted![/color]\r\n" + + + %OutputLog.text += "\r\n" + + +func convertGlossaries(): + %OutputLog.text += "Converting glossaries: [color=red]not currently implemented[/color] \r\n" + + %OutputLog.text += "\r\n" + +func convertThemes(): + %OutputLog.text += "Converting themes: [color=red]not currently implemented[/color] \r\n" + + %OutputLog.text += "\r\n" + +func varNameStripSpecial(oldVariable): + # This is to remove special characters from variable names + # Since in code variables are accessed by Dialogic.VAR.path.to.variable, characters not usable in Godot paths have to be removed + var newVariable = oldVariable + newVariable = newVariable.replace(" ", "_") + newVariable = newVariable.replace(".", "_") + newVariable = newVariable.replace("-", "_") + + + return(newVariable) + +func variableNameConversion(oldText): + var newText = oldText + var regex = RegEx.new() + regex.compile('(\\[.*?\\])') + var result = regex.search_all(oldText) + if result: + for res in result: + var r_string = res.get_string() + var newString = res.get_string() + newString = newString.replace("[", "") + newString = newString.replace("]", "") + if newString[0] == '/': + newString = newString.right(-1) + + newString = varNameStripSpecial(newString) + newString = newString.replace("/", ".") + + + + if newString in flatDefinitionsFile: + newString = "{" + newString + "}" + newText = newText.replace(r_string, newString) + + + + return(newText) + +func characterNameConversion(oldText:String, filter_forward_slash:bool = true): + #as some characters aren't supported in filenames, we need to convert both the filenames, and references to them + #Most character name restrictions are only restrictions of Windows, but we are going to enforce it for Dialogic due to node and regex complexities for platform uniformity + #We're also restricting the special characters used for other designations + var newText = oldText + newText = newText.replace("<","[") + newText = newText.replace(">","]") + newText = newText.replace(":","-") + newText = newText.replace("\\","/") + if filter_forward_slash: + newText = newText.replace("/","-") + newText = newText.replace("|","-") + newText = newText.replace("*","@") + newText = newText.replace("?","0") + newText = newText.replace('"',"'") + + #These ones as they are the delimiter for portraits + newText = newText.replace("(","[") + newText = newText.replace(")","]") + + + return newText + +func convertSettings(): + %OutputLog.text += "Converting other settings: \r\n" + %OutputLog.text += "[color=yellow]Note! Most original settings can't be converted.[/color] \r\n" + + + var config = ConfigFile.new() + + var err = config.load("res://dialogic/settings.cfg") + if err != OK: + %OutputLog.text += "[color=red]Dialogic 1.x Settings file could not be loaded![/color] \r\n" + return + + ProjectSettings.set_setting('dialogic/text/autocolor_names', config.get_value("dialog", "auto_color_names", true)) + ProjectSettings.set_setting('dialogic/choices/autofocus_first', config.get_value("input", "autofocus_choices", false)) + ProjectSettings.set_setting('dialogic/choices/delay', config.get_value("input", "delay_after_options", 0.2)) + ProjectSettings.save() + + + + +func _on_check_box_toggled(button_pressed): + var message := "\r\n\r\nToggling this will add a prefix to all character filenames, which will have letters from each folder depth they are in. Characters in the root folder will have no prefix. \r\n" + prefixCharacters = button_pressed + if button_pressed: + %OutputLog.text += message + else: + %OutputLog.text = %OutputLog.text.replace(message, '') + diff --git a/addons/dialogic/Modules/Converter/settings_converter.tscn b/addons/dialogic/Modules/Converter/settings_converter.tscn new file mode 100644 index 0000000000000000000000000000000000000000..f0f2d13791feb182f23479dd2ac7dacc71df0272 --- /dev/null +++ b/addons/dialogic/Modules/Converter/settings_converter.tscn @@ -0,0 +1,74 @@ +[gd_scene load_steps=2 format=3 uid="uid://dign131itj5fu"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Converter/settings_converter.gd" id="1_3feab"] + +[node name="Dialogic 1 Converter" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +theme_type_variation = &"DialogicPanelA" +script = ExtResource("1_3feab") +short_info = "This converter allows converting (some) dialogic 1 data into a usable format." + +[node name="HBox" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="RightPanel" type="VBoxContainer" parent="HBox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label2" type="Label" parent="HBox/RightPanel"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Settings" + +[node name="CheckBox" type="CheckBox" parent="HBox/RightPanel"] +layout_mode = 2 +text = "Prefix duplicate characters" + +[node name="Verify" type="Button" parent="HBox/RightPanel"] +layout_mode = 2 +text = "Verify Files" + +[node name="Begin" type="Button" parent="HBox/RightPanel"] +layout_mode = 2 +disabled = true +text = "Begin Conversion" + +[node name="VSeparator" type="VSeparator" parent="HBox"] +layout_mode = 2 + +[node name="LeftPanel" type="VBoxContainer" parent="HBox"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 1.75 + +[node name="Label" type="Label" parent="HBox/LeftPanel"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Output" + +[node name="ScrollContainer" type="ScrollContainer" parent="HBox/LeftPanel"] +layout_mode = 2 +size_flags_vertical = 3 +vertical_scroll_mode = 0 + +[node name="OutputLog" type="RichTextLabel" parent="HBox/LeftPanel/ScrollContainer"] +unique_name_in_owner = true +clip_contents = false +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +bbcode_enabled = true +text = "This is a converter script to convert Dialogic 1.4+ files into Dialogic 2.0 format. + +Please copy your res://dialogic folder from a Dialogic 1.4 project into this project before proceeding. + +Once you are ready, the Verify Files button will do a check to make sure all of the files are present, and prepare it for conversion." + +[connection signal="toggled" from="HBox/RightPanel/CheckBox" to="." method="_on_check_box_toggled"] +[connection signal="pressed" from="HBox/RightPanel/Verify" to="." method="_on_verify_pressed"] +[connection signal="pressed" from="HBox/RightPanel/Begin" to="." method="_on_begin_pressed"] diff --git a/addons/dialogic/Modules/Core/event_end_branch.gd b/addons/dialogic/Modules/Core/event_end_branch.gd new file mode 100644 index 0000000000000000000000000000000000000000..3eb30d2cb401f2aefcc59427e6a3838a8ddb5d58 --- /dev/null +++ b/addons/dialogic/Modules/Core/event_end_branch.gd @@ -0,0 +1,68 @@ +@tool +class_name DialogicEndBranchEvent +extends DialogicEvent + +## Event that indicates the end of a condition or choice (or custom branch). +## In text this is not stored (only as a change in indentation). + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + dialogic.current_event_idx = find_next_index()-1 + finish() + + +func find_next_index(): + var idx: int = dialogic.current_event_idx + + var ignore: int = 1 + while true: + idx += 1 + var event :DialogicEvent= dialogic.current_timeline.get_event(idx) + if not event: + return idx + if event is DialogicEndBranchEvent: + if ignore > 1: ignore -= 1 + elif event.can_contain_events and not event.should_execute_this_branch(): + ignore += 1 + elif ignore <= 1: + return idx + + return idx + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "End Branch" + event_color = Color(1,1,1,1) + event_category = "Flow" + event_sorting_index = 0 + disable_editor_button = true + continue_at_end = true + + +################################################################################ +## SAVING/LOADING +################################################################################ + +## NOTE: This event is very special. It is rarely stored at all, as it is usually +## just a placeholder for removing an indentation level. +## When copying events however, some representation of this is necessary. That's why this is half-implemented. +func to_text() -> String: + return "<>" + + +func from_text(string:String) -> void: + pass + + +func is_valid_event(string:String) -> bool: + if string.strip_edges().begins_with("<>"): + return true + return false diff --git a/addons/dialogic/Modules/Core/icon.png b/addons/dialogic/Modules/Core/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d97548051128613659ed7d84696b3acc64ff4279 Binary files /dev/null and b/addons/dialogic/Modules/Core/icon.png differ diff --git a/addons/dialogic/Modules/Core/icon.png.import b/addons/dialogic/Modules/Core/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..cd8e9ef427934b45c198ec006e2d11df2547e586 --- /dev/null +++ b/addons/dialogic/Modules/Core/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0h5mknqmos7r" +path="res://.godot/imported/icon.png-6a95c038153eac5ac32780e8a0c1529b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Core/icon.png" +dest_files=["res://.godot/imported/icon.png-6a95c038153eac5ac32780e8a0c1529b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Core/index.gd b/addons/dialogic/Modules/Core/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..c0116c6a8ec80b4747f4198b2c1d99fd5f31cdd4 --- /dev/null +++ b/addons/dialogic/Modules/Core/index.gd @@ -0,0 +1,13 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_end_branch.gd')] + + +func _get_subsystems() -> Array: + return [ + {'name':'Expression', 'script':this_folder.path_join('subsystem_expression.gd')}, + {'name':'Animation', 'script':this_folder.path_join('subsystem_animation.gd')}, + ] diff --git a/addons/dialogic/Modules/Core/subsystem_animation.gd b/addons/dialogic/Modules/Core/subsystem_animation.gd new file mode 100644 index 0000000000000000000000000000000000000000..ad80a9513ee3b67f9891037b5ddfef2a559ad920 --- /dev/null +++ b/addons/dialogic/Modules/Core/subsystem_animation.gd @@ -0,0 +1,23 @@ +extends DialogicSubsystem + +## Subsystem that allows entering and leaving an animation state. + +signal finished + +var prev_state : int = 0 + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +func is_animating() -> bool: + return dialogic.current_state == dialogic.States.ANIMATING + +func start_animating() -> void: + prev_state = dialogic.current_state + dialogic.current_state = dialogic.States.ANIMATING + +func animation_finished(arg:String= "") -> void: + dialogic.current_state = prev_state + finished.emit() + diff --git a/addons/dialogic/Modules/Core/subsystem_expression.gd b/addons/dialogic/Modules/Core/subsystem_expression.gd new file mode 100644 index 0000000000000000000000000000000000000000..5616b58dc588d5cfeea0cc7236df97c99c36df96 --- /dev/null +++ b/addons/dialogic/Modules/Core/subsystem_expression.gd @@ -0,0 +1,66 @@ +extends DialogicSubsystem + +## Subsystem that allows executing strings (with the Expression class). +## This is used by conditions and to allow expresions as variables. + + + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +func execute_string(string:String, default = null) -> Variant: + # Some methods are not supported by the expression class, but very useful. + # Thus they are recreated below and secretly added. + string = string.replace('range(', 'd_range(') + string = string.replace('len(', 'd_len(') + + + var regex: RegEx = RegEx.create_from_string('{(\\w.*)}') + + for res in regex.search_all(string): + var value: String = str(dialogic.VAR.get_variable(res.get_string())) + if !value.is_valid_float(): + value = '"'+value+'"' + string = string.replace(res.get_string(), value) + + var expr := Expression.new() + + var autoloads := [] + var autoload_names := [] + for c in get_tree().root.get_children(): + autoloads.append(c) + autoload_names.append(c.name) + + if expr.parse(string, autoload_names) != OK: + printerr('Dialogic: Expression failed to parse: ', expr.get_error_text()) + return default + + var result := expr.execute(autoloads, self) + if expr.has_execute_failed(): + printerr('Dialogic: Expression failed to execute: ', expr.get_error_text()) + return default + return result + + +func execute_condition(condition:String) -> bool: + if execute_string(condition, false): + return true + return false + + +#################################################################################################### +## MAIN METHODS +#################################################################################################### +func d_range(a1, a2=null,a3=null,a4=null) -> Array: + if !a2: + return range(a1) + elif !a3: + return range(a1, a2) + elif !a4: + return range(a1, a2, a3) + else: + return range(a1, a2, a3, a4) + +func d_len(arg:Variant) -> int: + return len(arg) diff --git a/addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.gd b/addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.gd new file mode 100644 index 0000000000000000000000000000000000000000..cd43f0192126288470adf337b1ac50f7c3a2afbe --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.gd @@ -0,0 +1,101 @@ +@tool +extends CanvasLayer + +enum Alignments {LEFT, CENTER, RIGHT} + +@export_group("Main") +@export_subgroup("Text") +@export var text_alignment :Alignments= Alignments.LEFT +@export var text_size := 15 +@export var text_color : Color = Color.WHITE +@export_file('*.ttf') var normal_font:String = "" +@export_file('*.ttf') var bold_font:String = "" +@export_file('*.ttf') var italic_font:String = "" +@export_file('*.ttf') var bold_italic_font:String = "" + +@export_subgroup("Box") +@export var box_modulate : Color = Color(0.00784313771874, 0.00784313771874, 0.00784313771874, 0.84313726425171) +@export var box_size : Vector2 = Vector2(550, 110) + +@export_subgroup("Name Label") +@export var name_label_alignment := Alignments.LEFT +@export var name_label_font_size := 15 +@export var name_label_color := Color.WHITE +@export var name_label_use_character_color := true +@export_file('*.ttf') var name_label_font : String = "" +@export var name_label_box_modulate : Color = box_modulate +@export var name_label_box_offset := Vector2.ZERO + + +@export_group("Other") +@export_subgroup("Next Indicator") +@export var next_indicator_enabled := true +@export_enum('bounce', 'blink', 'none') var next_indicator_animation := 0 +@export_file("*.png","*.svg") var next_indicator_texture := '' +@export var next_indicator_show_on_questions := true +@export var next_indicator_show_on_autoadvance := false + + +@export_subgroup('Portraits') +@export var portrait_size_mode := DialogicNode_PortraitContainer.SizeModes.FIT_SCALE_HEIGHT + + +## Called by dialogic whenever export overrides might change +func _apply_export_overrides(): + if !is_inside_tree(): + await ready + + ## FONT SETTINGS + %DialogicNode_DialogText.alignment = text_alignment + + %DialogicNode_DialogText.add_theme_font_size_override("normal_font_size", text_size) + %DialogicNode_DialogText.add_theme_font_size_override("bold_font_size", text_size) + %DialogicNode_DialogText.add_theme_font_size_override("italics_font_size", text_size) + %DialogicNode_DialogText.add_theme_font_size_override("bold_italics_font_size", text_size) + + %DialogicNode_DialogText.add_theme_color_override("default_color", text_color) + + if !normal_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("normal_font", load(normal_font)) + if !bold_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("bold_font", load(bold_font)) + if !italic_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("italitc_font", load(italic_font)) + if !bold_italic_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("bold_italics_font", load(bold_italic_font)) + + ## BOX SETTINGS + %DialogTextPanel.self_modulate = box_modulate + %DialogTextPanel.custom_minimum_size = box_size + %TextInputPanel.self_modulate = box_modulate + + ## NAME LABEL SETTINGS + %DialogicNode_NameLabel.add_theme_font_size_override("font_size", name_label_font_size) + + if !name_label_font.is_empty(): + %DialogicNode_NameLabel.add_theme_font_override('font', load(name_label_font)) + + %DialogicNode_NameLabel.add_theme_color_override("font_color", name_label_color) + + %DialogicNode_NameLabel.use_character_color = name_label_use_character_color + + %NameLabelPanel.self_modulate = name_label_box_modulate + + %NameLabelPanel.position = name_label_box_offset+Vector2(0, -50) + %NameLabelPanel.anchor_left = name_label_alignment/2.0 + %NameLabelPanel.anchor_right = name_label_alignment/2.0 + %NameLabelPanel.grow_horizontal = [1, 2, 0][name_label_alignment] + + ## NEXT INDICATOR SETTINGS + if !next_indicator_enabled: + %NextIndicator.queue_free() + else: + %NextIndicator.animation = next_indicator_animation + if FileAccess.file_exists(next_indicator_texture): + %NextIndicator.texture = load(next_indicator_texture) + %NextIndicator.show_on_questions = next_indicator_show_on_questions + %NextIndicator.show_on_autoadvance = next_indicator_show_on_autoadvance + + ## PORTRAIT SETTINGS + for child in %Portraits.get_children(): + child.size_mode = portrait_size_mode diff --git a/addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.tscn b/addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.tscn new file mode 100644 index 0000000000000000000000000000000000000000..9414d4c151452f974b4d12a531be24331e89f40a --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.tscn @@ -0,0 +1,577 @@ +[gd_scene load_steps=31 format=3 uid="uid://uan2wdyuprb6"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/Default/DialogicDefaultLayout.gd" id="1"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_dialog_text.gd" id="2"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_name_label.gd" id="3"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Character/node_portrait_container.gd" id="3_dbhei"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Choice/node_choice_button.gd" id="4"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Background/node_background_holder.gd" id="5_uvb2c"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_type_sound.gd" id="10"] +[ext_resource type="AudioStream" uid="uid://c2viukvbub6v6" path="res://addons/dialogic/Example Assets/sound-effects/typing4.wav" id="11"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Style/node_style.gd" id="12"] +[ext_resource type="AudioStream" uid="uid://dwcre3fjf3cj8" path="res://addons/dialogic/Example Assets/sound-effects/typing5.wav" id="13"] +[ext_resource type="AudioStream" uid="uid://b6c1p14bc20p1" path="res://addons/dialogic/Example Assets/sound-effects/typing1.wav" id="14"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/Default/autoadvance_indicator.gd" id="15_ptoy3"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/Default/animations.gd" id="16_07l6b"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Choice/node_button_sound.gd" id="18"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_next_indicator.gd" id="20_ljcq2"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/Default/ExampleGlossaryPopup.gd" id="20_vmnp2"] +[ext_resource type="PackedScene" path="res://addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.tscn" id="22_854kg"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/TextInput/node_text_input.gd" id="22_ltkcf"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/HideWithChild.gd" id="23"] +[ext_resource type="FontFile" uid="uid://vrrmdx83skor" path="res://addons/dialogic/Example Assets/Fonts/Roboto-Regular.ttf" id="23_gyqve"] +[ext_resource type="FontFile" uid="uid://cc4xli25271fd" path="res://addons/dialogic/Example Assets/Fonts/Roboto-Bold.ttf" id="24_aa4pl"] +[ext_resource type="FontFile" uid="uid://b5c0p00x6g6u5" path="res://addons/dialogic/Example Assets/Fonts/Roboto-Italic.ttf" id="25_wt3bc"] + +[sub_resource type="Animation" id="Animation_au0a2"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("DialogTextAnimationParent:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(576, 648)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("DialogTextAnimationParent:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [0.0] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("DialogTextAnimationParent:scale") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(1, 1)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("DialogTextAnimationParent:modulate") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/4/type = "bezier" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("%DialogTextPanel:rotation") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0) +} + +[sub_resource type="Animation" id="Animation_6kbwc"] +resource_name = "new_text" +length = 0.4 +tracks/0/type = "method" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("%DialogicNode_DialogText") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0.2), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [&"text", ""], +"method": &"set" +}] +} +tracks/1/type = "bezier" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("%DialogTextPanel:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"handle_modes": PackedInt32Array(3, 3, 3, 3, 3), +"points": PackedFloat32Array(0, -0.025, 0, 0.025, 0, 0.005, -0.025, 0, 0.025, 0, -0.005, -0.025, 0, 0.025, 0, 0.005, -0.025, 0, 0.025, 0, 0, -0.025, 0, 0.025, 0), +"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4) +} + +[sub_resource type="Animation" id="Animation_htbgc"] +resource_name = "text_box_reveal" +length = 0.3 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("DialogTextAnimationParent:position") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2, 0.3), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Vector2(577, 700), Vector2(582, 647), Vector2(576, 648)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("DialogTextAnimationParent:rotation") +tracks/1/interp = 2 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.2, 0.3), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [-0.0899883, 0.0258223, 0.0] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("DialogTextAnimationParent:scale") +tracks/2/interp = 2 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.2, 0.3), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Vector2(0.793957, 0.778082), Vector2(0.937299, 1.14248), Vector2(1, 1)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("DialogTextAnimationParent:modulate") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_c14kh"] +_data = { +"RESET": SubResource("Animation_au0a2"), +"new_text": SubResource("Animation_6kbwc"), +"text_box_reveal": SubResource("Animation_htbgc") +} + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q3vpc"] +content_margin_left = 15.0 +content_margin_top = 15.0 +content_margin_right = 15.0 +content_margin_bottom = 15.0 +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wpkyg"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 +expand_margin_left = 5.0 +expand_margin_top = 5.0 +expand_margin_right = 5.0 +expand_margin_bottom = 5.0 + +[sub_resource type="FontVariation" id="FontVariation_7goc6"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a3cyk"] +bg_color = Color(0.12549, 0.12549, 0.12549, 1) +border_width_left = 2 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 2 +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 +expand_margin_left = 5.0 +expand_margin_top = 5.0 +expand_margin_right = 5.0 +expand_margin_bottom = 5.0 + +[node name="DefaultDialogNode" type="CanvasLayer"] +script = ExtResource("1") +name_label_box_modulate = Color(0.00784314, 0.00784314, 0.00784314, 0.843137) + +[node name="DialogicNode_BackgroundHolder" type="CanvasLayer" parent="."] +layer = -1 +script = ExtResource("5_uvb2c") + +[node name="Portraits" type="CanvasLayer" parent="."] +layer = 0 + +[node name="Portraits" type="Control" parent="Portraits"] +unique_name_in_owner = true +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="DialogicNode_PortraitContainer1" type="Control" parent="Portraits/Portraits"] +layout_mode = 1 +anchor_right = 0.231771 +anchor_bottom = 1.0 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_dbhei") +metadata/_edit_use_anchors_ = true + +[node name="DialogicNode_PortraitContainer2" type="Control" parent="Portraits/Portraits"] +layout_mode = 1 +anchor_left = 0.190104 +anchor_right = 0.401042 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_dbhei") +position_index = 1 +metadata/_edit_use_anchors_ = true + +[node name="DialogicNode_PortraitContainer3" type="Control" parent="Portraits/Portraits"] +layout_mode = 1 +anchor_left = 0.371528 +anchor_right = 0.625868 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_dbhei") +position_index = 2 +metadata/_edit_use_anchors_ = true + +[node name="DialogicNode_PortraitContainer4" type="Control" parent="Portraits/Portraits"] +layout_mode = 1 +anchor_left = 0.592882 +anchor_right = 0.805556 +anchor_bottom = 0.996914 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_dbhei") +position_index = 3 +mirrored = true +metadata/_edit_use_anchors_ = true + +[node name="DialogicNode_PortraitContainer5" type="Control" parent="Portraits/Portraits"] +layout_mode = 1 +anchor_left = 0.776042 +anchor_top = -0.00462963 +anchor_right = 1.00434 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("3_dbhei") +position_index = 4 +mirrored = true +metadata/_edit_use_anchors_ = true + +[node name="DefaultStyle" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("12") + +[node name="Animations" type="AnimationPlayer" parent="DefaultStyle"] +autoplay = "RESET" +libraries = { +"": SubResource("AnimationLibrary_c14kh") +} +script = ExtResource("16_07l6b") + +[node name="Choices" type="VBoxContainer" parent="DefaultStyle"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -86.5 +offset_top = -103.0 +offset_right = 86.5 +offset_bottom = 103.0 +grow_horizontal = 2 +grow_vertical = 2 +alignment = 1 +metadata/_edit_layout_mode = 1 + +[node name="DialogicNode_ChoiceButton1" type="Button" parent="DefaultStyle/Choices"] +layout_mode = 2 +text = "Some text" +script = ExtResource("4") + +[node name="DialogicNode_ChoiceButton2" type="Button" parent="DefaultStyle/Choices"] +layout_mode = 2 +text = "Some text" +script = ExtResource("4") + +[node name="DialogicNode_ChoiceButton3" type="Button" parent="DefaultStyle/Choices"] +layout_mode = 2 +text = "Some text" +script = ExtResource("4") + +[node name="DialogicNode_ChoiceButton4" type="Button" parent="DefaultStyle/Choices"] +layout_mode = 2 +text = "Some text" +script = ExtResource("4") + +[node name="DialogicNode_ChoiceButton5" type="Button" parent="DefaultStyle/Choices"] +layout_mode = 2 +text = "Some text" +script = ExtResource("4") + +[node name="DialogicNode_ChoiceButton6" type="Button" parent="DefaultStyle/Choices"] +layout_mode = 2 +text = "Some text" +script = ExtResource("4") + +[node name="DialogicNode_ButtonSound" type="AudioStreamPlayer" parent="DefaultStyle/Choices"] +script = ExtResource("18") +sound_pressed = ExtResource("14") +sound_hover = ExtResource("11") +sound_focus = ExtResource("13") + +[node name="DialogTextAnimationParent" type="Control" parent="DefaultStyle"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 0 + +[node name="DialogTextPanel" type="PanelContainer" parent="DefaultStyle/DialogTextAnimationParent"] +unique_name_in_owner = true +self_modulate = Color(0.00784314, 0.00784314, 0.00784314, 0.843137) +custom_minimum_size = Vector2(500, 110) +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -294.0 +offset_top = -154.0 +offset_right = 294.0 +offset_bottom = -22.0 +grow_horizontal = 2 +grow_vertical = 0 +pivot_offset = Vector2(275, 60) +theme_override_styles/panel = SubResource("StyleBoxFlat_q3vpc") +metadata/_edit_layout_mode = 1 + +[node name="DialogicNode_DialogText" type="RichTextLabel" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel" node_paths=PackedStringArray("textbox_root")] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/default_color = Color(1, 1, 1, 1) +theme_override_font_sizes/normal_font_size = 15 +theme_override_font_sizes/bold_font_size = 15 +theme_override_font_sizes/italics_font_size = 15 +theme_override_font_sizes/bold_italics_font_size = 15 +bbcode_enabled = true +text = "Some default text" +visible_characters_behavior = 1 +script = ExtResource("2") +textbox_root = NodePath("..") + +[node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel/DialogicNode_DialogText"] +script = ExtResource("10") +play_every_character = 0 + +[node name="NextIndicator" type="Control" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +script = ExtResource("20_ljcq2") +show_on_questions = true +metadata/_edit_layout_mode = 1 + +[node name="AutoAdvanceProgressbar" type="ProgressBar" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel"] +modulate = Color(1, 1, 1, 0.188235) +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 +size_flags_vertical = 8 +max_value = 1.0 +step = 0.001 +value = 0.5 +show_percentage = false +script = ExtResource("15_ptoy3") + +[node name="NameLabelHolder" type="Control" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel"] +layout_mode = 2 +mouse_filter = 2 + +[node name="NameLabelPanel" type="PanelContainer" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel/NameLabelHolder"] +unique_name_in_owner = true +self_modulate = Color(0.00784314, 0.00784314, 0.00784314, 0.843137) +layout_mode = 1 +offset_left = -10.0 +offset_top = -49.0 +offset_right = -1.0 +offset_bottom = -24.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_wpkyg") +script = ExtResource("23") +metadata/_edit_layout_mode = 1 +metadata/_edit_use_custom_anchors = true +metadata/_edit_group_ = true + +[node name="DialogicNode_NameLabel" type="Label" parent="DefaultStyle/DialogTextAnimationParent/DialogTextPanel/NameLabelHolder/NameLabelPanel"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_fonts/font = SubResource("FontVariation_7goc6") +theme_override_font_sizes/font_size = 15 +text = "S" +script = ExtResource("3") + +[node name="DialogicNode_TextInput" type="Control" parent="DefaultStyle"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -210.0 +offset_top = -50.0 +offset_right = 210.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("22_ltkcf") +input_line_edit = NodePath("TextInputPanel/VBoxContainer/InputField") +text_label = NodePath("TextInputPanel/VBoxContainer/TextLabel") +confirmation_button = NodePath("TextInputPanel/VBoxContainer/ConfirmationButton") +metadata/_edit_layout_mode = 1 + +[node name="TextInputPanel" type="PanelContainer" parent="DefaultStyle/DialogicNode_TextInput"] +unique_name_in_owner = true +self_modulate = Color(0.360784, 0.0941176, 0.168627, 1) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_q3vpc") + +[node name="VBoxContainer" type="VBoxContainer" parent="DefaultStyle/DialogicNode_TextInput/TextInputPanel"] +layout_mode = 2 + +[node name="TextLabel" type="Label" parent="DefaultStyle/DialogicNode_TextInput/TextInputPanel/VBoxContainer"] +layout_mode = 2 +text = "Please enter some text:" +autowrap_mode = 3 + +[node name="InputField" type="LineEdit" parent="DefaultStyle/DialogicNode_TextInput/TextInputPanel/VBoxContainer"] +layout_mode = 2 + +[node name="ConfirmationButton" type="Button" parent="DefaultStyle/DialogicNode_TextInput/TextInputPanel/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +text = "Confirm" + +[node name="Glossary" type="Control" parent="DefaultStyle"] +layout_mode = 1 +anchors_preset = 0 +script = ExtResource("20_vmnp2") + +[node name="Panel" type="PanelContainer" parent="DefaultStyle/Glossary"] +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -99.0 +offset_top = -115.0 +offset_right = 104.0 +offset_bottom = -34.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_override_styles/panel = SubResource("StyleBoxFlat_a3cyk") +metadata/_edit_use_custom_anchors = true +metadata/_edit_layout_mode = 1 + +[node name="VBox" type="VBoxContainer" parent="DefaultStyle/Glossary/Panel"] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="Title" type="Label" parent="DefaultStyle/Glossary/Panel/VBox"] +layout_mode = 2 +text = "MyGlossaryEntry" + +[node name="HSeparator" type="HSeparator" parent="DefaultStyle/Glossary/Panel/VBox"] +layout_mode = 2 + +[node name="Text" type="RichTextLabel" parent="DefaultStyle/Glossary/Panel/VBox"] +layout_mode = 2 +bbcode_enabled = true +text = "Some text" +fit_content = true + +[node name="Extra" type="RichTextLabel" parent="DefaultStyle/Glossary/Panel/VBox"] +layout_mode = 2 +theme_override_colors/default_color = Color(1, 1, 1, 0.584314) +theme_override_font_sizes/normal_font_size = 15 +bbcode_enabled = true +text = "[right]Wow, some text!" +fit_content = true + +[node name="Control" type="Control" parent="DefaultStyle/Glossary/Panel"] +show_behind_parent = true +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 8 + +[node name="PanelContainer" type="PanelContainer" parent="DefaultStyle/Glossary/Panel/Control"] +layout_mode = 0 +offset_left = -0.999999 +offset_top = -14.0 +offset_right = 19.0 +offset_bottom = 6.0 +rotation = 0.75799 +size_flags_horizontal = 4 +size_flags_vertical = 8 +theme_override_styles/panel = SubResource("StyleBoxFlat_a3cyk") + +[node name="ExampleHistoryScene" parent="." instance=ExtResource("22_854kg")] +history_font_normal = ExtResource("23_gyqve") +history_font_bold = ExtResource("24_aa4pl") +history_font_italics = ExtResource("25_wt3bc") +metadata/_edit_lock_ = true + +[connection signal="meta_hover_ended" from="DefaultStyle/DialogTextAnimationParent/DialogTextPanel/DialogicNode_DialogText" to="DefaultStyle/Glossary" method="_on_dialogic_display_dialog_text_meta_hover_ended"] +[connection signal="meta_hover_started" from="DefaultStyle/DialogTextAnimationParent/DialogTextPanel/DialogicNode_DialogText" to="DefaultStyle/Glossary" method="_on_dialogic_display_dialog_text_meta_hover_started"] diff --git a/addons/dialogic/Modules/DefaultStyles/Default/ExampleGlossaryPopup.gd b/addons/dialogic/Modules/DefaultStyles/Default/ExampleGlossaryPopup.gd new file mode 100644 index 0000000000000000000000000000000000000000..03f256c6dd0412cdf7ab291e117b4cd3d23127dd --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/ExampleGlossaryPopup.gd @@ -0,0 +1,21 @@ +extends Control + +func _ready() -> void: + hide() + +func _process(delta) -> void: + if visible: + global_position = get_global_mouse_position() + +func _on_dialogic_display_dialog_text_meta_hover_started(meta:String) -> void: + var info :Dictionary = Dialogic.Glossary.get_entry(meta) + if info: + show() + $Panel/VBox/Title.text = info.get('title', '') + $Panel/VBox/Text.text = info.get('text', '') + $Panel/VBox/Extra.text = '[right]'+info.get('extra', '') + modulate = info.get('color', Color.WHITE)#.darkened(0.5) + global_position = get_global_mouse_position() + +func _on_dialogic_display_dialog_text_meta_hover_ended(meta:String) -> void: + hide() diff --git a/addons/dialogic/Modules/DefaultStyles/Default/animations.gd b/addons/dialogic/Modules/DefaultStyles/Default/animations.gd new file mode 100644 index 0000000000000000000000000000000000000000..891763fd0eedefb4c0a41754c6c11d26ada864b8 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/animations.gd @@ -0,0 +1,31 @@ +extends AnimationPlayer + +# A custom script/node that adds some animations to the textbox. + +func _ready(): + Dialogic.Text.animation_textbox_hide.connect(_on_textbox_hide) + Dialogic.Text.animation_textbox_show.connect(_on_textbox_show) + Dialogic.Text.animation_textbox_new_text.connect(_on_textbox_new_text) + + +func _on_textbox_show(): + Dialogic.Animation.start_animating() + %DialogicNode_DialogText.text = "" + get_node("../DialogTextAnimationParent").modulate = Color.TRANSPARENT + play("text_box_reveal") + if not animation_finished.is_connected(Dialogic.Animation.animation_finished): + animation_finished.connect(Dialogic.Animation.animation_finished, CONNECT_ONE_SHOT) + + +func _on_textbox_hide(): + Dialogic.Animation.start_animating() + play_backwards("text_box_reveal") + if not animation_finished.is_connected(Dialogic.Animation.animation_finished): + animation_finished.connect(Dialogic.Animation.animation_finished, CONNECT_ONE_SHOT) + + +func _on_textbox_new_text(): + Dialogic.Animation.start_animating() + play("new_text") + if not animation_finished.is_connected(Dialogic.Animation.animation_finished): + animation_finished.connect(Dialogic.Animation.animation_finished, CONNECT_ONE_SHOT) diff --git a/addons/dialogic/Modules/DefaultStyles/Default/autoadvance_indicator.gd b/addons/dialogic/Modules/DefaultStyles/Default/autoadvance_indicator.gd new file mode 100644 index 0000000000000000000000000000000000000000..7ee13ba54fb87d9991df240338fe99576487c01f --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/autoadvance_indicator.gd @@ -0,0 +1,8 @@ +extends Range + +func _process(delta): + if Dialogic.Text.get_autoadvance_progress() < 0: + hide() + else: + show() + value = Dialogic.Text.get_autoadvance_progress() diff --git a/addons/dialogic/Modules/DefaultStyles/Default/preview.png b/addons/dialogic/Modules/DefaultStyles/Default/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..092cddf5b7e858f1bf1a9bf40e764df32c158cf4 Binary files /dev/null and b/addons/dialogic/Modules/DefaultStyles/Default/preview.png differ diff --git a/addons/dialogic/Modules/DefaultStyles/Default/preview.png.import b/addons/dialogic/Modules/DefaultStyles/Default/preview.png.import new file mode 100644 index 0000000000000000000000000000000000000000..132dcf99cc8652dcbb3c9dca73e2b9c24280babd --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/preview.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ptlnkdsl67tf" +path="res://.godot/imported/preview.png-1f03b89100d0ad1c91555207cc3d6207.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/DefaultStyles/Default/preview.png" +dest_files=["res://.godot/imported/preview.png-1f03b89100d0ad1c91555207cc3d6207.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/DefaultStyles/Default/style.cfg b/addons/dialogic/Modules/DefaultStyles/Default/style.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f9a999dae4dcdf345082d2b3dacf5536eb4f16c3 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/Default/style.cfg @@ -0,0 +1,5 @@ +[style] +name = "Visual Novel" +author = "Jowan Spooner" +description = "The default scene. Supports all events and settings." +scene = "DialogicDefaultLayout.tscn" diff --git a/addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.gd b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.gd new file mode 100644 index 0000000000000000000000000000000000000000..93cd49f151e30680039882b4eca478c09a5e0325 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.gd @@ -0,0 +1,25 @@ +extends Container + +func load_info(text:String, character:String = "", character_color:=Color(), icon:Texture= null) -> void: + %TextBox.text = text + if character: + %NameLabel.text = character + %NameLabel.add_theme_color_override('font_color', character_color) + %NameLabel.show() + else: + %NameLabel.hide() + if icon == null: + %Icon.hide() + else: + %Icon.show() + %Icon.texture = icon + +func prepare_textbox(history_root:Node) -> void: + %TextBox.add_theme_font_override("normal_font", history_root.history_font_normal) + %NameLabel.add_theme_font_override("font", history_root.history_font_normal) + %NameLabel.add_theme_font_size_override("font_size", history_root.history_font_size) + %TextBox.add_theme_font_override("bold_font", history_root.history_font_bold) + %TextBox.add_theme_font_override("italics_font", history_root.history_font_italics) + %TextBox.add_theme_font_size_override("normal_font_size", history_root.history_font_size) + %TextBox.add_theme_font_size_override("bold_font_size", history_root.history_font_size) + %TextBox.add_theme_font_size_override("italics_font_size", history_root.history_font_size) diff --git a/addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.tscn b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.tscn new file mode 100644 index 0000000000000000000000000000000000000000..40a72710de92aa5254aead92e91136c53383b6a0 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.tscn @@ -0,0 +1,48 @@ +[gd_scene load_steps=4 format=3] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/ExampleHistoryItem.gd" id="1_dgoja"] +[ext_resource type="Texture2D" uid="uid://bvqgwmds7enrv" path="res://icon.svg" id="2_ywvym"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_upgjp"] +content_margin_left = 5.0 +content_margin_top = 5.0 +content_margin_right = 5.0 +content_margin_bottom = 5.0 +bg_color = Color(0.780392, 0.780392, 0.780392, 0.156863) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 + +[node name="HistoryItem" type="PanelContainer"] +offset_left = -37.0 +offset_top = 510.0 +offset_right = 1085.0 +offset_bottom = 555.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_upgjp") +script = ExtResource("1_dgoja") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Icon" type="TextureRect" parent="HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +texture = ExtResource("2_ywvym") +expand_mode = 1 +stretch_mode = 4 + +[node name="NameLabel" type="Label" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 0 + +[node name="TextBox" type="RichTextLabel" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +bbcode_enabled = true +text = "Some tex" +fit_content = true diff --git a/addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.gd b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.gd new file mode 100644 index 0000000000000000000000000000000000000000..8a335d303f0b3f8f2a13482f676064471152f9e4 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.gd @@ -0,0 +1,95 @@ +extends Control + +## Example scene for viewing the History +## Implements most of the visual options from 1.x History mode +@export_group('Open &Close Button') +@export var show_open_button: bool = true +@export var show_close_button: bool = true + +@export_group('Event visibility') +@export var show_all_choices: bool = true +@export var show_join_and_leave: bool = true + +@export_group('Presentation') +@export var scroll_to_bottom: bool = true +@export var show_name_colors: bool = true +@export var name_delimeter: String = ": " + +@export_group('Fonts') +@export var history_font_size: int +@export var history_font_normal: Font +@export var history_font_bold: Font +@export var history_font_italics: Font + +var scroll_to_bottom_flag: bool = false + + +func _ready(): + if Dialogic.has_subsystem('History'): + $ShowHistory.visible = show_open_button and Dialogic.History.simple_history_enabled + else: + self.visible = false + + +func _process(delta): + if scroll_to_bottom_flag and $HistoryBox.visible and %HistoryLog.get_child_count(): + await get_tree().process_frame + %HistoryBox.ensure_control_visible(%HistoryLog.get_children()[-1]) + scroll_to_bottom_flag = false + + +func _on_show_history_pressed(): + Dialogic.paused = true + show_history() + + +func show_history() -> void: + for child in %HistoryLog.get_children(): + child.queue_free() + + for info in Dialogic.History.get_simple_history(): + var history_item :Control= load(DialogicUtil.get_module_path('DefaultStyles').path_join("ExampleHistoryItem.tscn")).instantiate() + history_item.prepare_textbox(self) + match info.event_type: + "Text": + if info.has('character') and info['character']: + if show_name_colors: + history_item.load_info(info['text'], info['character']+name_delimeter, info['character_color']) + else: + history_item.load_info(info['text'], info['character']+name_delimeter) + else: + history_item.load_info(info['text']) + "Character": + if !show_join_and_leave: + history_item.queue_free() + continue + history_item.load_info('[i]'+info['text']) + "Choice": + var choices_text := "" + if show_all_choices: + for i in info['all_choices']: + if i.ends_with('#disabled'): + choices_text += "- [i]("+i.trim_suffix('#disabled')+")[/i]\n" + elif i == info['text']: + choices_text += "-> [b]"+i+"[/b]\n" + else: + choices_text += "-> "+i+"\n" + else: + choices_text += "- [b]"+info['text']+"[/b]\n" + history_item.load_info(choices_text) + + %HistoryLog.add_child(history_item) + + if scroll_to_bottom: + scroll_to_bottom_flag = true + + $ShowHistory.hide() + $HideHistory.visible = show_close_button + %HistoryBox.show() + + +func _on_hide_history_pressed(): + Dialogic.paused = false + %HistoryBox.hide() + $HideHistory.hide() + $ShowHistory.visible = show_open_button and Dialogic.History.simple_history_enabled diff --git a/addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.tscn b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.tscn new file mode 100644 index 0000000000000000000000000000000000000000..5a5bde920bbad4ed0992f267ae32dcf87c93a12f --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.tscn @@ -0,0 +1,84 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/ExampleHistoryScene.gd" id="1_o5tfq"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1hdvb"] +content_margin_left = 10.0 +content_margin_top = 10.0 +content_margin_right = 10.0 +content_margin_bottom = 10.0 +bg_color = Color(0, 0, 0, 0.776471) +border_color = Color(0.8, 0.8, 0.8, 0) +corner_radius_top_left = 20 +corner_radius_top_right = 20 +corner_radius_bottom_right = 20 +corner_radius_bottom_left = 20 + +[node name="ExampleHistoryScene" type="Control"] +layout_direction = 1 +layout_mode = 3 +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 +mouse_filter = 2 +script = ExtResource("1_o5tfq") + +[node name="HistoryBox" type="ScrollContainer" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 74.0 +offset_top = 65.0 +offset_right = -74.0 +offset_bottom = -57.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_1hdvb") +horizontal_scroll_mode = 0 + +[node name="HistoryLog" type="VBoxContainer" parent="HistoryBox"] +unique_name_in_owner = true +layout_direction = 1 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ShowHistory" type="Button" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -73.0 +offset_top = 7.0 +offset_right = -9.0 +offset_bottom = 38.0 +grow_horizontal = 0 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "History" + +[node name="HideHistory" type="Button" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -123.0 +offset_top = 58.0 +offset_right = -62.0 +offset_bottom = 89.0 +grow_horizontal = 0 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Return" + +[connection signal="pressed" from="ShowHistory" to="." method="_on_show_history_pressed"] +[connection signal="pressed" from="HideHistory" to="." method="_on_hide_history_pressed"] diff --git a/addons/dialogic/Modules/DefaultStyles/HideWithChild.gd b/addons/dialogic/Modules/DefaultStyles/HideWithChild.gd new file mode 100644 index 0000000000000000000000000000000000000000..d63262759f102cda0c17e09c32b8590886674ade --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/HideWithChild.gd @@ -0,0 +1,8 @@ +extends Control + +func _ready(): + get_child(0).visibility_changed.connect(_on_child_visibility_changed) + _on_child_visibility_changed() + +func _on_child_visibility_changed(): + visible = get_child(0).visible diff --git a/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.gd b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.gd new file mode 100644 index 0000000000000000000000000000000000000000..5738f4fc6883e3662eea196d0ab1f814becbe795 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.gd @@ -0,0 +1,91 @@ +extends CanvasLayer + +enum Alignments {LEFT, CENTER, RIGHT} +enum LimitedAlignments {LEFT=0, RIGHT=1} + +@export_group('Main') +@export_subgroup("Text") +@export var text_alignment :Alignments= Alignments.LEFT +@export var text_size := 15 +@export var text_color : Color = Color.WHITE +@export_file('*.ttf') var normal_font:String = "" +@export_file('*.ttf') var bold_font:String = "" +@export_file('*.ttf') var italic_font:String = "" +@export_file('*.ttf') var bold_italic_font:String = "" + +@export_subgroup("Name Label") +@export var name_label_alignment := Alignments.LEFT +@export var name_label_font_size := 15 +@export var name_label_color := Color.WHITE +@export var name_label_use_character_color := true +@export_file('*.ttf') var name_label_font : String = "" +@export var name_label_hide_when_no_character := false + +@export_group('Box & Portrait') +@export_subgroup("Box") +@export var box_modulate : Color = Color(0.47247135639191, 0.31728461384773, 0.16592600941658) +@export var box_size : Vector2 = Vector2(600, 160) +@export var box_distance := 25 +@export var box_corner_radius := 5 +@export var box_padding := 10 +@export_range(-0.3, 0.3) var box_tilt := 0.079 + +@export_subgroup('Portrait') +@export var portrait_stretch_factor = 0.3 +@export var portrait_position :LimitedAlignments = LimitedAlignments.LEFT +@export var portrait_bg_modulate := Color(0, 0, 0, 0.5137255191803) + + +## Called by dialogic whenever export overrides might change +func _apply_export_overrides(): + if !is_inside_tree(): + await ready + + ## FONT SETTINGS + %DialogicNode_DialogText.alignment = text_alignment + + %DialogicNode_DialogText.add_theme_font_size_override("normal_font_size", text_size) + %DialogicNode_DialogText.add_theme_font_size_override("bold_font_size", text_size) + %DialogicNode_DialogText.add_theme_font_size_override("italics_font_size", text_size) + %DialogicNode_DialogText.add_theme_font_size_override("bold_italics_font_size", text_size) + + %DialogicNode_DialogText.add_theme_color_override("default_color", text_color) + + if !normal_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("normal_font", load(normal_font)) + if !bold_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("bold_font", load(bold_font)) + if !italic_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("italitc_font", load(italic_font)) + if !bold_italic_font.is_empty(): + %DialogicNode_DialogText.add_theme_font_override("bold_italics_font", load(bold_italic_font)) + + ## BOX SETTINGS + %Panel.self_modulate = box_modulate + %Panel.size = box_size + %Panel.position = Vector2(-box_size.x/2, -box_size.y-box_distance) + %PortraitPanel.size_flags_stretch_ratio = portrait_stretch_factor + + var stylebox :StyleBoxFlat = %Panel.get_theme_stylebox('panel', 'PanelContainer') + stylebox.set_corner_radius_all(box_corner_radius) + stylebox.set_content_margin_all(box_padding) + stylebox.skew.x = box_tilt + + ## PORTRAIT SETTINGS + %PortraitBackgroundColor.color = portrait_bg_modulate + %PortraitPanel.get_parent().move_child(%PortraitPanel, portrait_position) + stylebox = %PortraitPanel.get_theme_stylebox('panel', 'Panel') + stylebox.set_corner_radius_all(box_corner_radius) + stylebox.skew.x = box_tilt + + ## NAME LABEL SETTINGS + %DialogicNode_NameLabel.add_theme_font_size_override("font_size", name_label_font_size) + + if !name_label_font.is_empty(): + %DialogicNode_NameLabel.add_theme_font_override('font', load(name_label_font)) + + %DialogicNode_NameLabel.add_theme_color_override("font_color", name_label_color) + %DialogicNode_NameLabel.use_character_color = name_label_use_character_color + %DialogicNode_NameLabel.horizontal_alignment = name_label_alignment + + %DialogicNode_NameLabel.hide_when_empty = name_label_hide_when_no_character diff --git a/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.tscn b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.tscn new file mode 100644 index 0000000000000000000000000000000000000000..c4ef76594ef1cf9bd062eaf72765942d7420b786 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.tscn @@ -0,0 +1,166 @@ +[gd_scene load_steps=8 format=3 uid="uid://cgc2samfe5ghg"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/DialogicRPGLayout.gd" id="1_ry4cs"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Character/node_portrait_container.gd" id="2_ih78e"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_dialog_text.gd" id="3"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_name_label.gd" id="6"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Choice/node_choice_button.gd" id="9"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_smrwn"] +content_margin_left = 10.0 +content_margin_top = 10.0 +content_margin_right = 10.0 +content_margin_bottom = 10.0 +bg_color = Color(1, 1, 1, 1) +skew = Vector2(0.088, 0) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_775n1"] +bg_color = Color(0.254902, 0.254902, 0.254902, 1) +skew = Vector2(0.073, 0) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[node name="RPG_ExampleNode" type="CanvasLayer"] +layer = 3 +script = ExtResource("1_ry4cs") + +[node name="Anchor" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 0 + +[node name="Panel" type="PanelContainer" parent="Anchor"] +unique_name_in_owner = true +self_modulate = Color(0.533333, 0.376471, 0.176471, 1) +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -89.0 +offset_top = -66.0 +offset_right = 112.0 +offset_bottom = -16.0 +grow_horizontal = 2 +grow_vertical = 0 +theme_override_styles/panel = SubResource("StyleBoxFlat_smrwn") + +[node name="HBox" type="HBoxContainer" parent="Anchor/Panel"] +layout_mode = 2 +theme_override_constants/separation = 15 + +[node name="PortraitPanel" type="Panel" parent="Anchor/Panel/HBox"] +unique_name_in_owner = true +clip_children = 1 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.3 +theme_override_styles/panel = SubResource("StyleBoxFlat_775n1") + +[node name="PortraitBackgroundColor" type="ColorRect" parent="Anchor/Panel/HBox/PortraitPanel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -123.0 +offset_right = 123.0 +offset_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.231373) + +[node name="DialogicNode_PortraitContainer" type="Control" parent="Anchor/Panel/HBox/PortraitPanel/PortraitBackgroundColor"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -2.0 +offset_top = 4.0 +offset_right = 2.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2_ih78e") +mode = 1 +portrait_prefix = "Faces/" +size_mode = 2 +debug_character_portrait = "speaker" + +[node name="VBoxContainer" type="VBoxContainer" parent="Anchor/Panel/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="DialogicNode_NameLabel" type="Label" parent="Anchor/Panel/HBox/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_font_sizes/font_size = 8 +text = "Name" +script = ExtResource("6") + +[node name="DialogicNode_DialogText" type="RichTextLabel" parent="Anchor/Panel/HBox/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/normal_font_size = 6 +bbcode_enabled = true +text = "Some text" +scroll_following = true +script = ExtResource("3") + +[node name="VBoxContainer" type="HBoxContainer" parent="Anchor/Panel/HBox/VBoxContainer"] +layout_mode = 2 +alignment = 2 + +[node name="DialogicNode_ChoiceButton" type="Button" parent="Anchor/Panel/HBox/VBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Text" +flat = true +script = ExtResource("9") + +[node name="DialogicNode_ChoiceButton2" type="Button" parent="Anchor/Panel/HBox/VBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Text" +flat = true +script = ExtResource("9") + +[node name="DialogicNode_ChoiceButton3" type="Button" parent="Anchor/Panel/HBox/VBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Text" +flat = true +script = ExtResource("9") + +[node name="DialogicNode_ChoiceButton4" type="Button" parent="Anchor/Panel/HBox/VBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Text" +flat = true +script = ExtResource("9") + +[node name="DialogicNode_ChoiceButton5" type="Button" parent="Anchor/Panel/HBox/VBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Text" +flat = true +script = ExtResource("9") + +[node name="DialogicNode_ChoiceButton6" type="Button" parent="Anchor/Panel/HBox/VBoxContainer/VBoxContainer"] +visible = false +layout_mode = 2 +text = "Text" +flat = true +script = ExtResource("9") diff --git a/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..dd452ffeaa8373e36fe3f0d58f18265afffcd6f6 Binary files /dev/null and b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png differ diff --git a/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png.import b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png.import new file mode 100644 index 0000000000000000000000000000000000000000..3cdbf002224ab7e8ddd3d02ddecc0cd224cd3122 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnc5unptt5uod" +path="res://.godot/imported/preview.png-26e1db82aeba4ab35313659ee348c2e8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/preview.png" +dest_files=["res://.godot/imported/preview.png-26e1db82aeba4ab35313659ee348c2e8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/style.cfg b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/style.cfg new file mode 100644 index 0000000000000000000000000000000000000000..d957ef940f4fdc7fd428ec58322b619ad5864e86 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/RPG_BoxPortrait/style.cfg @@ -0,0 +1,5 @@ +[style] +name = "RPG Single Portrait" +author = "Jowan Spooner" +description = "An example RPG layout. Has no portrait positions, only a speaker portrait." +scene = "DialogicRPGLayout.tscn" diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.gd b/addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.gd new file mode 100644 index 0000000000000000000000000000000000000000..b71b10a2b48ace5a5b41b32a90bf6cbaac9cba58 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.gd @@ -0,0 +1,166 @@ +extends CanvasLayer + +## This layout won't do anything on it's own + +@export_group("Main") +@export_subgroup("Text") +@export var text_size := 15 +@export var text_color : Color = Color.BLACK +@export_file('*.ttf') var normal_font:String = "" +@export_file('*.ttf') var bold_font:String = "" +@export_file('*.ttf') var italic_font:String = "" +@export_file('*.ttf') var bold_italic_font:String = "" +@export var text_max_width := 300 + +@export_subgroup('Box') +@export var box_modulate := Color.WHITE +@export var box_modulate_by_character_color := false +@export var box_padding := Vector2(10,10) +@export_range(0.1, 2) var box_corner_radius := 0.3 +@export_range(0.1, 5) var box_wobble_speed := 1 +@export_range(0, 1) var box_wobbliness := 0.2 + +@export_subgroup('Behaviour') +@export var behaviour_distance := 50 +@export var behaviour_direction := Vector2(1, -1) + +@export_group('Name Label & Choices') +@export_subgroup("Name Label") +@export var name_label_enabled := true +@export var name_label_font_size := 15 +@export_file('*.ttf') var name_label_font : String = "" +@export var name_label_use_character_color := true +@export var name_label_color := Color.BLACK +@export var name_label_box_modulate : Color = Color.WHITE +@export var name_label_padding := Vector2(5,0) +@export var name_label_offset := Vector2(0,0) + +@export_subgroup('Choices Text') +@export var choices_text_size := 15 +@export var choices_text_color := Color.LIGHT_SLATE_GRAY +@export var choices_text_color_hover := Color.DARK_GRAY +@export var choices_text_color_focus := Color.BLACK + + +var bubbles :Array = [] +var fallback_bubble :Control = null + +func _ready(): + Dialogic.Text.about_to_show_text.connect(_on_dialogic_text_event) + + $Example/ExamplePoint.position = $Example.get_viewport_rect().size/2 + + fallback_bubble = preload("res://addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.tscn").instantiate() + fallback_bubble.speaker_node = $Example/ExamplePoint + fallback_bubble.name = "Fallback Bubble" + bubble_apply_overrides(fallback_bubble) + add_child(fallback_bubble) + + +func register_character(character:DialogicCharacter, node:Node2D): + var new_bubble := preload("res://addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.tscn").instantiate() + new_bubble.speaker_node = node + new_bubble.character = character + new_bubble.name = character.resource_path.get_file().trim_suffix("."+character.resource_path.get_extension()) + "Bubble" + add_child(new_bubble) + bubble_apply_overrides(new_bubble) + bubbles.append(new_bubble) + + +## Called by dialogic whenever export overrides might change +func _apply_export_overrides(): + for bubble in bubbles: + bubble_apply_overrides(bubble) + + if fallback_bubble: + bubble_apply_overrides(fallback_bubble) + + +func bubble_apply_overrides(bubble:Control) -> void: + ## TEXT FONT AND COLOR + var rtl : RichTextLabel = bubble.get_node('DialogText') + rtl.add_theme_font_size_override('normal_font', text_size) + rtl.add_theme_font_size_override("normal_font_size", text_size) + rtl.add_theme_font_size_override("bold_font_size", text_size) + rtl.add_theme_font_size_override("italics_font_size", text_size) + rtl.add_theme_font_size_override("bold_italics_font_size", text_size) + + rtl.add_theme_color_override("default_color", text_color) + + if !normal_font.is_empty(): + rtl.add_theme_font_override("normal_font", load(normal_font)) + if !bold_font.is_empty(): + rtl.add_theme_font_override("bold_font", load(bold_font)) + if !italic_font.is_empty(): + rtl.add_theme_font_override("italitc_font", load(italic_font)) + if !bold_italic_font.is_empty(): + rtl.add_theme_font_override("bold_italics_font", load(bold_italic_font)) + bubble.max_width = text_max_width + + + ## BOX & TAIL COLOR + bubble.get_node('Tail').default_color = box_modulate + bubble.get_node('Background').color = box_modulate + bubble.get_node('Background').material.set_shader_parameter('radius', box_corner_radius) + bubble.get_node('Background').material.set_shader_parameter('crease', box_wobbliness*0.1) + bubble.get_node('Background').material.set_shader_parameter('speed', box_wobble_speed) + if box_modulate_by_character_color and bubble.character != null: + bubble.get_node('Tail').modulate = bubble.character.color + bubble.get_node('Background').modulate = bubble.character.color + bubble.padding = box_padding + + ## NAME LABEL SETTINGS + var nl : Label = bubble.get_node('%NameLabel') + nl.add_theme_font_size_override("font_size", name_label_font_size) + + if !name_label_font.is_empty(): + nl.add_theme_font_override('font', load(name_label_font)) + + nl.use_character_color = name_label_use_character_color + if !nl.use_character_color: + nl.add_theme_color_override("font_color", name_label_color) + + var nlp : Container = bubble.get_node('DialogText/NameLabel') + nlp.self_modulate = name_label_box_modulate + nlp.get_theme_stylebox('panel').content_margin_left = name_label_padding.x + nlp.get_theme_stylebox('panel').content_margin_right = name_label_padding.x + nlp.get_theme_stylebox('panel').content_margin_top = name_label_padding.y + nlp.get_theme_stylebox('panel').content_margin_bottom = name_label_padding.y + nlp.position += name_label_offset + + if !name_label_enabled: + nlp.queue_free() + + + ## CHOICE SETTINGS + var choice_theme := Theme.new() + choice_theme.set_font_size('font_size', 'Button', choices_text_size) + choice_theme.set_color('font_color', 'Button', choices_text_color) + choice_theme.set_color('font_pressed_color', 'Button', choices_text_color) + choice_theme.set_color('font_hover_color', 'Button', choices_text_color_hover) + choice_theme.set_color('font_focus_color', 'Button', choices_text_color_focus) + + bubble.get_node('DialogText/ChoiceContainer').theme = choice_theme + + ## BEHAVIOUR + bubble.safe_zone = behaviour_distance + bubble.base_direction = behaviour_direction + + +func _on_dialogic_text_event(info:Dictionary): + var no_bubble_open := true + for b in bubbles: + if b.character == info.character: + no_bubble_open = false + b.open() + else: + b.close() + if no_bubble_open: + if box_modulate_by_character_color and info.character != null: + fallback_bubble.get_node('Tail').modulate = info.character.color + fallback_bubble.get_node('Background').modulate = info.character.color + $Example.show() + fallback_bubble.open() + else: + $Example.hide() + fallback_bubble.close() diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.tscn b/addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.tscn new file mode 100644 index 0000000000000000000000000000000000000000..a6508372b70a048da5ad999d9a80a56291df3082 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.tscn @@ -0,0 +1,36 @@ +[gd_scene load_steps=2 format=3 uid="uid://syki6k0e6aac"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/TextBubble/DialogicTextBubbleLayout.gd" id="1_502mo"] + +[node name="TextBubbleHolder" type="CanvasLayer"] +script = ExtResource("1_502mo") + +[node name="Example" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ExamplePoint" type="Polygon2D" parent="Example"] +polygon = PackedVector2Array(0, -24, -22, 0, 0, 28, 24, 0) + +[node name="Label" type="RichTextLabel" parent="Example"] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 13.0 +offset_top = -210.0 +offset_right = 647.0 +offset_bottom = -16.0 +grow_vertical = 0 +bbcode_enabled = true +text = "This is a fallback bubble, that is not actually connected to any character. In game use the following code to add speech bubbles to a character: +[color=darkgray] +var layout = Dialogic.start(@timeline_path) +layout.register_character(@character_resource, @node) +[/color] +[color=lightblue]@character_resource[/color] should be a loaded DialogicCharacter (a .dtl file) +[color=lightblue]@node[/color] should be the 2D node the bubble should point at." diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.gd b/addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.gd new file mode 100644 index 0000000000000000000000000000000000000000..7ba66b45db183943bde2324e8905136c951bda1a --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.gd @@ -0,0 +1,111 @@ +extends Control + +@onready var tail : Line2D = $Tail +@onready var bubble : Control = $Background +var speaker_node : Node = null +var character : DialogicCharacter = null +var max_width := 300 + +var bubble_rect : Rect2 = Rect2(0.0, 0.0, 2.0, 2.0) +var base_position := Vector2.ZERO + +var base_direction := Vector2(1.0, -1.0).normalized() +var safe_zone := 50.0 +var padding := Vector2() + +func _ready() -> void: + scale = Vector2.ZERO + modulate.a = 0.0 + if speaker_node: + position = speaker_node.get_global_transform_with_canvas().origin + + +func _process(delta): + if speaker_node: + base_position = speaker_node.get_global_transform_with_canvas().origin + + var center := get_viewport_rect().size / 2.0 + + var dist_x := abs(base_position.x - center.x) + var dist_y := abs(base_position.y - center.y) + var x_e := center.x - bubble_rect.size.x + var y_e := center.y - bubble_rect.size.y + var influence_x := remap(clamp(dist_x, x_e, center.x), x_e, center.x * 0.8, 0.0, 1.0) + var influence_y := remap(clamp(dist_y, y_e, center.y), y_e, center.y * 0.8, 0.0, 1.0) + if base_position.x > center.x: influence_x = -influence_x + if base_position.y > center.y: influence_y = -influence_y + var edge_influence := Vector2(influence_x, influence_y) + + var direction := (base_direction + edge_influence).normalized() + + var p : Vector2 = base_position + direction * (safe_zone + lerp(bubble_rect.size.y, bubble_rect.size.x, abs(direction.x)) * 0.4) + p = p.clamp(bubble_rect.size / 2.0, get_viewport_rect().size - bubble_rect.size / 2.0) + + position = lerp(position, p, 10.0 * delta) + + var point_a : Vector2 = Vector2.ZERO + var point_b : Vector2 = (base_position - position) * 0.5 + + var offset = Vector2.from_angle(point_a.angle_to_point(point_b)) * bubble_rect.size * abs(direction.x) * 0.4 + + point_a += offset + point_b += offset * 0.5 + + var curve := Curve2D.new() + var direction_point := Vector2(0, (point_b.y - point_a.y)) + curve.add_point(point_a, Vector2.ZERO, direction_point * 0.5) + curve.add_point(point_b) + tail.points = curve.tessellate(5) + tail.width = bubble_rect.size.x * 0.15 + + +func open() -> void: + show() + %DialogText.enabled = true + var open_tween := create_tween().set_parallel(true) + open_tween.tween_property(self, "scale", Vector2.ONE, 0.1).from(Vector2.ZERO) + open_tween.tween_property(self, "modulate:a", 1.0, 0.1).from(0.0) + + + +func close() -> void: + %DialogText.enabled = false + var close_tween := create_tween().set_parallel(true) + close_tween.tween_property(self, "scale", Vector2.ONE * 0.8, 0.1) + close_tween.tween_property(self, "modulate:a", 0.0, 0.1) + await close_tween.finished + hide() + + +func _on_dialog_text_started_revealing_text(): + var font :Font = %DialogText.get_theme_font("normal_font") + %DialogText.size = font.get_multiline_string_size(%DialogText.get_parsed_text(), HORIZONTAL_ALIGNMENT_LEFT, max_width, %DialogText.get_theme_font_size("normal_font_size")) + if Dialogic.Choices.is_question(Dialogic.current_event_idx): + font = $DialogText/ChoiceContainer/DialogicNode_ChoiceButton.get_theme_font('font') + %DialogText.size.y += font.get_string_size(%DialogText.get_parsed_text(), HORIZONTAL_ALIGNMENT_LEFT, max_width, $DialogText/ChoiceContainer/DialogicNode_ChoiceButton.get_theme_font_size("font_size")).y + %DialogText.position = -%DialogText.size/2 + + _resize_bubble() + + +func _resize_bubble() -> void: + var bubble_size :Vector2 = %DialogText.size+(padding*2) + var half_size :Vector2= (bubble_size / 2.0) + %DialogText.pivot_offset = half_size + bubble.pivot_offset = half_size + bubble_rect = Rect2(position, bubble_size * Vector2(1.1, 1.1)) + bubble.size = bubble_size + bubble.position = -half_size + + var t := create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + t.tween_property(bubble, "scale", Vector2.ONE, 0.2).from(Vector2.ZERO) + + # set bubble's ratio + var bubble_ratio := Vector2.ONE + if bubble_rect.size.x < bubble_rect.size.y: + bubble_ratio.y = bubble_rect.size.y / bubble_rect.size.x + else: + bubble_ratio.x = bubble_rect.size.x / bubble_rect.size.y + + bubble.material.set("shader_parameter/ratio", bubble_ratio) + diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.tscn b/addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.tscn new file mode 100644 index 0000000000000000000000000000000000000000..2ab71edd4b516112f7cb3b134b250a2454875e49 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.tscn @@ -0,0 +1,172 @@ +[gd_scene load_steps=18 format=3 uid="uid://dlx7jcvm52tyw"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/TextBubble/TextBubble.gd" id="1_jdhpk"] +[ext_resource type="Shader" path="res://addons/dialogic/Modules/DefaultStyles/TextBubble/speech_bubble.gdshader" id="2_1mhvf"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_dialog_text.gd" id="3_syv35"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_type_sound.gd" id="4_7bm4b"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultStyles/HideWithChild.gd" id="5_371j3"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_name_label.gd" id="6_5gd03"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Choice/node_choice_button.gd" id="7_0tnh1"] + +[sub_resource type="Curve" id="Curve_0j8nu"] +_data = [Vector2(0, 1), 0.0, -1.0, 0, 1, Vector2(1, 0), -1.0, 0.0, 1, 0] +point_count = 2 + +[sub_resource type="Curve" id="Curve_4meji"] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(1, 1), 2.61284, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="CurveTexture" id="CurveTexture_q6qf6"] +curve = SubResource("Curve_4meji") + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_lsfnp"] +noise_type = 0 +fractal_type = 0 +cellular_jitter = 0.15 + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_kr7hw"] +seamless = true +noise = SubResource("FastNoiseLite_lsfnp") + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_ejxnv"] +noise_type = 2 +frequency = 0.012 +fractal_type = 0 +cellular_jitter = 0.008 + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_4la3x"] +seamless = true +noise = SubResource("FastNoiseLite_ejxnv") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_60xbe"] +shader = ExtResource("2_1mhvf") +shader_parameter/radius = 1.39 +shader_parameter/ratio = Vector2(2.251, 1) +shader_parameter/crease = 0.12 +shader_parameter/speed = 2.53 +shader_parameter/texture_scale = 0.24 +shader_parameter/texture_offset = 172.5 +shader_parameter/deformation_sampler = SubResource("NoiseTexture2D_kr7hw") +shader_parameter/spikes_sampler = SubResource("NoiseTexture2D_4la3x") +shader_parameter/curve_sampler = SubResource("CurveTexture_q6qf6") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h6ls0"] +content_margin_left = 5.0 +content_margin_right = 5.0 +bg_color = Color(0.901961, 0.901961, 0.901961, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 +shadow_color = Color(0, 0, 0, 0.278431) +shadow_size = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g4yjl"] +draw_center = false +border_width_bottom = 3 +border_color = Color(0, 0, 0, 0.498039) + +[node name="TextBubble" type="Control"] +layout_mode = 3 +anchors_preset = 0 +script = ExtResource("1_jdhpk") + +[node name="Tail" type="Line2D" parent="."] +width = 96.0 +width_curve = SubResource("Curve_0j8nu") + +[node name="Background" type="ColorRect" parent="."] +material = SubResource("ShaderMaterial_60xbe") +layout_mode = 1 +offset_left = -69.0 +offset_top = -21.0 +offset_right = 225.0 +offset_bottom = 79.0 +mouse_filter = 2 + +[node name="DialogText" type="RichTextLabel" parent="."] +unique_name_in_owner = true +clip_contents = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -53.0 +offset_top = -13.0 +offset_right = 53.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/default_color = Color(0, 0, 0, 1) +text = "Some Text" +scroll_active = false +visible_characters_behavior = 1 +script = ExtResource("3_syv35") + +[node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="DialogText"] +script = ExtResource("4_7bm4b") + +[node name="NameLabel" type="PanelContainer" parent="DialogText"] +layout_mode = 1 +anchors_preset = -1 +offset_left = 16.0 +offset_top = -26.0 +offset_right = 27.0 +grow_horizontal = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_h6ls0") +script = ExtResource("5_371j3") + +[node name="NameLabel" type="Label" parent="DialogText/NameLabel"] +unique_name_in_owner = true +layout_mode = 2 +horizontal_alignment = 1 +script = ExtResource("6_5gd03") + +[node name="ChoiceContainer" type="HBoxContainer" parent="DialogText"] +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 5.0 +offset_top = -31.0 +offset_right = -4.0 +grow_horizontal = 2 +grow_vertical = 0 +alignment = 2 + +[node name="DialogicNode_ChoiceButton" type="Button" parent="DialogText/ChoiceContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_styles/focus = SubResource("StyleBoxFlat_g4yjl") +text = "A" +flat = true +script = ExtResource("7_0tnh1") + +[node name="DialogicNode_ChoiceButton2" type="Button" parent="DialogText/ChoiceContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_styles/focus = SubResource("StyleBoxFlat_g4yjl") +text = "A" +flat = true +script = ExtResource("7_0tnh1") + +[node name="DialogicNode_ChoiceButton3" type="Button" parent="DialogText/ChoiceContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_styles/focus = SubResource("StyleBoxFlat_g4yjl") +text = "A" +flat = true +script = ExtResource("7_0tnh1") + +[node name="DialogicNode_ChoiceButton4" type="Button" parent="DialogText/ChoiceContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_styles/focus = SubResource("StyleBoxFlat_g4yjl") +text = "A" +flat = true +script = ExtResource("7_0tnh1") + +[connection signal="started_revealing_text" from="DialogText" to="." method="_on_dialog_text_started_revealing_text"] diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png b/addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..32668e205e6ff351662d890c200f0381654dc4c2 Binary files /dev/null and b/addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png differ diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png.import b/addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png.import new file mode 100644 index 0000000000000000000000000000000000000000..94efa864e4a7121f221d14196f1bf37b7edcc04f --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bf4h16wsj1gc5" +path="res://.godot/imported/preview.png-af64fc5a61a1152f3e800d52664a3106.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/DefaultStyles/TextBubble/preview.png" +dest_files=["res://.godot/imported/preview.png-af64fc5a61a1152f3e800d52664a3106.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/speech_bubble.gdshader b/addons/dialogic/Modules/DefaultStyles/TextBubble/speech_bubble.gdshader new file mode 100644 index 0000000000000000000000000000000000000000..17c529aab010fa50608e7503c33437716e7f46b5 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/speech_bubble.gdshader @@ -0,0 +1,24 @@ +shader_type canvas_item; +uniform sampler2D deformation_sampler : filter_linear, repeat_enable; +uniform sampler2D spikes_sampler : filter_linear, repeat_enable; +uniform sampler2D curve_sampler; +uniform float radius :hint_range(0.1, 5, 0.01)= 0.25; +uniform vec2 ratio = vec2(1.0, 1.0); + +uniform float crease : hint_range(0.0, 1.0, 0.01) = 0.1; +uniform float speed : hint_range(0.0, 10.0, 0.01) = 1; +uniform float texture_scale : hint_range(0.0, 0.5, 0.01) = 0.2; +uniform float texture_offset : hint_range(0.0, 300, 0.5) = 1; + +void fragment() { + vec2 ratio_uv = UV * ratio; + float spikes = texture(spikes_sampler, ratio_uv * texture_scale + vec2(texture_offset)).x; +// + float d = length(max(abs(ratio_uv - vec2(0.5) * ratio) + radius - vec2(0.5) * ratio,0.0)) - radius; + d += (distance(vec2(0.5), UV) - 0.5) * radius; + float curve = texture(curve_sampler, vec2(d + spikes, 0.0)).x; + d += curve * crease; + d += texture(deformation_sampler, ratio_uv * 0.05 + TIME * speed*0.01).x * 0.1; + float mask = smoothstep(0.0, -0.005, d); + COLOR.a = mask; +} diff --git a/addons/dialogic/Modules/DefaultStyles/TextBubble/style.cfg b/addons/dialogic/Modules/DefaultStyles/TextBubble/style.cfg new file mode 100644 index 0000000000000000000000000000000000000000..dd1838723a2878c4c40561a69c0620ae85dce4c4 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/TextBubble/style.cfg @@ -0,0 +1,5 @@ +[style] +name = "Text Bubble" +author = "Jowan Spooner" +description = "An example textbubble. Only supports basic text and choice interactions (no portraits, text input, etc.)." +scene = "DialogicTextBubbleLayout.tscn" diff --git a/addons/dialogic/Modules/DefaultStyles/index.gd b/addons/dialogic/Modules/DefaultStyles/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..f58ac0ce55bbeffe3d15a29219f11636d11112f4 --- /dev/null +++ b/addons/dialogic/Modules/DefaultStyles/index.gd @@ -0,0 +1,4 @@ +extends DialogicIndexer + +func _get_layout_scenes() -> Array[Dictionary]: + return scan_for_layouts() diff --git a/addons/dialogic/Modules/End/event_end.gd b/addons/dialogic/Modules/End/event_end.gd new file mode 100644 index 0000000000000000000000000000000000000000..d24237b503addc23c1f8d7e77fe738555ee9dc4d --- /dev/null +++ b/addons/dialogic/Modules/End/event_end.gd @@ -0,0 +1,41 @@ +@tool +class_name DialogicEndTimelineEvent +extends DialogicEvent + +## Event that ends a timeline (even if more events come after). + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + for character in dialogic.Portraits.get_joined_characters(): + dialogic.Portraits.remove_character(character) + dialogic.end_timeline() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "End" + set_default_color('Color4') + event_category = "Flow" + event_sorting_index = 10 + + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "end_timeline" + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_label('End Timeline') diff --git a/addons/dialogic/Modules/End/icon.png b/addons/dialogic/Modules/End/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d97548051128613659ed7d84696b3acc64ff4279 Binary files /dev/null and b/addons/dialogic/Modules/End/icon.png differ diff --git a/addons/dialogic/Modules/End/icon.png.import b/addons/dialogic/Modules/End/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..650020d52dfbd5d56481cc18275640a2a0fab3fe --- /dev/null +++ b/addons/dialogic/Modules/End/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cstw2y41yacgc" +path="res://.godot/imported/icon.png-8e345f81e023043fdbb4f75b1b0e9bb0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/End/icon.png" +dest_files=["res://.godot/imported/icon.png-8e345f81e023043fdbb4f75b1b0e9bb0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/End/index.gd b/addons/dialogic/Modules/End/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..0997630f4f76b08a781ab743bd30dcf39ea6dfec --- /dev/null +++ b/addons/dialogic/Modules/End/index.gd @@ -0,0 +1,6 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_end.gd')] diff --git a/addons/dialogic/Modules/Glossary/add-glossary.svg b/addons/dialogic/Modules/Glossary/add-glossary.svg new file mode 100644 index 0000000000000000000000000000000000000000..f1b9f87a5510ce6cd991c7d53fd6e7dcbe50bd59 --- /dev/null +++ b/addons/dialogic/Modules/Glossary/add-glossary.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/dialogic/Modules/Glossary/add-glossary.svg.import b/addons/dialogic/Modules/Glossary/add-glossary.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..901b93208cb0733fdc6cb7a9fbcbd7170b4c1887 --- /dev/null +++ b/addons/dialogic/Modules/Glossary/add-glossary.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cenut3sc5cul0" +path="res://.godot/imported/add-glossary.svg-1cde77c043d3874d9bc84cc14d0ec9dc.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Glossary/add-glossary.svg" +dest_files=["res://.godot/imported/add-glossary.svg-1cde77c043d3874d9bc84cc14d0ec9dc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Glossary/event_glossary.gd b/addons/dialogic/Modules/Glossary/event_glossary.gd new file mode 100644 index 0000000000000000000000000000000000000000..f1c095eb884c845b8438b0c6cc6d110b40d68d5c --- /dev/null +++ b/addons/dialogic/Modules/Glossary/event_glossary.gd @@ -0,0 +1,43 @@ +@tool +class_name DialogicGlossaryEvent +extends DialogicEvent + +## Event that does nothing right now. + + +################################################################################ +## EXECUTE +################################################################################ + +func _execute() -> void: + pass + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Glossary" + set_default_color('Color6') + event_category = "Other" + event_sorting_index = 0 + expand_by_default = false + + +################################################################################ +## SAVING/LOADING +################################################################################ +func get_shortcode() -> String: + return "glossary" + +func get_shortcode_parameters() -> Dictionary: + return { + } + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + pass diff --git a/addons/dialogic/Modules/Glossary/glossary_editor.gd b/addons/dialogic/Modules/Glossary/glossary_editor.gd new file mode 100644 index 0000000000000000000000000000000000000000..fb61824a2257382c79921ae6863ee0ee3d43a5e4 --- /dev/null +++ b/addons/dialogic/Modules/Glossary/glossary_editor.gd @@ -0,0 +1,243 @@ +@tool +extends DialogicEditor + +var current_glossary :DialogicGlossary = null +var current_entry_name := "" + +################################################################################ +## BASICS +################################################################################ + +func _get_title() -> String: + return "Glossary" + + +func _get_icon() -> Texture: + return load(self.get_script().get_path().get_base_dir() + "/icon.svg") + + +func _register() -> void: + editors_manager.register_simple_editor(self) + alternative_text = "Create and edit glossaries." + + +func _ready() -> void: + %AddGlossaryFile.icon = load(self.get_script().get_path().get_base_dir() + "/add-glossary.svg") + %LoadGlossaryFile.icon = get_theme_icon('Folder', 'EditorIcons') + %DeleteGlossaryFile.icon = get_theme_icon('Remove', 'EditorIcons') + %DeleteGlossaryEntry.icon = get_theme_icon('Remove', 'EditorIcons') + + %AddGlossaryEntry.icon = get_theme_icon('Add', 'EditorIcons') + %EntrySearch.right_icon = get_theme_icon('Search', 'EditorIcons') + + %GlossaryList.item_selected.connect(_on_GlossaryList_item_selected) + %EntryList.item_selected.connect(_on_EntryList_item_selected) + + %DefaultColor.color_changed.connect(set_setting.bind('dialogic/glossary/default_color')) + %DefaultCaseSensitive.toggled.connect(set_setting.bind('dialogic/glossary/default_case_sensitive')) + + %EntryCaseSensitive.icon = get_theme_icon("MatchCase", "EditorIcons") + + +func set_setting(value, setting:String) -> void: + ProjectSettings.set_setting(setting, value) + ProjectSettings.save() + +func _open(argument:Variant = null) -> void: + %DefaultColor.color = ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE) + %DefaultCaseSensitive.button_pressed = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true) + + %GlossaryList.clear() + var idx := 0 + for file in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): + if FileAccess.file_exists(file): + %GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileList', 'EditorIcons')) + else: + %GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileDead', 'EditorIcons')) + + %GlossaryList.set_item_tooltip(idx, file) + idx += 1 + + %EntryList.clear() + + if %GlossaryList.item_count != 0: + %GlossaryList.select(0) + _on_GlossaryList_item_selected(0) + else: + current_glossary = null + hide_entry_editor() + +################################################################################ +## GLOSSARY LIST +################################################################################ +func _on_GlossaryList_item_selected(idx:int) -> void: + %EntryList.clear() + if FileAccess.file_exists(%GlossaryList.get_item_tooltip(idx)): + current_glossary = load(%GlossaryList.get_item_tooltip(idx)) + if not current_glossary is DialogicGlossary: + return + var entry_idx := 0 + for entry in current_glossary.entries: + %EntryList.add_item(entry, get_theme_icon("Breakpoint", "EditorIcons")) + %EntryList.set_item_icon_modulate(entry_idx, current_glossary.entries[entry].get('color', %DefaultColor.color)) + entry_idx += 1 + + if %EntryList.item_count != 0: + %EntryList.select(0) + _on_EntryList_item_selected(0) + else: + hide_entry_editor() + +func _on_add_glossary_file_pressed() -> void: + find_parent('EditorView').godot_file_dialog(create_new_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_SAVE_FILE, 'Create new glossary resource') + +func create_new_glossary_file(path:String) -> void: + var glossary := DialogicGlossary.new() + glossary.resource_path = path + ResourceSaver.save(glossary, path) + load_glossary_file(path) + +func _on_load_glossary_file_pressed() -> void: + find_parent('EditorView').godot_file_dialog(load_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_OPEN_FILE, 'Select glossary resource') + +func load_glossary_file(path:String) -> void: + var list :Array= ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + if not path in list: + list.append(path) + ProjectSettings.set_setting('dialogic/glossary/glossary_files', list) + ProjectSettings.save() + %GlossaryList.add_item(DialogicUtil.pretty_name(path), get_theme_icon('FileList', 'EditorIcons')) + %GlossaryList.set_item_tooltip(%GlossaryList.item_count-1, path) + %GlossaryList.select(%GlossaryList.item_count-1) + _on_GlossaryList_item_selected(%GlossaryList.item_count-1) + +func _on_delete_glossary_file_pressed() -> void: + if len(%GlossaryList.get_selected_items()) != 0: + var list :Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + list.erase(%GlossaryList.get_item_tooltip( + %GlossaryList.get_selected_items()[0])) + ProjectSettings.set_setting('dialogic/glossary/glossary_files', list) + ProjectSettings.save() + _open() + +################################################################################ +## ENTRY LIST +################################################################################ +func _on_EntryList_item_selected(idx:int) -> void: + current_entry_name = %EntryList.get_item_text(idx) + var entry_info = current_glossary.entries[current_entry_name] + %EntrySettings.show() + %EntryName.text = current_entry_name + %EntryCaseSensitive.button_pressed = entry_info.get('case_sensitive', %DefaultCaseSensitive.button_pressed) + var alts = "" + for i in entry_info.get('alternatives', []): + alts += i+", " + %EntryAlternatives.text = alts + %EntryTitle.text = entry_info.get('title', '') + %EntryText.text = entry_info.get('text', '') + %EntryExtra.text = entry_info.get('extra', '') + %EntryEnabled.button_pressed = entry_info.get('enabled', true) + + %EntryColor.color = entry_info.get('color', %DefaultColor.color) + %EntryCustomColor.button_pressed = entry_info.has('color') + %EntryColor.disabled = !entry_info.has('color') + +func _on_add_glossary_entry_pressed() -> void: + if !current_glossary: + return + + var new_name := "New Entry" + if new_name in current_glossary.entries: + var count := 2 + while new_name + " " +str(count) in current_glossary.entries: + count += 1 + new_name += " " + str(count) + current_glossary.entries[new_name] = {} + ResourceSaver.save(current_glossary) + %EntryList.add_item(new_name, get_theme_icon("Breakpoint", "EditorIcons")) + %EntryList.set_item_icon_modulate(%EntryList.item_count-1, %DefaultColor.color) + %EntryList.select(%EntryList.item_count-1) + _on_EntryList_item_selected(%EntryList.item_count-1) + %EntryList.ensure_current_is_visible() + %EntryName.grab_focus() + +func _on_delete_glossary_entry_pressed() -> void: + if len(%EntryList.get_selected_items()) != 0: + if current_glossary: + current_glossary.entries.erase(%EntryList.get_item_text( + %EntryList.get_selected_items()[0])) + %EntryList.remove_item(%EntryList.get_selected_items()[0]) + +func _on_entry_search_text_changed(new_text:String) -> void: + if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(%EntryList.get_selected_items()[0]).to_lower(): + return + for i in %EntryList.item_count: + if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(i).to_lower(): + %EntryList.select(i) + _on_EntryList_item_selected(i) + %EntryList.ensure_current_is_visible() + +################################################################################ +## ENTRY EDITOR +################################################################################ +func hide_entry_editor() -> void: + %EntrySettings.hide() + +func _on_entry_name_text_changed(new_text:String) -> void: + if current_entry_name != new_text.strip_edges(): + if new_text.strip_edges().is_empty() or new_text.strip_edges() in current_glossary.entries.keys(): + %EntryList.set_item_custom_bg_color(%EntryList.get_selected_items()[0], + get_theme_color("warning_color", "Editor").darkened(0.8)) + %EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges() + " (invalid name)") + return + else: + %EntryList.set_item_custom_bg_color(%EntryList.get_selected_items()[0], + Color.TRANSPARENT) + var info :Dictionary = current_glossary.entries[current_entry_name] + current_glossary.entries.erase(current_entry_name) + current_glossary.entries[new_text.strip_edges()] = info + %EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges()) + current_entry_name = new_text.strip_edges() + + ResourceSaver.save(current_glossary) + +func _on_entry_case_sensitive_toggled(button_pressed:bool) -> void: + current_glossary.entries[current_entry_name]['case_sensitive'] = button_pressed + ResourceSaver.save(current_glossary) + +func _on_entry_alternatives_text_changed(new_text:String) -> void: + var alts := [] + for i in new_text.split(',', false): + alts.append(i.strip_edges()) + current_glossary.entries[current_entry_name]['alternatives'] = alts + ResourceSaver.save(current_glossary) + +func _on_entry_title_text_changed(new_text:String) -> void: + current_glossary.entries[current_entry_name]['title'] = new_text + ResourceSaver.save(current_glossary) + +func _on_entry_text_text_changed() -> void: + current_glossary.entries[current_entry_name]['text'] = %EntryText.text + ResourceSaver.save(current_glossary) + +func _on_entry_extra_text_changed() -> void: + current_glossary.entries[current_entry_name]['extra'] = %EntryExtra.text + ResourceSaver.save(current_glossary) + +func _on_entry_enabled_toggled(button_pressed:bool) -> void: + current_glossary.entries[current_entry_name]['enabled'] = button_pressed + ResourceSaver.save(current_glossary) + +func _on_entry_custom_color_toggled(button_pressed:bool) -> void: + %EntryColor.disabled = !button_pressed + if !button_pressed: + current_glossary.entries[current_entry_name].erase('color') + %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %DefaultColor.color) + else: + current_glossary.entries[current_entry_name]['color'] = %EntryColor.color + %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %EntryColor.color) + +func _on_entry_color_color_changed(color:Color) -> void: + current_glossary.entries[current_entry_name]['color'] = color + %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], color) + ResourceSaver.save(current_glossary) diff --git a/addons/dialogic/Modules/Glossary/glossary_editor.tscn b/addons/dialogic/Modules/Glossary/glossary_editor.tscn new file mode 100644 index 0000000000000000000000000000000000000000..9bc8b9badacda76eac8271900f73cd3cb66cae0b --- /dev/null +++ b/addons/dialogic/Modules/Glossary/glossary_editor.tscn @@ -0,0 +1,320 @@ +[gd_scene load_steps=5 format=3 uid="uid://due48ce7jiudt"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/Glossary/glossary_editor.gd" id="1_tf3p1"] +[ext_resource type="Texture2D" uid="uid://cenut3sc5cul0" path="res://addons/dialogic/Modules/Glossary/add-glossary.svg" id="2_0elx7"] + +[sub_resource type="Image" id="Image_kueg2"] +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_ajdpw"] +image = SubResource("Image_kueg2") + +[node name="GlossaryEditor" type="VBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1_tf3p1") + +[node name="Entries" type="HSplitContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +split_offset = -200 + +[node name="Settings" type="VBoxContainer" parent="Entries"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.3 + +[node name="Label" type="Label" parent="Entries/Settings"] +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "Glossaries" + +[node name="Glossaries" type="PanelContainer" parent="Entries/Settings"] +layout_mode = 2 +size_flags_vertical = 3 +theme_type_variation = &"DialogicPanelA" + +[node name="Glossaries" type="VBoxContainer" parent="Entries/Settings/Glossaries"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +size_flags_stretch_ratio = 0.69 + +[node name="HBox" type="HBoxContainer" parent="Entries/Settings/Glossaries/Glossaries"] +layout_mode = 2 + +[node name="AddGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "New Glossary" +icon = ExtResource("2_0elx7") + +[node name="LoadGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "Import Glossary File" +icon = SubResource("ImageTexture_ajdpw") + +[node name="DeleteGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "Delete Glossary" +icon = SubResource("ImageTexture_ajdpw") + +[node name="ScrollContainer" type="ScrollContainer" parent="Entries/Settings/Glossaries/Glossaries"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="GlossaryList" type="ItemList" parent="Entries/Settings/Glossaries/Glossaries/ScrollContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Label2" type="Label" parent="Entries/Settings"] +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "Defaults" + +[node name="Defaults" type="VBoxContainer" parent="Entries/Settings"] +layout_mode = 2 + +[node name="DefaultsColor" type="HBoxContainer" parent="Entries/Settings/Defaults"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Entries/Settings/Defaults/DefaultsColor"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Color" + +[node name="DefaultColor" type="ColorPickerButton" parent="Entries/Settings/Defaults/DefaultsColor"] +unique_name_in_owner = true +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +size_flags_horizontal = 8 + +[node name="DefCaseSensitive" type="HBoxContainer" parent="Entries/Settings/Defaults"] +layout_mode = 2 + +[node name="Label" type="Label" parent="Entries/Settings/Defaults/DefCaseSensitive"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Case sensitive" + +[node name="DefaultCaseSensitive" type="CheckBox" parent="Entries/Settings/Defaults/DefCaseSensitive"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="HSplit" type="HSplitContainer" parent="Entries"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="Entries/HSplit"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label2" type="Label" parent="Entries/HSplit/VBoxContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "Entries" + +[node name="Tabs" type="PanelContainer" parent="Entries/HSplit/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_type_variation = &"DialogicPanelA" + +[node name="Entries" type="VBoxContainer" parent="Entries/HSplit/VBoxContainer/Tabs"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.69 + +[node name="HBox" type="HBoxContainer" parent="Entries/HSplit/VBoxContainer/Tabs/Entries"] +layout_mode = 2 + +[node name="AddGlossaryEntry" type="Button" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "New Glossary Entry" +icon = SubResource("ImageTexture_ajdpw") + +[node name="DeleteGlossaryEntry" type="Button" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "Delete Glossary Entry" +icon = SubResource("ImageTexture_ajdpw") + +[node name="EntrySearch" type="LineEdit" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Search" +right_icon = SubResource("ImageTexture_ajdpw") + +[node name="ScrollContainer" type="ScrollContainer" parent="Entries/HSplit/VBoxContainer/Tabs/Entries"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="EntryList" type="ItemList" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/ScrollContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +focus_neighbor_right = NodePath("../../../../EntryEditor/Tabs/Entry Settings/EntrySettings/HBox/EntryName") + +[node name="EntryEditor" type="ScrollContainer" parent="Entries/HSplit"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +horizontal_scroll_mode = 0 +metadata/_edit_layout_mode = 1 + +[node name="VBox" type="VBoxContainer" parent="Entries/HSplit/EntryEditor"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Label" type="Label" parent="Entries/HSplit/EntryEditor/VBox"] +layout_mode = 2 +theme_type_variation = &"DialogicSection" +text = "Entry Settings" + +[node name="Entry Settings" type="VBoxContainer" parent="Entries/HSplit/EntryEditor/VBox"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="EntrySettings" type="GridContainer" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/h_separation = 13 +columns = 2 + +[node name="Label2" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Name" + +[node name="HBox2" type="HBoxContainer" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 + +[node name="EntryName" type="LineEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_left = NodePath("../../../../../../VBoxContainer/Tabs/Entries/ScrollContainer/EntryList") +placeholder_text = "MyEntry" +caret_blink = true + +[node name="EntryCaseSensitive" type="Button" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Case sensitive" +toggle_mode = true +icon = SubResource("ImageTexture_ajdpw") +flat = true + +[node name="Label3" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Alternatives" + +[node name="EntryAlternatives" type="LineEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +caret_blink = true + +[node name="Label4" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Title" + +[node name="EntryTitle" type="LineEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +caret_blink = true + +[node name="Label5" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Description" + +[node name="EntryText" type="TextEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 100) +layout_mode = 2 +focus_next = NodePath("../EntryExtra") +wrap_mode = 1 + +[node name="Label6" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Extra" + +[node name="EntryExtra" type="TextEdit" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +wrap_mode = 1 + +[node name="Label8" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Enabled" + +[node name="EntryEnabled" type="CheckBox" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +unique_name_in_owner = true +layout_mode = 2 +button_pressed = true + +[node name="Label7" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Color" + +[node name="HBox" type="HBoxContainer" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] +layout_mode = 2 + +[node name="EntryCustomColor" type="CheckBox" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="EntryColor" type="ColorPickerButton" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/AddGlossaryFile" to="." method="_on_add_glossary_file_pressed"] +[connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/LoadGlossaryFile" to="." method="_on_load_glossary_file_pressed"] +[connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/DeleteGlossaryFile" to="." method="_on_delete_glossary_file_pressed"] +[connection signal="pressed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/AddGlossaryEntry" to="." method="_on_add_glossary_entry_pressed"] +[connection signal="pressed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/DeleteGlossaryEntry" to="." method="_on_delete_glossary_entry_pressed"] +[connection signal="text_changed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/EntrySearch" to="." method="_on_entry_search_text_changed"] +[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2/EntryName" to="." method="_on_entry_name_text_changed"] +[connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2/EntryCaseSensitive" to="." method="_on_entry_case_sensitive_toggled"] +[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryAlternatives" to="." method="_on_entry_alternatives_text_changed"] +[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryTitle" to="." method="_on_entry_title_text_changed"] +[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryText" to="." method="_on_entry_text_text_changed"] +[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryExtra" to="." method="_on_entry_extra_text_changed"] +[connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryEnabled" to="." method="_on_entry_enabled_toggled"] +[connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox/EntryCustomColor" to="." method="_on_entry_custom_color_toggled"] +[connection signal="color_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox/EntryColor" to="." method="_on_entry_color_color_changed"] diff --git a/addons/dialogic/Modules/Glossary/glossary_resource.gd b/addons/dialogic/Modules/Glossary/glossary_resource.gd new file mode 100644 index 0000000000000000000000000000000000000000..5228c1dd29a509f2dbf75b16308ff77ab697c86c --- /dev/null +++ b/addons/dialogic/Modules/Glossary/glossary_resource.gd @@ -0,0 +1,12 @@ +@tool +class_name DialogicGlossary +extends Resource + +## Resource used to store glossary entries. Can be saved to disc and used as a glossary. +## Add/create glossaries fom the glossaries editor + +## Stores all entry information +@export var entries :Dictionary = {} + +## If false, no entries from this glossary will be shown +@export var enabled :bool = true diff --git a/addons/dialogic/Modules/Glossary/icon.png.import b/addons/dialogic/Modules/Glossary/icon.png.import new file mode 100644 index 0000000000000000000000000000000000000000..e921f640401d89b54b472b4dd4d07a2d611121ca --- /dev/null +++ b/addons/dialogic/Modules/Glossary/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6wqvg2qcjxs" +path="res://.godot/imported/icon.png-624eb6dbf7e3ab27845a397653fa2fbb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Events/Glossary/icon.png" +dest_files=["res://.godot/imported/icon.png-624eb6dbf7e3ab27845a397653fa2fbb.ctex"] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/bptc_ldr=0 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/dialogic/Modules/Glossary/icon.svg b/addons/dialogic/Modules/Glossary/icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..175e284b0fa353c48bed7fd9589fb000a9dd70ca --- /dev/null +++ b/addons/dialogic/Modules/Glossary/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Modules/Glossary/icon.svg.import b/addons/dialogic/Modules/Glossary/icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..288e61f52a3e3d5a944c61904885e2f83761c36f --- /dev/null +++ b/addons/dialogic/Modules/Glossary/icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b5xwnxdb7064n" +path="res://.godot/imported/icon.svg-4fc0c12c53379638e37d654e7bbaea1a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Glossary/icon.svg" +dest_files=["res://.godot/imported/icon.svg-4fc0c12c53379638e37d654e7bbaea1a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Glossary/index.gd b/addons/dialogic/Modules/Glossary/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..4b0f997fb68e0b146cabb31781bb20a6c18a0d88 --- /dev/null +++ b/addons/dialogic/Modules/Glossary/index.gd @@ -0,0 +1,14 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [] +# return [this_folder.path_join('event_glossary.gd')] + +func _get_editors() -> Array: + return [this_folder.path_join('glossary_editor.tscn')] + +func _get_subsystems() -> Array: + return [{'name':'Glossary', 'script':this_folder.path_join('subsystem_glossary.gd')}] + diff --git a/addons/dialogic/Modules/Glossary/subsystem_glossary.gd b/addons/dialogic/Modules/Glossary/subsystem_glossary.gd new file mode 100644 index 0000000000000000000000000000000000000000..59be8d7eb6af954e0b118ac351c028a9c7132f12 --- /dev/null +++ b/addons/dialogic/Modules/Glossary/subsystem_glossary.gd @@ -0,0 +1,76 @@ +extends DialogicSubsystem + +## Subsystem that handles glossaries. + +## List of glossary resources that are used. +var glossaries := [] +## If false, no parsing will be done. +var enabled := true + + +#################################################################################################### +## STATE +#################################################################################################### + +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR) -> void: + glossaries = [] + for path in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): + add_glossary(path) + + +#################################################################################################### +## MAIN METHODS +#################################################################################################### + +func parse_glossary(text:String) -> String: + if !enabled: return text + var def_case_sensitive :bool = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true) + var def_color : Color= ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE) + var regex := RegEx.new() + for glossary in glossaries: + if !glossary.enabled: + continue + for entry in glossary.entries.keys(): + if !glossary.entries[entry].get('enabled', true): + continue + var pattern :String = '(?<=\\W|^)(?'+glossary.entries[entry].get('regopts', entry)+')(?!])(?=\\W|$)' + if glossary.entries[entry].get('case_sensitive', def_case_sensitive): + regex.compile(pattern) + else: + regex.compile('(?i)'+pattern) + text = regex.sub(text, + '[url=' + entry + ']' + + '[color=' + glossary.entries[entry].get('color', def_color).to_html() + ']${word}[/color]' + + '[/url]', true + ) + return text + + +func add_glossary(path:String) -> void: + if ResourceLoader.exists(path): + var x = load(path) + if x is DialogicGlossary: + glossaries.append(x) + for entry in x.entries.keys(): + var regopts :String = entry + for i in x.entries[entry].get('alternatives', []): + regopts += '|'+i + x.entries[entry]['regopts'] = regopts + else: + printerr('[Dialogic] The glossary file "' + path + '" is missing. Make sure it exists.') + + +func get_entry(name:String, parse_variables:bool = true) -> Dictionary: + for glossary in glossaries: + if name in glossary.entries: + var info:Dictionary = glossary.entries[name].duplicate() + if parse_variables and Dialogic.has_subsystem('VAR'): + for key in info.keys(): + if typeof(info[key]) == TYPE_STRING: + info[key] = Dialogic.VAR.parse_variables(info[key]) + return info + return {} + + +func set_entry(name: String, value: Dictionary) -> bool: + return false diff --git a/addons/dialogic/Modules/History/definition.svg b/addons/dialogic/Modules/History/definition.svg new file mode 100644 index 0000000000000000000000000000000000000000..236ca35a01af8f3b5384b6bf145fbebd21da9732 --- /dev/null +++ b/addons/dialogic/Modules/History/definition.svg @@ -0,0 +1,3 @@ + + + diff --git a/addons/dialogic/Modules/History/definition.svg.import b/addons/dialogic/Modules/History/definition.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..80aea9b9c5013ccd2e5db5f9535db6ff07bfa148 --- /dev/null +++ b/addons/dialogic/Modules/History/definition.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlwtdexd63bxi" +path="res://.godot/imported/definition.svg-dbaabe55d84e4ad95047a50fc6c13843.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/History/definition.svg" +dest_files=["res://.godot/imported/definition.svg-dbaabe55d84e4ad95047a50fc6c13843.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/dialogic/Modules/History/event_history.gd b/addons/dialogic/Modules/History/event_history.gd new file mode 100644 index 0000000000000000000000000000000000000000..7eabc2ebb305da47d5abfadff36d7ce8f9c065a1 --- /dev/null +++ b/addons/dialogic/Modules/History/event_history.gd @@ -0,0 +1,77 @@ +@tool +class_name DialogicHistoryEvent +extends DialogicEvent + +## Event that allows clearing, pausing and resuming of history functionality. + +enum Actions {CLEAR, PAUSE, RESUME} + +### Settings + +## The type of action: Clear, Pause or Resume +var action := Actions.PAUSE + + +################################################################################ +## EXECUTION +################################################################################ + +func _execute() -> void: + match action: + Actions.CLEAR: + dialogic.History.full_history = [] + Actions.PAUSE: + dialogic.History.full_history_enabled = false + Actions.RESUME: + dialogic.History.full_history_enabled = true + + finish() + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "History" + set_default_color('Color9') + event_category = "Other" + event_sorting_index = 20 + expand_by_default = false + + +################################################################################ +## SAVING/LOADING +################################################################################ + +func get_shortcode() -> String: + return "history" + +func get_shortcode_parameters() -> Dictionary: + return { + #param_name : property_info + "action" : {"property": "action", "default": Actions.PAUSE, + "suggestions": func(): return {"Clear":{'value':'0'}, "Pause":{'value':'1'}, "Resume":{'value':'2'}}}, + } + +################################################################################ +## EDITOR REPRESENTATION +################################################################################ + +func build_event_editor(): + add_header_edit('action', ValueType.FIXED_OPTION_SELECTOR, '', '', { + 'selector_options': [ + { + 'label': 'Pause History', + 'value': Actions.PAUSE, + }, + { + 'label': 'Resume History', + 'value': Actions.RESUME, + }, + { + 'label': 'Clear History', + 'value': Actions.CLEAR, + }, + ] + }) diff --git a/addons/dialogic/Modules/History/icon.svg b/addons/dialogic/Modules/History/icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..f49bd1a0dd9cfa321d3d477de9365ea822600569 --- /dev/null +++ b/addons/dialogic/Modules/History/icon.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/dialogic/Modules/History/icon.svg.import b/addons/dialogic/Modules/History/icon.svg.import new file mode 100644 index 0000000000000000000000000000000000000000..b7409f91f13075f2e726f0abe07368607bab5b38 --- /dev/null +++ b/addons/dialogic/Modules/History/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1n5bqdv34pmy" +path="res://.godot/imported/icon.svg-82841efe3f86e947d4f66fd24dc8f52c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/History/icon.svg" +dest_files=["res://.godot/imported/icon.svg-82841efe3f86e947d4f66fd24dc8f52c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/dialogic/Modules/History/index.gd b/addons/dialogic/Modules/History/index.gd new file mode 100644 index 0000000000000000000000000000000000000000..7791b1f73a408a06ffd92121c6729bc4caee970a --- /dev/null +++ b/addons/dialogic/Modules/History/index.gd @@ -0,0 +1,13 @@ +@tool +extends DialogicIndexer + + +func _get_events() -> Array: + return [this_folder.path_join('event_history.gd')] + + +func _get_subsystems() -> Array: + return [{'name':'History', 'script':this_folder.path_join('subsystem_history.gd')}] + +func _get_settings_pages() -> Array: + return [this_folder.path_join('settings_history.tscn')] diff --git a/addons/dialogic/Modules/History/settings_history.gd b/addons/dialogic/Modules/History/settings_history.gd new file mode 100644 index 0000000000000000000000000000000000000000..ba2ff8a54df5e679d234bfb196c5a3c181d1d34f --- /dev/null +++ b/addons/dialogic/Modules/History/settings_history.gd @@ -0,0 +1,23 @@ +@tool +extends DialogicSettingsPage + + +func _get_priority() -> int: + return -10 + + +func _ready() -> void: + %SimpleHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/simple_history_enabled')) + %FullHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/full_history_enabled')) + %AlreadyReadHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/already_read_history_enabled')) + + +func _refresh() -> void: + %SimpleHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', false) + %FullHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/full_history_enabled', false) + %AlreadyReadHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/already_read_history_enabled', false) + + +func setting_toggled(button_pressed:bool, setting:String) -> void: + ProjectSettings.set_setting(setting, button_pressed) + ProjectSettings.save() diff --git a/addons/dialogic/Modules/History/settings_history.tscn b/addons/dialogic/Modules/History/settings_history.tscn new file mode 100644 index 0000000000000000000000000000000000000000..dec920e31c4c47b54e5045eff326045d62699b93 --- /dev/null +++ b/addons/dialogic/Modules/History/settings_history.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=2 format=3 uid="uid://b5yq6xh412ilm"] + +[ext_resource type="Script" path="res://addons/dialogic/Modules/History/settings_history.gd" id="1_hbhst"] + +[node name="History" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_type_variation = &"DialogicPanelA" +script = ExtResource("1_hbhst") + +[node name="HistoryOptions" type="VBoxContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Title3" type="Label" parent="HistoryOptions"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Simple History" + +[node name="HBoxContainer" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="Label" type="Label" parent="HistoryOptions/HBoxContainer"] +layout_mode = 2 +text = "Enabled" + +[node name="SimpleHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="FullHistoryInfo2" type="Label" parent="HistoryOptions"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"DialogicHintText2" +text = "When enabled some events (Text, Join, Leave, Choice) will store a log that can easily be displayed. The default layout will have a history panel if this is enabled. " +autowrap_mode = 3 + +[node name="Title" type="Label" parent="HistoryOptions"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Full History" + +[node name="HBoxContainer5" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="Label" type="Label" parent="HistoryOptions/HBoxContainer5"] +layout_mode = 2 +text = "Enabled" + +[node name="FullHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer5"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="FullHistoryInfo" type="Label" parent="HistoryOptions"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"DialogicHintText2" +text = "When enabled a copy of each event that is played is stored. This can be used to implement some undo logic or other complitacted things. " +autowrap_mode = 3 + +[node name="Title2" type="Label" parent="HistoryOptions"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Already read History" + +[node name="HBoxContainer4" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="Label" type="Label" parent="HistoryOptions/HBoxContainer4"] +layout_mode = 2 +text = "Enabled" + +[node name="AlreadyReadHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer4"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="AlreadReadInfo" type="Label" parent="HistoryOptions"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"DialogicHintText2" +text = "When enabled the signals [Dialogic.History.already_read_event_reached] and [Dialogic.History.not_read_event_reached] are emmited. This could be used to auto-skip already read text or show an indicator." +autowrap_mode = 3 diff --git a/addons/dialogic/Modules/History/subsystem_history.gd b/addons/dialogic/Modules/History/subsystem_history.gd new file mode 100644 index 0000000000000000000000000000000000000000..62cf58c0c65afa75994ce38fa30a3fa0d5c8dbd4 --- /dev/null +++ b/addons/dialogic/Modules/History/subsystem_history.gd @@ -0,0 +1,86 @@ +extends DialogicSubsystem + +## Subsystem that manages history storing. + + +## Simple history that stores limited information +## Used for the history display +var simple_history_enabled := true +var simple_history_content : Array[Dictionary] = [] +signal simple_history_changed + +## Full event history (can be used for undo) +var full_event_history_enabled := false +var full_event_history_content := [] +signal full_event_history_changed + +## Read text history +## Stores which text events and choices have already been visited +var already_read_history_enabled := false +var already_read_history_content := {} +signal already_read_event_reached +signal not_read_event_reached + +#################################################################################################### +## INITIALIZE +#################################################################################################### + +func _ready() -> void: + Dialogic.event_handled.connect(store_full_event) + Dialogic.event_handled.connect(check_already_read) + + simple_history_enabled = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', false) + full_event_history_enabled = ProjectSettings.get_setting('dialogic/history/full_history_enabled', false) + already_read_history_enabled = ProjectSettings.get_setting('dialogic/history/already_read_history_enabled', false) + + +#################################################################################################### +## STATE +#################################################################################################### + +# nothing implemented right now + +#################################################################################################### +## SIMPLE HISTORY +#################################################################################################### + +func store_simple_history_entry(text:String, event_type:String, extra_info := {}) -> void: + if !simple_history_enabled: return + extra_info['text'] = text + extra_info['event_type'] = event_type + simple_history_content.append(extra_info) + simple_history_changed.emit() + + +func get_simple_history() -> Array: + return simple_history_content + + + +#################################################################################################### +## FULL EVENT HISTORY +#################################################################################################### + +# called on each event +func store_full_event(event:DialogicEvent) -> void: + if !full_event_history_enabled: return + full_event_history_content.append(event) + full_event_history_changed.emit() + + +#################################################################################################### +## ALREADY READ HISTORY +#################################################################################################### + +func event_was_read(event:DialogicEvent) -> void: + if !already_read_history_enabled: return + already_read_history_content[Dialogic.current_timeline.resource_path+str(Dialogic.current_event_idx)] = Dialogic.current_event_idx + + +# called on each event +func check_already_read(event:DialogicEvent) -> void: + if !already_read_history_enabled: return + if Dialogic.current_timeline.resource_path+str(Dialogic.current_event_idx) in already_read_history_content: + already_read_event_reached.emit() + else: + not_read_event_reached.emit() diff --git a/addons/dialogic/Modules/Jump/event_jump.gd b/addons/dialogic/Modules/Jump/event_jump.gd new file mode 100644 index 0000000000000000000000000000000000000000..1d18827d3fdbd6e0c790c1e615b825e706ef119a --- /dev/null +++ b/addons/dialogic/Modules/Jump/event_jump.gd @@ -0,0 +1,159 @@ +@tool +class_name DialogicJumpEvent +extends DialogicEvent + +## Event that allows starting another timeline. Also can jump to a label in that or the current timeline. + + +### Settings + +## The timeline to jump to, if null then it's the current one. This setting should be a dialogic timeline resource. +var timeline :DialogicTimeline = null: + get: + if timeline == null: + if !_timeline_file.is_empty(): + if _timeline_file.contains("res://"): + return load(_timeline_file) + else: + return load(Dialogic.find_timeline(_timeline_file)) + return timeline +## If not empty, the event will try to find a Label event with this set as name. Empty by default.. +var label_name : String = "" + + +### Helpers + +## Path to the timeline. Mainly used by the editor. +var _timeline_file: String = "" + + +################################################################################ +## EXECUTION +################################################################################ + +func _execute() -> void: + dialogic.Jump.push_to_jump_stack() + if timeline and timeline != dialogic.current_timeline: + dialogic.Jump.switched_timeline.emit({'previous_timeline':dialogic.current_timeline, 'timeline':timeline, 'label':label_name}) + dialogic.start_timeline(timeline, label_name) + else: + if label_name: + dialogic.Jump.jump_to_label(label_name) + finish() + else: + dialogic.start_timeline(dialogic.current_timeline) + + +################################################################################ +## INITIALIZE +################################################################################ + +func _init() -> void: + event_name = "Jump" + set_default_color('Color4') + event_category = "Flow" + event_sorting_index = 4 + expand_by_default = false + + +func _get_icon() -> Resource: + return load(self.get_script().get_path().get_base_dir().path_join('icon_jump.png')) + + +################################################################################ +## SAVING/LOADING +################################################################################ +func to_text() -> String: + var result := "jump " + if _timeline_file: + result += _timeline_file+'/' + if label_name: + result += label_name + elif label_name: + result += label_name + return result + + +func from_text(string:String) -> void: + var result := RegEx.create_from_string('jump (?\\w*\\/)?(?