diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index b35d4ce3d..80c251814 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,9 +1,9 @@ # These are supported funding model platforms -github: coppolaemilio -patreon: coppolaemilio +github: jowan-spooner +patreon: jowanspooner open_collective: # Replace with a single Open Collective username -ko_fi: coppolaemilio +ko_fi: jowan_spooner tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 50df48e53..d098e3f09 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -27,8 +27,8 @@ If applicable, add screenshots to help explain your problem. **System (please complete the following information):** - OS: [e.g. Windows, Linux] - - Godot Version: [e.g. 3.2.3] - - Dialogic Version: [e.g. 1.0] + - Godot Version: [e.g. 4.2.2] + - Dialogic Version: [e.g. 2.0 Alpha 14, please be specific!] ## Solutions diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ac4012363 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: true +contact_links: + - name: Dialogic 1 Repository + url: https://github.com/dialogic-godot/dialogic-1/issues + about: Please post anything related to Dialogc 1.x (Godot 3.x) on the Dialogic 1 Repository! + diff --git a/LICENSE b/LICENSE index ad41466f6..f001429b7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 - 2023 Emilio Coppola +Copyright (c) 2020 - present Emilio Coppola Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f54bc2dde..7d84d182d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ cover

-

Dialogic

+

Dialogic 2

Create Dialogs, Visual Novels, RPGs, and manage Characters with Godot to create your Game! @@ -28,9 +28,7 @@ Dialogic 2 **requires at least Godot 4.2**. -It's a major rewrite compared to the previous Dialogic 1. - -[If you are looking for the Godot 3.5 version you can find it here.](https://github.com/dialogic-godot/dialogic/tree/dialogic-1) +[If you are looking for the Godot 3.x version (Dialogic 1.x) you can find it here.](https://github.com/dialogic-godot/dialogic-1) ## Installation Follow the installation instructions on our [Getting Started](https://docs.dialogic.pro/getting-started.html#1-installation--activation) documentation. @@ -68,9 +66,9 @@ Changelogs will accommodate for these changes and inform you on how to update yo ## Credits -Made by [Emilio Coppola](https://github.com/coppolaemilio) and [Jowan-Spooner](https://github.com/Jowan-Spooner). +Made by [Jowan-Spooner](https://github.com/Jowan-Spooner) and [Emilio Coppola](https://github.com/coppolaemilio). -Contributors: [zaknafean](https://github.com/zaknafean), [thebardsrc](https://github.com/thebardsrc), [and more!](https://github.com/dialogic-godot/dialogic/graphs/contributors). +Contributors: [CakeVR](https://github.com/CakeVR), [Exelia](https://github.com/exelia-antonov), [zaknafean](https://github.com/zaknafean), [and more!](https://github.com/dialogic-godot/dialogic/graphs/contributors). Special thanks: [Arnaud](https://github.com/arnaudvergnet), [AnidemDex](https://github.com/AnidemDex), [ellogwen](https://github.com/ellogwen), [Tim Krief](https://github.com/timkrief), [Toen](https://twitter.com/ToenAndreMC), Òscar, [Francisco Presencia](https://francisco.io/), [M7mdKady14](https://github.com/M7mdKady14). diff --git a/Tests/Unit/guess_special_resource_test.gd b/Tests/Unit/guess_special_resource_test.gd new file mode 100644 index 000000000..41749c7d0 --- /dev/null +++ b/Tests/Unit/guess_special_resource_test.gd @@ -0,0 +1,40 @@ +extends GdUnitTestSuite + +## Check if transition animations can be accessed with "in", "out, "in out" +## as space-delimited prefix. +func test_fade_in_animation_paths() -> void: + const TYPE := "PortraitAnimation" + var fade_in_1: String = DialogicResourceUtil.guess_special_resource(TYPE, "fade in").get('path', "") + var fade_in_2: String = DialogicResourceUtil.guess_special_resource(TYPE, "fade cross").get('path', "") + var fade_in_3: String = DialogicResourceUtil.guess_special_resource(TYPE, "fade out").get('path', "") + + var is_any_fade_in_empty := fade_in_1.is_empty() or fade_in_2.is_empty() or fade_in_3.is_empty() + assert(is_any_fade_in_empty == false, "Fade In/Out animations are empty.") + + var are_all_fade_in_equal := fade_in_1 == fade_in_2 and fade_in_2 == fade_in_3 + assert(are_all_fade_in_equal == true, "Fade In/Out animations returned different paths.") + + +## Test if invalid animation paths will return empty strings. +func test_invalid_animation_path() -> void: + const TYPE := "PortraitAnimation" + var invalid_animation_1: String = DialogicResourceUtil.guess_special_resource(TYPE, "fade i").get('path', "") + assert(invalid_animation_1.is_empty() == true, "Invalid animation 1's path is not empty.") + + + var invalid_animation_2: String = DialogicResourceUtil.guess_special_resource(TYPE, "fade").get('path', "") + assert(invalid_animation_2.is_empty() == true, "Invalid animation 2's path is not empty.") + + +## Test if invalid types will return empty strings. +func test_invalid_type_path() -> void: + const INVALID_TYPE := "Portait Animation" + var invalid_animation: String = DialogicResourceUtil.guess_special_resource(INVALID_TYPE, "fade in").get('path', "") + assert(invalid_animation.is_empty() == true, "Invalid animation 1's path is not empty.") + + const VALID_TYPE := "PortraitAnimation" + var valid_animation_path: String = DialogicResourceUtil.guess_special_resource(VALID_TYPE, "fade in").get('path', "") + assert(valid_animation_path.is_empty() == false, "Valids animation's path is empty.") + + assert(not invalid_animation == valid_animation_path, "Valid and invalid animation paths are equal.") + diff --git a/addons/dialogic/Core/DialogicGameHandler.gd b/addons/dialogic/Core/DialogicGameHandler.gd index b9e582c80..43c255744 100644 --- a/addons/dialogic/Core/DialogicGameHandler.gd +++ b/addons/dialogic/Core/DialogicGameHandler.gd @@ -104,6 +104,9 @@ var Backgrounds := preload("res://addons/dialogic/Modules/Background/subsystem_b var Portraits := preload("res://addons/dialogic/Modules/Character/subsystem_portraits.gd").new(): get: return get_subsystem("Portraits") +var PortraitContainers := preload("res://addons/dialogic/Modules/Character/subsystem_containers.gd").new(): + get: return get_subsystem("PortraitContainers") + var Choices := preload("res://addons/dialogic/Modules/Choice/subsystem_choices.gd").new(): get: return get_subsystem("Choices") @@ -151,8 +154,6 @@ var Voice := preload("res://addons/dialogic/Modules/Voice/subsystem_voice.gd").n ## Autoloads are added first, so this happens REALLY early on game startup. func _ready() -> void: - DialogicResourceUtil.update() - _collect_subsystems() clear() @@ -166,7 +167,7 @@ func _ready() -> void: ## -> returns the layout node func start(timeline:Variant, label:Variant="") -> Node: # If we don't have a style subsystem, default to just start_timeline() - if !has_subsystem('Styles'): + if not has_subsystem('Styles'): printerr("[Dialogic] You called Dialogic.start() but the Styles subsystem is missing!") clear(ClearFlags.KEEP_VARIABLES) start_timeline(timeline, label) @@ -178,12 +179,12 @@ func start(timeline:Variant, label:Variant="") -> Node: scene = self.Styles.load_style() else: scene = self.Styles.get_layout_node() + scene.show() if not scene.is_node_ready(): scene.ready.connect(clear.bind(ClearFlags.KEEP_VARIABLES)) scene.ready.connect(start_timeline.bind(timeline, label)) else: - clear(ClearFlags.KEEP_VARIABLES) start_timeline(timeline, label) return scene @@ -205,10 +206,12 @@ func start_timeline(timeline:Variant, label_or_idx:Variant = "") -> void: printerr("[Dialogic] There was an error loading this timeline. Check the filename, and the timeline for errors") return - await (timeline as DialogicTimeline).process() + (timeline as DialogicTimeline).process() current_timeline = timeline current_timeline_events = current_timeline.events + for event in current_timeline_events: + event.dialogic = self current_event_idx = -1 if typeof(label_or_idx) == TYPE_STRING: @@ -232,15 +235,15 @@ func preload_timeline(timeline_resource:Variant) -> Variant: if timeline_resource == null: printerr("[Dialogic] There was an error preloading this timeline. Check the filename, and the timeline for errors") return null - else: - await (timeline_resource as DialogicTimeline).process() - return timeline_resource + + (timeline_resource as DialogicTimeline).process() + return timeline_resource ## Clears and stops the current timeline. func end_timeline() -> void: - clear(ClearFlags.TIMELINE_INFO_ONLY) + await clear(ClearFlags.TIMELINE_INFO_ONLY) _on_timeline_ended() timeline_ended.emit() @@ -256,8 +259,7 @@ func handle_event(event_index:int) -> void: if not current_timeline: return - if has_meta('previous_event') and get_meta('previous_event') is DialogicEvent and (get_meta('previous_event') as DialogicEvent).event_finished.is_connected(handle_next_event): - (get_meta('previous_event') as DialogicEvent).event_finished.disconnect(handle_next_event) + _cleanup_previous_event() if paused: await dialogic_resumed @@ -286,22 +288,34 @@ func handle_event(event_index:int) -> void: ## By using the clear flags from the [member ClearFlags] enum you can specify ## what info should be kept. ## For example, at timeline end usually it doesn't clear node or subsystem info. -func clear(clear_flags := ClearFlags.FULL_CLEAR) -> bool: +func clear(clear_flags := ClearFlags.FULL_CLEAR) -> void: + _cleanup_previous_event() if !clear_flags & ClearFlags.TIMELINE_INFO_ONLY: for subsystem in get_children(): if subsystem is DialogicSubsystem: (subsystem as DialogicSubsystem).clear_game_state(clear_flags) - # Resetting variables - if current_timeline: - current_timeline.clean() + var timeline := current_timeline current_timeline = null current_event_idx = -1 current_timeline_events = [] current_state = States.IDLE - return true + + # Resetting variables + if timeline: + await timeline.clean() + + +## Cleanup after previous event (if any). +func _cleanup_previous_event(): + if has_meta('previous_event') and get_meta('previous_event') is DialogicEvent: + var event := get_meta('previous_event') as DialogicEvent + if event.event_finished.is_connected(handle_next_event): + event.event_finished.disconnect(handle_next_event) + event._clear_state() + remove_meta("previous_event") #endregion @@ -320,6 +334,9 @@ func get_full_state() -> Dictionary: current_state_info['current_event_idx'] = -1 current_state_info['current_timeline'] = null + for subsystem in get_children(): + (subsystem as DialogicSubsystem).save_game_state() + return current_state_info.duplicate(true) @@ -406,4 +423,11 @@ func _on_timeline_ended() -> void: @warning_ignore("unsafe_method_access") self.Styles.get_layout_node().hide() + +func print_debug_moment() -> void: + if not current_timeline: + return + + printerr("\tAt event ", current_event_idx+1, " (",current_timeline_events[current_event_idx].event_name, ' Event) in timeline "', DialogicResourceUtil.get_unique_identifier(current_timeline.resource_path), '" (',current_timeline.resource_path,').') + print("\n") #endregion diff --git a/addons/dialogic/Core/DialogicResourceUtil.gd b/addons/dialogic/Core/DialogicResourceUtil.gd index c020b8a60..a86356cbf 100644 --- a/addons/dialogic/Core/DialogicResourceUtil.gd +++ b/addons/dialogic/Core/DialogicResourceUtil.gd @@ -4,7 +4,7 @@ class_name DialogicResourceUtil static var label_cache := {} static var event_cache: Array[DialogicEvent] = [] -static var special_resources : Array[Dictionary] = [] +static var special_resources := {} static func update() -> void: @@ -156,7 +156,7 @@ static func update_event_cache() -> Array: for indexer in DialogicUtil.get_indexers(): # build event cache for event in indexer._get_events(): - if not FileAccess.file_exists(event): + if not ResourceLoader.exists(event): continue if not 'event_end_branch.gd' in event and not 'event_text.gd' in event: event_cache.append(load(event).new()) @@ -173,25 +173,78 @@ static func update_event_cache() -> Array: ################################################################################ static func update_special_resources() -> void: - special_resources = [] + special_resources.clear() for indexer in DialogicUtil.get_indexers(): - special_resources.append_array(indexer._get_special_resources()) + var additions := indexer._get_special_resources() + for resource_type in additions: + if not resource_type in special_resources: + special_resources[resource_type] = {} + special_resources[resource_type].merge(additions[resource_type]) -static func list_special_resources_of_type(type:String) -> Array: +static func list_special_resources(type:String, filter := {}) -> Dictionary: if special_resources.is_empty(): update_special_resources() - return special_resources.filter(func(x:Dictionary): return type == x.get('type','')).map(func(x:Dictionary): return x.get('path', '')) + if type in special_resources: + if filter.is_empty(): + return special_resources[type] + else: + var results := {} + for i in special_resources[type]: + if match_resource_filter(special_resources[type][i], filter): + results[i] = special_resources[type][i] + return results + return {} + + +static func match_resource_filter(dict:Dictionary, filter:Dictionary) -> bool: + for i in filter: + if not i in dict: + return false + if typeof(filter[i]) == TYPE_ARRAY: + if not dict[i] in filter[i]: + return false + else: + if not dict[i] == filter[i]: + return false + return true + + +static func guess_special_resource(type: String, string: String, default := {}, filter := {}, ignores:PackedStringArray=[]) -> Dictionary: + if string.is_empty(): + return default - -static func guess_special_resource(type:String, name:String, default:="") -> String: if special_resources.is_empty(): update_special_resources() - if name.begins_with('res://'): - return name - for path in list_special_resources_of_type(type): - if DialogicUtil.pretty_name(path).to_lower() == name.to_lower(): - return path + var resources := list_special_resources(type, filter) + if resources.is_empty(): + printerr("[Dialogic] No ", type, "s found, but attempted to use one.") + return default + + if string.begins_with('res://'): + for i in resources.values(): + if i.path == string: + return i + printerr("[Dialogic] Unable to find ", type, " at path '", string, "'.") + return default + + string = string.to_lower() + + if string in resources: + return resources[string] + + if not ignores.is_empty(): + var regex := RegEx.create_from_string(r" ?\b(" + "|".join(ignores) + r")\b") + for name in resources: + if regex.sub(name, "") == regex.sub(string, ""): + return resources[name] + + ## As a last effort check against the unfiltered list + if string in special_resources[type]: + push_warning("[Dialogic] Using ", type, " '", string,"' when not supposed to.") + return special_resources[type][string] + + printerr("[Dialogic] Unable to identify ", type, " based on string '", string, "'.") return default #endregion diff --git a/addons/dialogic/Core/DialogicUtil.gd b/addons/dialogic/Core/DialogicUtil.gd index 7dcbe3650..3924f48d4 100644 --- a/addons/dialogic/Core/DialogicUtil.gd +++ b/addons/dialogic/Core/DialogicUtil.gd @@ -5,7 +5,9 @@ class_name DialogicUtil ## Used whenever the same thing is needed in different parts of the plugin. #region EDITOR -################################################################################ + +## This method should be used instead of EditorInterface.get_editor_scale(), because if you use that +## it will run perfectly fine from the editor, but crash when the game is exported. static func get_editor_scale() -> float: return get_dialogic_plugin().get_editor_interface().get_editor_scale() @@ -32,7 +34,7 @@ static func autoload() -> DialogicGameHandler: #region FILE SYSTEM ################################################################################ -static func listdir(path: String, files_only:= true, throw_error:= true, full_file_path:= false, include_imports := false) -> Array: +static func listdir(path: String, files_only:= true, _throw_error:= true, full_file_path:= false, include_imports := false) -> Array: var files: Array = [] if path.is_empty(): path = "res://" if DirAccess.dir_exists_absolute(path): @@ -82,7 +84,7 @@ static func _update_autoload_subsystem_access() -> void: new_subsystem_access_list += '\nvar {name} := preload("{script}").new():\n\tget: return get_subsystem("{name}")\n'.format(subsystem) new_subsystem_access_list += "\n#endregion" - script.source_code = RegEx.create_from_string("#region SUBSYSTEMS\\n#*\\n((?!#endregion)(.*\\n))*#endregion").sub(script.source_code, new_subsystem_access_list) + script.source_code = RegEx.create_from_string(r"#region SUBSYSTEMS\n#*\n((?!#endregion)(.*\n))*#endregion").sub(script.source_code, new_subsystem_access_list) ResourceSaver.save(script) Engine.get_singleton("EditorInterface").get_resource_filesystem().reimport_files(["res://addons/dialogic/Core/DialogicGameHandler.gd"]) @@ -95,38 +97,28 @@ static func get_indexers(include_custom := true, force_reload := false) -> Array for file in listdir(DialogicUtil.get_module_path(''), false): var possible_script: String = DialogicUtil.get_module_path(file).path_join("index.gd") - if FileAccess.file_exists(possible_script): + if ResourceLoader.exists(possible_script): indexers.append(load(possible_script).new()) if include_custom: var extensions_folder: String = ProjectSettings.get_setting('dialogic/extensions_folder', "res://addons/dialogic_additions/") for file in listdir(extensions_folder, false, false): var possible_script: String = extensions_folder.path_join(file + "/index.gd") - if FileAccess.file_exists(possible_script): + if ResourceLoader.exists(possible_script): indexers.append(load(possible_script).new()) Engine.get_main_loop().set_meta('dialogic_indexers', indexers) return indexers -enum AnimationType {ALL, IN, OUT, ACTION} -static func get_portrait_animation_scripts(type:=AnimationType.ALL, include_custom:=true) -> Array: - var animations := DialogicResourceUtil.list_special_resources_of_type("PortraitAnimation") - - return animations.filter( - func(script): - if type == AnimationType.ALL: return true; - if type == AnimationType.IN: return '_in' in script; - if type == AnimationType.OUT: return '_out' in script; - if type == AnimationType.ACTION: return not ('_in' in script or '_out' in script)) - -static func pretty_name(script:String) -> String: - var _name := script.get_file().trim_suffix("."+script.get_extension()) +## Turns a [param file_path] from `some_file.png` to `Some File`. +static func pretty_name(file_path: String) -> String: + var _name := file_path.get_file().trim_suffix("." + file_path.get_extension()) _name = _name.replace('_', ' ') _name = _name.capitalize() - return _name + return _name #endregion @@ -193,6 +185,29 @@ static func update_timer_process_callback(timer:Timer) -> void: #endregion +#region MULTITWEEN +################################################################################ +static func multitween(tweened_value:Variant, item:Node, property:String, part:String) -> void: + var parts: Dictionary = item.get_meta(property+'_parts', {}) + parts[part] = tweened_value + + if not item.has_meta(property+'_base_value') and not 'base' in parts: + item.set_meta(property+'_base_value', item.get(property)) + + var final_value: Variant = parts.get('base', item.get_meta(property+'_base_value', item.get(property))) + + for key in parts: + if key == 'base': + continue + else: + final_value += parts[key] + + item.set(property, final_value) + item.set_meta(property+'_parts', parts) + +#endregion + + #region TRANSLATIONS ################################################################################ @@ -225,7 +240,7 @@ static func list_variables(dict:Dictionary, path := "", type:=VarTypes.ANY) -> A return array -static func get_variable_value_type(value:Variant) -> int: +static func get_variable_value_type(value:Variant) -> VarTypes: match typeof(value): TYPE_STRING: return VarTypes.STRING @@ -339,7 +354,7 @@ static func get_scene_export_defaults(node:Node) -> Dictionary: if !Engine.get_main_loop().has_meta('dialogic_scene_export_defaults'): Engine.get_main_loop().set_meta('dialogic_scene_export_defaults', {}) var defaults := {} - var property_info :Array[Dictionary] = node.script.get_script_property_list() + var property_info: Array[Dictionary] = node.script.get_script_property_list() for i in property_info: if i['usage'] & PROPERTY_USAGE_EDITOR: defaults[i['name']] = node.get(i['name']) @@ -348,6 +363,74 @@ static func get_scene_export_defaults(node:Node) -> Dictionary: #endregion +#region MAKE CUSTOM + +static func make_file_custom(original_file:String, target_folder:String, new_file_name := "", new_folder_name := "") -> String: + if not ResourceLoader.exists(original_file): + push_error("[Dialogic] Unable to make file with invalid path custom!") + return "" + + if new_folder_name: + target_folder = target_folder.path_join(new_folder_name) + DirAccess.make_dir_absolute(target_folder) + + if new_file_name.is_empty(): + new_file_name = "custom_" + original_file.get_file() + + if not new_file_name.ends_with(original_file.get_extension()): + new_file_name += "." + original_file.get_extension() + + var target_file := target_folder.path_join(new_file_name) + + customize_file(original_file, target_file) + + get_dialogic_plugin().get_editor_interface().get_resource_filesystem().scan_sources() + + return target_file + + +static func customize_file(original_file:String, target_file:String) -> String: + #print("\nCUSTOMIZE FILE") + #printt(original_file, "->", target_file) + + DirAccess.copy_absolute(original_file, target_file) + + var file := FileAccess.open(target_file, FileAccess.READ) + var file_text := file.get_as_text() + file.close() + + # If we are customizing a scene, we check for any resources used in that scene that are in the same folder. + # Those will be copied as well and the scene will be modified to point to them. + if file_text.begins_with('[gd_'): + var base_path: String = original_file.get_base_dir() + + var remove_uuid_regex := r'\[gd_.* (?uid="uid:[^"]*")' + var result := RegEx.create_from_string(remove_uuid_regex).search(file_text) + if result: + file_text = file_text.replace(result.get_string("uid"), "") + + # This regex also removes the UID referencing the original resource + var file_regex := r'(uid="[^"]*" )?\Qpath="'+base_path+r'\E(?[^"]*)"' + result = RegEx.create_from_string(file_regex).search(file_text) + while result: + var found_file_name := result.get_string('file') + var found_file_path := base_path.path_join(found_file_name) + var target_file_path := target_file.get_base_dir().path_join(found_file_name) + + # Files found in this file will ALSO be customized. + customize_file(found_file_path, target_file_path) + + file_text = file_text.replace(found_file_path, target_file_path) + + result = RegEx.create_from_string(file_regex).search(file_text) + + file = FileAccess.open(target_file, FileAccess.WRITE) + file.store_string(file_text) + file.close() + + return target_file + +#endregion #region INSPECTOR FIELDS ################################################################################ @@ -365,7 +448,7 @@ static func setup_script_property_edit_node(property_info: Dictionary, value:Var if value != null: input.color = value input.color_changed.connect(DialogicUtil._on_export_color_submitted.bind(property_info.name, property_changed)) - input.custom_minimum_size.x = get_editor_scale()*50 + input.custom_minimum_size.x = get_editor_scale() * 50 TYPE_INT: if property_info['hint'] & PROPERTY_HINT_ENUM: input = OptionButton.new() @@ -432,9 +515,14 @@ static func setup_script_property_edit_node(property_info: Dictionary, value:Var if value != null: input.text = value input.text_submitted.connect(DialogicUtil._on_export_input_text_submitted.bind(property_info.name, property_changed)) + TYPE_DICTIONARY: + input = load("res://addons/dialogic/Editor/Events/Fields/field_dictionary.tscn").instantiate() + input.property_name = property_info["name"] + input.value_changed.connect(_on_export_dict_submitted.bind(property_changed)) TYPE_OBJECT: input = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate() input.hint_text = "Objects/Resources as settings are currently not supported. \nUse @export_file('*.extension') instead and load the resource once needed." + _: input = LineEdit.new() if value != null: @@ -467,6 +555,9 @@ static func _on_export_string_enum_submitted(value:int, property_name:String, li static func _on_export_vector_submitted(property_name:String, value:Variant, callable: Callable) -> void: callable.call(property_name, var_to_str(value)) +static func _on_export_dict_submitted(property_name:String, value:Variant, callable: Callable) -> void: + callable.call(property_name, var_to_str(value)) + #endregion @@ -539,3 +630,71 @@ static func str_to_hash_set(source: String) -> Dictionary: return dictionary #endregion + + +static func get_character_suggestions(_search_text:String, current_value:DialogicCharacter = null, allow_none := true, allow_all:= false, editor_node:Node = null) -> Dictionary: + var suggestions := {} + + var icon := load("res://addons/dialogic/Editor/Images/Resources/character.svg") + + if allow_none and current_value: + suggestions['(No one)'] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + + if allow_all: + suggestions['ALL'] = {'value':'--All--', 'tooltip':'All currently joined characters leave', 'editor_icon':["GuiEllipsis", "EditorIcons"]} + + # Get characters in the current timeline and place them at the top of suggestions. + if editor_node: + var recent_characters := [] + var timeline_node := editor_node.get_parent().find_parent("Timeline") as DialogicEditor + for event_node in timeline_node.find_child("Timeline").get_children(): + if event_node == editor_node: + break + if event_node.resource is DialogicCharacterEvent or event_node.resource is DialogicTextEvent: + recent_characters.append(event_node.resource.character) + + recent_characters.reverse() + for character in recent_characters: + if character and not character.get_character_name() in suggestions: + suggestions[character.get_character_name()] = {'value': character.get_character_name(), 'tooltip': character.resource_path, 'icon': icon.duplicate()} + + var character_directory := DialogicResourceUtil.get_character_directory() + for resource in character_directory.keys(): + suggestions[resource] = {'value': resource, 'tooltip': character_directory[resource], 'icon': icon} + + return suggestions + + +static func get_portrait_suggestions(search_text:String, character:DialogicCharacter, allow_empty := false, empty_text := "Don't Change") -> Dictionary: + var icon := load("res://addons/dialogic/Editor/Images/Resources/portrait.svg") + var suggestions := {} + + if allow_empty: + suggestions[empty_text] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + + if "{" in search_text: + suggestions[search_text] = {'value':search_text, 'editor_icon':["Variant", "EditorIcons"]} + + if character != null: + for portrait in character.portraits: + suggestions[portrait] = {'value':portrait, 'icon':icon} + + return suggestions + + +static func get_portrait_position_suggestions(search_text := "") -> Dictionary: + var icon := load(DialogicUtil.get_module_path("Character").path_join('portrait_position.svg')) + + var setting: String = ProjectSettings.get_setting('dialogic/portraits/position_suggestion_names', 'leftmost, left, center, right, rightmost') + + var suggestions := {} + + if not search_text.is_empty(): + suggestions[search_text] = {'value':search_text.strip_edges(), 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]} + + for position_id in setting.split(','): + suggestions[position_id.strip_edges()] = {'value':position_id.strip_edges(), 'icon':icon} + if not search_text.is_empty() and position_id.strip_edges().begins_with(search_text): + suggestions.erase(search_text) + + return suggestions diff --git a/addons/dialogic/Core/Dialogic_Subsystem.gd b/addons/dialogic/Core/Dialogic_Subsystem.gd index 4b742a788..3ff55e60d 100644 --- a/addons/dialogic/Core/Dialogic_Subsystem.gd +++ b/addons/dialogic/Core/Dialogic_Subsystem.gd @@ -13,14 +13,21 @@ func post_install() -> void: # To be overriden by sub-classes # Fill in everything that should be cleared (for example before loading a different state) -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: +func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: pass # To be overriden by sub-classes # Fill in everything that should be loaded using the dialogic_game_handler.current_state_info # This is called when a save is loaded -func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void: +func load_game_state(_load_flag:=LoadFlags.FULL_LOAD) -> void: + pass + + +# To be overriden by sub-classes +# Fill in everything that should be saved into the dialogic_game_handler.current_state_info +# This is called when a save is saved +func save_game_state() -> void: pass diff --git a/addons/dialogic/Core/index_class.gd b/addons/dialogic/Core/index_class.gd index efc8adc89..30c8fa088 100644 --- a/addons/dialogic/Core/index_class.gd +++ b/addons/dialogic/Core/index_class.gd @@ -7,13 +7,13 @@ extends RefCounted ## Overwrite the methods to return the contents of that folder. -var this_folder : String = get_script().resource_path.get_base_dir() +var this_folder: String = get_script().resource_path.get_base_dir() ## Overwrite if this module contains any events. [br] ## Return an array with all the paths to the event scripts.[br] ## You can use the [property this_folder].path_join('my_event.gd') func _get_events() -> Array: - if FileAccess.file_exists(this_folder.path_join('event.gd')): + if ResourceLoader.exists(this_folder.path_join('event.gd')): return [this_folder.path_join('event.gd')] return [] @@ -59,7 +59,12 @@ func _get_text_modifiers() -> Array[Dictionary]: ## These can later be retrieved with DialogicResourceUtil. ## Each dictionary should contain (at least "type" and "path"). ## E.g. {"type":"Animation", "path": "res://..."} -func _get_special_resources() -> Array[Dictionary]: +func _get_special_resources() -> Dictionary: + return {} + + +## Return a list of dictionaries, each +func _get_portrait_scene_presets() -> Array[Dictionary]: return [] @@ -70,12 +75,26 @@ func list_dir(subdir:='') -> Array: return Array(DirAccess.get_files_at(this_folder.path_join(subdir))).map(func(file):return this_folder.path_join(subdir).path_join(file)) -func list_special_resources(subdir:='', type:='', extension:="") -> Array[Dictionary]: - var array := [] +func list_special_resources(subdir:='', extension:="") -> Dictionary: + var dict := {} for i in list_dir(subdir): if extension.is_empty() or i.ends_with(extension): - array.append({'type':type, 'path':i}) - return Array(array, TYPE_DICTIONARY, "", null) + dict[DialogicUtil.pretty_name(i).to_lower()] = {"path":i} + return dict + + +func list_animations(subdir := "") -> Dictionary: + var full_animation_list := {} + for path in list_dir(subdir): + if not path.ends_with(".gd") and not path.ends_with(".gdc"): + continue + var anim_object: DialogicAnimation = load(path).new() + var versions := anim_object._get_named_variations() + for version_name in versions: + full_animation_list[version_name] = versions[version_name] + full_animation_list[version_name]["path"] = path + anim_object.queue_free() + return full_animation_list #endregion @@ -99,7 +118,7 @@ func _get_layout_parts() -> Array[Dictionary]: ## Helper that allows scanning sub directories that might be layout parts or styles func scan_for_layout_parts() -> Array[Dictionary]: var dir := DirAccess.open(this_folder) - var style_list :Array[Dictionary] = [] + var style_list: Array[Dictionary] = [] if !dir: return style_list dir.list_dir_begin() diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd index 3d5b226e7..9ad94cbbb 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.gd @@ -5,80 +5,82 @@ extends DialogicCharacterEditorPortraitSection ## for custom portrait scenes var current_portrait_data := {} - +var last_scene := "" func _get_title() -> String: return "Settings" -func _init(): - hint_text = "The settings here are @export variables from the used scene." - - func _load_portrait_data(data:Dictionary) -> void: - _recheck(data) + _recheck(data, true) ## Recheck section visibility and reload export fields. ## This allows reacting to changes of the portrait_scene setting. -func _recheck(data:Dictionary): - if data.get('scene', '').is_empty() and ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').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() - +func _recheck(data: Dictionary, force:=false): + if last_scene == data.get("scene", "") and not force: current_portrait_data = data - load_portrait_scene_export_variables() + last_scene = data.get("scene", "") + return + last_scene = data.get("scene", "") + current_portrait_data = data -func load_portrait_scene_export_variables(): - var scene = null - if !current_portrait_data.get('scene', '').is_empty(): - scene = load(current_portrait_data.get('scene')) - elif !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty(): - scene = load(ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')) + for child in $Grid.get_children(): + child.get_parent().remove_child(child) + child.queue_free() + + var scene: Variant = null + + if current_portrait_data.get('scene', '').is_empty(): + if ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty(): + scene = load(character_editor.def_portrait_path) + else: + scene = load(ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')) else: - scene = load(character_editor.def_portrait_path) + scene = load(current_portrait_data.get('scene')) - if !scene: + if not scene: return - for child in $Grid.get_children(): - child.queue_free() - scene = scene.instantiate() + var skip := false for i in scene.script.get_script_property_list(): if i['usage'] & PROPERTY_USAGE_EDITOR and !skip: - var label = Label.new() + var label := Label.new() label.text = i['name'].capitalize() $Grid.add_child(label) - var current_value :Variant = scene.get(i['name']) + 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']]) if current_value == null and typeof(scene.get(i['name'])) == TYPE_STRING: current_value = current_portrait_data['export_overrides'][i['name']] - var input :Node = DialogicUtil.setup_script_property_edit_node(i, current_value, set_export_override) + var input: Node = DialogicUtil.setup_script_property_edit_node(i, current_value, set_export_override) input.size_flags_horizontal = SIZE_EXPAND_FILL $Grid.add_child(input) if i['usage'] & PROPERTY_USAGE_GROUP: - if i['name'] == 'Main': + if i['name'] == 'Main' or i["name"] == "Private": skip = true continue else: skip = false - $Label.visible = $Grid.get_child_count() == 0 + if $Grid.get_child_count(): + get_parent().get_child(get_index()-1).show() + show() + else: + hide() + get_parent().get_child(get_index()-1).hide() + get_parent().get_child(get_index()+1).hide() ## On any change, save the export override to the portrait items metadata. func set_export_override(property_name:String, value:String = "") -> void: - var data:Dictionary = selected_item.get_metadata(0) + var data: Dictionary = selected_item.get_metadata(0) if !data.has('export_overrides'): data['export_overrides'] = {} if !value.is_empty(): diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn index 41dc75c2b..1392a1817 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn @@ -8,13 +8,6 @@ offset_right = 367.0 offset_bottom = 82.0 script = ExtResource("1_isys8") -[node name="Label" type="Label" parent="."] -layout_mode = 2 -theme_type_variation = &"DialogicHintText" -theme_override_colors/font_color = Color(0, 0, 0, 1) -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 diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd index 6dec0bf47..03f4346e9 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd @@ -17,28 +17,28 @@ func _load_portrait_data(data:Dictionary) -> void: func _on_portrait_scale_value_changed(value:float) -> void: - var data:Dictionary = selected_item.get_metadata(0) + 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) + 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) + 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) + 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_main.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd index 9a1c1867b..80a96c8e1 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd @@ -6,10 +6,14 @@ extends DialogicCharacterEditorPortraitSection func _get_title() -> String: return "Scene" -func _init(): +func _init() -> void: hint_text = "You can use a custom scene for this portrait." +func _start_opened() -> bool: + return true + func _ready() -> void: + %ChangeSceneButton.icon = get_theme_icon("Loop", "EditorIcons") %ScenePicker.file_filter = "*.tscn, *.scn; Scenes" %ScenePicker.resource_icon = get_theme_icon('PackedScene', 'EditorIcons') %ScenePicker.placeholder = 'Default scene' @@ -18,19 +22,80 @@ func _ready() -> void: func _load_portrait_data(data:Dictionary) -> void: - %ScenePicker.set_value(data.get('scene', '')) - %OpenSceneButton.visible = !data.get('scene', '').is_empty() + reload_ui(data) + + +func _on_open_scene_button_pressed() -> void: + var data: Dictionary = selected_item.get_metadata(0) + if ResourceLoader.exists(data.get("scene", "")): + DialogicUtil.get_dialogic_plugin().get_editor_interface().open_scene_from_path(data.get("scene", "")) + await get_tree().process_frame + EditorInterface.set_main_screen_editor("2D") + + +func _on_change_scene_button_pressed() -> void: + %PortraitSceneBrowserWindow.popup_centered_ratio(0.6) + + +func _on_portrait_scene_browser_activate_part(part_info: Dictionary) -> void: + %PortraitSceneBrowserWindow.hide() + match part_info.type: + "General": + set_scene_path(part_info.path) + "Preset": + find_parent("EditorView").godot_file_dialog( + create_new_portrait_scene.bind(part_info), + '*.tscn,*.scn', + EditorFileDialog.FILE_MODE_SAVE_FILE, + "Select where to save the new scene", + part_info.path.get_file().trim_suffix("."+part_info.path.get_extension())+"_"+character_editor.current_resource.get_character_name().to_lower()) + "Custom": + find_parent("EditorView").godot_file_dialog( + set_scene_path, + '*.tscn, *.scn', + EditorFileDialog.FILE_MODE_OPEN_FILE, + "Select custom portrait scene",) + "Default": + set_scene_path("") -func _on_scene_picker_value_changed(prop_name:String, value:String) -> void: - var data:Dictionary = selected_item.get_metadata(0) - data['scene'] = value +func create_new_portrait_scene(target_file: String, info: Dictionary) -> void: + var path := make_portrait_preset_custom(target_file, info) + set_scene_path(path) + + +func make_portrait_preset_custom(target_file:String, info: Dictionary) -> String: + var previous_file: String = info.path + + var result_path := DialogicUtil.make_file_custom(previous_file, target_file.get_base_dir(), target_file.get_file()) + + return result_path + + +func set_scene_path(path:String) -> void: + var data: Dictionary = selected_item.get_metadata(0) + data['scene'] = path update_preview.emit() changed.emit() - %OpenSceneButton.visible = !data.get('scene', '').is_empty() + reload_ui(data) -func _on_open_scene_button_pressed(): - if !%ScenePicker.current_value.is_empty() and FileAccess.file_exists(%ScenePicker.current_value): - DialogicUtil.get_dialogic_plugin().get_editor_interface().open_scene_from_path(%ScenePicker.current_value) - EditorInterface.set_main_screen_editor("2D") +func reload_ui(data: Dictionary) -> void: + var path: String = data.get('scene', '') + %OpenSceneButton.hide() + + if path.is_empty(): + %SceneLabel.text = "Default Portrait Scene" + %SceneLabel.tooltip_text = "Can be changed in the settings." + %SceneLabel.add_theme_color_override("font_color", get_theme_color("readonly_color", "Editor")) + + elif %PortraitSceneBrowser.is_premade_portrait_scene(path): + %SceneLabel.text = %PortraitSceneBrowser.portrait_scenes_info[path].name + %SceneLabel.tooltip_text = path + %SceneLabel.add_theme_color_override("font_color", get_theme_color("accent_color", "Editor")) + + else: + %SceneLabel.text = path.get_file() + %SceneLabel.tooltip_text = path + %SceneLabel.add_theme_color_override("font_color", get_theme_color("property_color_x", "Editor")) + %OpenSceneButton.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 index db355bd9f..41a38dd9f 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://djq4aasoihexj"] +[gd_scene load_steps=6 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/field_file.tscn" id="2_k8xs0"] +[ext_resource type="PackedScene" uid="uid://b1wn8r84uh11b" path="res://addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.tscn" id="3_ngvgq"] -[sub_resource type="Image" id="Image_sbh6e"] +[sub_resource type="Image" id="Image_tg5pd"] 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", @@ -12,8 +13,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_mbv6v"] -image = SubResource("Image_sbh6e") +[sub_resource type="ImageTexture" id="ImageTexture_f5xt2"] +image = SubResource("Image_tg5pd") [node name="Scene" type="GridContainer"] offset_right = 298.0 @@ -25,18 +26,47 @@ script = ExtResource("1_ht8lu") layout_mode = 2 size_flags_horizontal = 3 +[node name="ChangeSceneButton" type="Button" parent="HBox"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Change Scene" +icon = SubResource("ImageTexture_f5xt2") + +[node name="SceneLabel" type="Label" parent="HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 0 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "asdsdasdasd" +clip_text = true + [node name="ScenePicker" parent="HBox" instance=ExtResource("2_k8xs0")] unique_name_in_owner = true +visible = false layout_mode = 2 size_flags_horizontal = 3 file_filter = "*.tscn, *.scn; Scenes" placeholder = "Default scene" -resource_icon = SubResource("ImageTexture_mbv6v") [node name="OpenSceneButton" type="Button" parent="HBox"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_mbv6v") +tooltip_text = "Open/Edit Scene" +icon = SubResource("ImageTexture_f5xt2") + +[node name="PortraitSceneBrowserWindow" type="Window" parent="."] +unique_name_in_owner = true +title = "Portrait Scene Browser" +position = Vector2i(0, 36) +visible = false +wrap_controls = true +transient = true +popup_window = true + +[node name="PortraitSceneBrowser" parent="PortraitSceneBrowserWindow" instance=ExtResource("3_ngvgq")] +unique_name_in_owner = true -[connection signal="value_changed" from="HBox/ScenePicker" to="." method="_on_scene_picker_value_changed"] +[connection signal="pressed" from="HBox/ChangeSceneButton" to="." method="_on_change_scene_button_pressed"] [connection signal="pressed" from="HBox/OpenSceneButton" to="." method="_on_open_scene_button_pressed"] +[connection signal="activate_part" from="PortraitSceneBrowserWindow/PortraitSceneBrowser" to="." method="_on_portrait_scene_browser_activate_part"] diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main_exports.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main_exports.gd index 30da4d370..d9ff6e5a9 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main_exports.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main_exports.gd @@ -3,46 +3,61 @@ extends DialogicCharacterEditorPortraitSection ## Portrait Settings Section that only shows the MAIN settings of a portrait scene. +var current_portrait_data := {} +var last_scene := "" + func _show_title() -> bool: return false -var current_portrait_data := {} func _load_portrait_data(data:Dictionary) -> void: + _recheck(data, true) + + +func _recheck(data:Dictionary, force := false) -> void: get_parent().get_child(get_index()+1).hide() + if last_scene == data.get("scene", "") and not force: + current_portrait_data = data + last_scene = data.get("scene", "") + return + + last_scene = data.get("scene", "") current_portrait_data = data + load_portrait_scene_export_variables() -func load_portrait_scene_export_variables(): + +func load_portrait_scene_export_variables() -> void: for child in $Grid.get_children(): child.queue_free() - var scene = null - if !current_portrait_data.get('scene', '').is_empty(): - scene = load(current_portrait_data.get('scene')) - elif !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty(): - scene = load(ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')) + var scene: Variant = null + if current_portrait_data.get('scene', '').is_empty(): + if ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty(): + scene = load(character_editor.def_portrait_path) + else: + scene = load(ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')) else: - scene = load(character_editor.def_portrait_path) + scene = load(current_portrait_data.get('scene')) - if !scene: + if not scene: return scene = scene.instantiate() var skip := true for i in scene.script.get_script_property_list(): if i['usage'] & PROPERTY_USAGE_EDITOR and !skip: - var label = Label.new() + var label := Label.new() label.text = i['name'].capitalize() $Grid.add_child(label) - var current_value :Variant = scene.get(i['name']) + 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']]) if current_value == null and typeof(scene.get(i['name'])) == TYPE_STRING: current_value = current_portrait_data['export_overrides'][i['name']] - var input :Node = DialogicUtil.setup_script_property_edit_node(i, current_value, set_export_override) + var input: Node = DialogicUtil.setup_script_property_edit_node(i, current_value, set_export_override) input.size_flags_horizontal = SIZE_EXPAND_FILL $Grid.add_child(input) @@ -54,7 +69,7 @@ func load_portrait_scene_export_variables(): continue func set_export_override(property_name:String, value:String = "") -> void: - var data:Dictionary = selected_item.get_metadata(0) + var data: Dictionary = selected_item.get_metadata(0) if !data.has('export_overrides'): data['export_overrides'] = {} if !value.is_empty(): diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd index 6fcd21dff..a5fe5140e 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd @@ -14,7 +14,7 @@ func _start_opened() -> bool: func _ready() -> void: # Connecting all necessary signals - %ColorPickerButton.custom_minimum_size.x = DialogicUtil.get_editor_scale()*30 + %ColorPickerButton.custom_minimum_size.x = DialogicUtil.get_editor_scale() * 30 %ColorPickerButton.color_changed.connect(character_editor.something_changed) %DisplayNameLineEdit.text_changed.connect(character_editor.something_changed) %NicknameLineEdit.text_changed.connect(character_editor.something_changed) diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor.gd b/addons/dialogic/Editor/CharacterEditor/character_editor.gd index 9d6f4e10e..cc635cf52 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor.gd +++ b/addons/dialogic/Editor/CharacterEditor/character_editor.gd @@ -9,11 +9,12 @@ signal portrait_selected() # Current state var loading := false -var current_previewed_scene = null +var current_previewed_scene: Variant = null +var current_scene_path: String = "" # References var selected_item: TreeItem -var def_portrait_path :String= DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn') +var def_portrait_path: String = DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn') ######### EDITOR STUFF and LOADING/SAVING ###################################### @@ -26,7 +27,7 @@ func _register() -> void: editors_manager.register_resource_editor("dch", self) ## Add an "add character" button - var add_character_button = editors_manager.add_icon_button( + var add_character_button: Button = editors_manager.add_icon_button( load("res://addons/dialogic/Editor/Images/Toolbar/add-character.svg"), 'Add Character', self) @@ -189,7 +190,7 @@ func add_settings_section(edit:Control, parent:Node) -> void: if edit.has_signal('update_preview'): edit.update_preview.connect(update_preview) - var button :Button + var button: Button if edit._show_title(): var hbox := HBoxContainer.new() hbox.name = edit._get_title()+"BOX" @@ -209,7 +210,7 @@ func add_settings_section(edit:Control, parent:Node) -> void: hbox.add_child(button) if !edit.hint_text.is_empty(): - var hint :Node = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate() + var hint: Node = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate() hint.hint_text = edit.hint_text hbox.add_child(hint) @@ -313,16 +314,28 @@ func import_portraits_from_folder(path:String) -> void: var dir := DirAccess.open(path) dir.list_dir_begin() - var file_name :String = dir.get_next() + var file_name: String = dir.get_next() + var files := [] while file_name != "": if not dir.current_is_dir(): - var file_lower = file_name.to_lower() + 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.path_join(file_name) - %PortraitTree.add_portrait_item(file_name.trim_suffix('.'+file_name.get_extension()), - {'scene':"",'export_overrides':{'image':var_to_str(final_name)}, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent) + files.append(file_name) file_name = dir.get_next() + + var prefix: String = files[0] + for file in files: + while true: + if file.begins_with(prefix): + break + if prefix.is_empty(): + break + prefix = prefix.substr(0, len(prefix)-1) + + for file in files: + %PortraitTree.add_portrait_item(file.trim_prefix(prefix).trim_suffix('.'+file.get_extension()), + {'scene':"",'export_overrides':{'image':var_to_str(path.path_join(file))}, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent) ## Handle selection if parent.get_child_count(): @@ -341,7 +354,7 @@ func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary= parent = %PortraitTree.get_selected() else: parent = %PortraitTree.get_selected().get_parent() - var item :TreeItem = %PortraitTree.add_portrait_item(portrait_name, portrait_data, 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) @@ -350,10 +363,10 @@ func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary= func add_portrait_group() -> void: - var parent_item :TreeItem = %PortraitTree.get_root() + 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) + var item: TreeItem = %PortraitTree.add_portrait_group("Group", parent_item) item.set_meta('new', true) item.set_editable(0, true) item.select(0) @@ -362,11 +375,11 @@ func add_portrait_group() -> void: func load_portrait_tree() -> void: %PortraitTree.clear_tree() - var root:TreeItem = %PortraitTree.create_item() + var root: TreeItem = %PortraitTree.create_item() for portrait in current_resource.portraits.keys(): - var portrait_label = portrait - var parent = %PortraitTree.get_root() + var portrait_label: String = portrait + var parent: TreeItem = %PortraitTree.get_root() if '/' in portrait: parent = %PortraitTree.create_necessary_group_items(portrait) portrait_label = portrait.split('/')[-1] @@ -384,11 +397,11 @@ func load_portrait_tree() -> void: load_selected_portrait() -func filter_portrait_list(filter_term:String = '') -> void: +func filter_portrait_list(filter_term := "") -> void: filter_branch(%PortraitTree.get_root(), filter_term) -func filter_branch(parent:TreeItem, filter_term:String) -> bool: +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'): @@ -402,12 +415,12 @@ func filter_branch(parent:TreeItem, filter_term:String) -> bool: return anything_visible -# this is used to save the portrait data +## 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: +func list_portraits(tree_items: Array[TreeItem], dict := {}, 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)+"/") @@ -416,7 +429,7 @@ func list_portraits(tree_items:Array[TreeItem], dict:Dictionary = {}, path_prefi return dict -func load_selected_portrait(): +func load_selected_portrait() -> void: if selected_item and is_instance_valid(selected_item): selected_item.set_editable(0, false) @@ -439,7 +452,7 @@ func load_selected_portrait(): update_preview() -func delete_portrait_item(item:TreeItem) -> void: +func delete_portrait_item(item: TreeItem) -> void: if item.get_next_visible(true) and item.get_next_visible(true) != item: item.get_next_visible(true).select(0) else: @@ -449,11 +462,13 @@ func delete_portrait_item(item:TreeItem) -> void: 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 duplicate_item(item: TreeItem) -> void: + var new_item: TreeItem = %PortraitTree.add_portrait_item(item.get_text(0)+'_duplicated', item.get_metadata(0).duplicate(true), item.get_parent()) + new_item.set_meta('new', true) + new_item.select(0) -func _input(event:InputEvent) -> void: +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: @@ -466,7 +481,7 @@ func _input(event:InputEvent) -> void: get_viewport().set_input_as_handled() -func _on_portrait_right_click_menu_index_pressed(id:int) -> void: +func _on_portrait_right_click_menu_index_pressed(id: int) -> void: # RENAME BUTTON if id == 0: _on_item_activated() @@ -480,22 +495,22 @@ func _on_portrait_right_click_menu_index_pressed(id:int) -> void: get_settings_section_by_name("Portraits").set_default_portrait(%PortraitTree.get_full_item_name(%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() +## 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() + var item: TreeItem = 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.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(): +func _on_item_edited() -> void: selected_item = %PortraitTree.get_selected() something_changed() if selected_item: @@ -509,14 +524,14 @@ func _on_item_edited(): update_preview() -func _on_item_activated(): +func _on_item_activated() -> void: if %PortraitTree.get_selected() == null: return %PortraitTree.get_selected().set_editable(0, true) %PortraitTree.edit_selected() -func report_name_change(item:TreeItem) -> void: +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'): @@ -536,36 +551,41 @@ func report_name_change(item:TreeItem) -> void: ########### PREVIEW ############################################################ #region Preview -func update_preview(force:=false) -> void: +func update_preview(force := false, ignore_settings_reload := false) -> 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) if not force and current_previewed_scene != null \ - and current_previewed_scene.get_meta('path', '') == current_portrait_data.get('scene') \ + and scene_file_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 + # We keep the same scene. + pass else: + for node in %RealPreviewPivot.get_children(): node.queue_free() current_previewed_scene = null + current_scene_path = "" var scene_path := def_portrait_path if not current_portrait_data.get('scene', '').is_empty(): scene_path = current_portrait_data.get('scene') - if FileAccess.file_exists(scene_path): + if ResourceLoader.exists(scene_path): current_previewed_scene = load(scene_path).instantiate() + current_scene_path = scene_path - if current_previewed_scene: + if not current_previewed_scene == null: %RealPreviewPivot.add_child(current_previewed_scene) - if current_previewed_scene != null: + if not current_previewed_scene == null: var scene: Node = current_previewed_scene scene.show_behind_parent = true @@ -573,16 +593,20 @@ func update_preview(force:=false) -> void: 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 is_instance_valid(scene.get_script()) and scene.script.is_tool(): + if scene.has_method('_update_portrait'): ## Create a fake duplicate resource that has all the portrait changes applied already var preview_character := current_resource.duplicate() preview_character.portraits = get_updated_portrait_dict() scene._update_portrait(preview_character, %PortraitTree.get_full_item_name(selected_item)) + if scene.has_method('_set_mirror'): scene._set_mirror(mirror) @@ -590,25 +614,33 @@ func update_preview(force:=false) -> void: 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() + + if not scene.get_script() == null 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) + + if not ignore_settings_reload: + 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 + current_scene_path = "" func _on_some_resource_saved(file:Variant) -> void: @@ -622,12 +654,12 @@ func _on_some_resource_saved(file:Variant) -> void: update_preview(true) -func _on_full_preview_available_rect_resized(): +func _on_full_preview_available_rect_resized() -> void: if %FitPreview_Toggle.button_pressed: - update_preview() + update_preview(false, true) -func _on_create_character_button_pressed(): +func _on_create_character_button_pressed() -> void: editors_manager.show_add_resource_dialog( new_character, '*.dch; DialogicCharacter', @@ -645,11 +677,11 @@ func _on_fit_preview_toggle_toggled(button_pressed): %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() + update_preview(false, true) #endregion ## Open the reference manager -func _on_reference_manger_button_pressed(): +func _on_reference_manger_button_pressed() -> void: editors_manager.reference_manager.open() - + %PortraitChangeInfo.hide() diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor.tscn b/addons/dialogic/Editor/CharacterEditor/character_editor.tscn index f77d11233..cef90fd17 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor.tscn +++ b/addons/dialogic/Editor/CharacterEditor/character_editor.tscn @@ -5,7 +5,7 @@ [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_yiygw"] +[sub_resource type="Image" id="Image_s4mcg"] 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", @@ -14,10 +14,10 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_hx3oq"] -image = SubResource("Image_yiygw") +[sub_resource type="ImageTexture" id="ImageTexture_oab13"] +image = SubResource("Image_s4mcg") -[sub_resource type="Image" id="Image_1n61j"] +[sub_resource type="Image" id="Image_fnxud"] 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", @@ -27,7 +27,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_u1a6g"] -image = SubResource("Image_1n61j") +image = SubResource("Image_fnxud") [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_es2rd"] @@ -43,38 +43,42 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("2") -[node name="VBoxContainer" type="VBoxContainer" parent="."] +[node name="Scroll" type="ScrollContainer" parent="."] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 + +[node name="VBox" type="VBoxContainer" parent="Scroll"] +layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 3 size_flags_stretch_ratio = 0.3 theme_override_constants/separation = 0 -[node name="TopSection" type="HBoxContainer" parent="VBoxContainer"] +[node name="TopSection" type="HBoxContainer" parent="Scroll/VBox"] layout_mode = 2 -[node name="NameContainer" type="HBoxContainer" parent="VBoxContainer/TopSection"] +[node name="NameContainer" type="HBoxContainer" parent="Scroll/VBox/TopSection"] layout_mode = 2 -[node name="CharacterName" type="Label" parent="VBoxContainer/TopSection/NameContainer"] +[node name="CharacterName" type="Label" parent="Scroll/VBox/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")] +[node name="NameTooltip" parent="Scroll/VBox/TopSection/NameContainer" instance=ExtResource("2_uhhqs")] layout_mode = 2 tooltip_text = "This unique identifier is based on the file name. You can change it in the Reference Manager. Use this name in timelines to reference this character." -texture = SubResource("ImageTexture_hx3oq") +texture = SubResource("ImageTexture_oab13") hint_text = "This unique identifier is based on the file name. You can change it in the Reference Manager. Use this name in timelines to reference this character." -[node name="MainSettingsCollapse" type="Button" parent="VBoxContainer/TopSection"] +[node name="MainSettingsCollapse" type="Button" parent="Scroll/VBox/TopSection"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 10 @@ -83,92 +87,91 @@ toggle_mode = true text = "Main Settings" icon = SubResource("ImageTexture_u1a6g") -[node name="MainHSplit" type="HSplitContainer" parent="VBoxContainer"] +[node name="MainHSplit" type="HSplitContainer" parent="Scroll/VBox"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 -[node name="MainSettings" type="VBoxContainer" parent="VBoxContainer/MainHSplit"] +[node name="MainSettings" type="VBoxContainer" parent="Scroll/VBox/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"] +[node name="MainSettingsTitle" type="Label" parent="Scroll/VBox/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"] +[node name="MainSettingsScroll" type="ScrollContainer" parent="Scroll/VBox/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"] +[node name="MainSettingsSections" type="VBoxContainer" parent="Scroll/VBox/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"] +[node name="Split" type="HSplitContainer" parent="Scroll/VBox/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"] +[node name="HBoxContainer" type="HBoxContainer" parent="Scroll/VBox/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"] +[node name="MarginContainer" type="MarginContainer" parent="Scroll/VBox/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"] +[node name="PortraitListSection" type="PanelContainer" parent="Scroll/VBox/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"] +[node name="Portraits" type="VBoxContainer" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection"] layout_mode = 2 -[node name="PortraitsTitle" type="Label" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +[node name="PortraitsTitle" type="Label" parent="Scroll/VBox/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"] +[node name="PortraitListTools" type="HBoxContainer" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] layout_mode = 2 -[node name="AddPortraitButton" type="Button" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +[node name="AddPortraitButton" type="Button" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Add portrait" icon = SubResource("ImageTexture_u1a6g") -[node name="AddPortraitGroupButton" type="Button" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +[node name="AddPortraitGroupButton" type="Button" parent="Scroll/VBox/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"] +[node name="ImportPortraitsButton" type="Button" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Import images from folder" icon = SubResource("ImageTexture_u1a6g") -[node name="PortraitSearch" type="LineEdit" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] +[node name="PortraitSearch" type="LineEdit" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitListTools"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -180,12 +183,12 @@ right_icon = SubResource("ImageTexture_u1a6g") caret_blink = true caret_blink_interval = 0.5 -[node name="PortraitTreePanel" type="PanelContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +[node name="PortraitTreePanel" type="PanelContainer" parent="Scroll/VBox/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"] +[node name="PortraitTree" type="Tree" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel"] unique_name_in_owner = true layout_mode = 2 allow_rmb_select = true @@ -193,29 +196,30 @@ 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"] +[node name="PortraitRightClickMenu" type="PopupMenu" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree"] size = Vector2i(118, 100) item_count = 5 item_0/text = "Rename" -item_0/icon = SubResource("ImageTexture_hx3oq") +item_0/icon = SubResource("ImageTexture_oab13") item_0/id = 2 item_1/text = "Duplicate" -item_1/icon = SubResource("ImageTexture_hx3oq") +item_1/icon = SubResource("ImageTexture_oab13") item_1/id = 0 item_2/text = "Delete" -item_2/icon = SubResource("ImageTexture_hx3oq") +item_2/icon = SubResource("ImageTexture_oab13") item_2/id = 1 item_3/text = "" item_3/id = 3 item_3/separator = true item_4/text = "Make Default" +item_4/icon = SubResource("ImageTexture_oab13") item_4/id = 4 -[node name="PortraitChangeInfo" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] +[node name="PortraitChangeInfo" type="HBoxContainer" parent="Scroll/VBox/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"] +[node name="PortraitChangeWarning" type="Label" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitChangeInfo"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -223,24 +227,24 @@ 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"] +[node name="ReferenceMangerButton" type="Button" parent="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitChangeInfo"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 text = "Reference Manager" -[node name="RightSection2" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split"] +[node name="RightSection2" type="VBoxContainer" parent="Scroll/VBox/MainHSplit/Split"] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 size_flags_stretch_ratio = 0.5 -[node name="Spacer" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection2"] +[node name="Spacer" type="Control" parent="Scroll/VBox/MainHSplit/Split/RightSection2"] custom_minimum_size = Vector2(0, 10) layout_mode = 2 -[node name="RightSection" type="SplitContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2"] +[node name="RightSection" type="SplitContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -248,7 +252,7 @@ size_flags_vertical = 3 size_flags_stretch_ratio = 0.5 vertical = true -[node name="PortraitPreviewSection" type="Panel" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection"] +[node name="PortraitPreviewSection" type="Panel" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection"] unique_name_in_owner = true show_behind_parent = true custom_minimum_size = Vector2(100, 0) @@ -257,7 +261,7 @@ size_flags_horizontal = 3 size_flags_vertical = 3 theme_type_variation = &"DialogicPanelB" -[node name="ClipRect" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] +[node name="ClipRect" type="Control" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] clip_contents = true layout_mode = 1 anchors_preset = 15 @@ -266,15 +270,15 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="Node2D" type="Node2D" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/ClipRect"] +[node name="Node2D" type="Node2D" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/ClipRect"] position = Vector2(13, 17) -[node name="RealPreviewPivot" type="Sprite2D" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/ClipRect/Node2D"] +[node name="RealPreviewPivot" type="Sprite2D" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/ClipRect/Node2D"] unique_name_in_owner = true position = Vector2(326.5, 267) texture = SubResource("ImageTexture_u1a6g") -[node name="ScenePreviewWarning" type="Label" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] +[node name="ScenePreviewWarning" type="Label" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] unique_name_in_owner = true visible = false layout_mode = 1 @@ -295,7 +299,7 @@ vertical_alignment = 1 autowrap_mode = 3 metadata/_edit_layout_mode = 1 -[node name="PreviewReal" type="CenterContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] +[node name="PreviewReal" type="CenterContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] unique_name_in_owner = true layout_mode = 1 anchors_preset = 7 @@ -311,16 +315,16 @@ grow_vertical = 0 mouse_filter = 2 metadata/_edit_layout_mode = 1 -[node name="Control" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/PreviewReal"] +[node name="Control" type="Control" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/PreviewReal"] layout_mode = 2 -[node name="RealSizeRemotePivotTransform" type="RemoteTransform2D" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/PreviewReal/Control"] +[node name="RealSizeRemotePivotTransform" type="RemoteTransform2D" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/PreviewReal/Control"] unique_name_in_owner = true remote_path = NodePath("../../../ClipRect/Node2D/RealPreviewPivot") update_rotation = false update_scale = false -[node name="FullPreviewAvailableRect" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] +[node name="FullPreviewAvailableRect" type="Control" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] unique_name_in_owner = true layout_mode = 1 anchors_preset = 15 @@ -335,7 +339,7 @@ grow_vertical = 2 mouse_filter = 2 metadata/_edit_layout_mode = 1 -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] +[node name="HBoxContainer" type="HBoxContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection"] layout_mode = 1 anchors_preset = 10 anchor_right = 1.0 @@ -346,7 +350,7 @@ offset_bottom = 43.0 grow_horizontal = 2 mouse_filter = 2 -[node name="PreviewLabel" type="Label" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/HBoxContainer"] +[node name="PreviewLabel" type="Label" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/HBoxContainer"] unique_name_in_owner = true show_behind_parent = true layout_mode = 2 @@ -356,7 +360,7 @@ theme_override_colors/font_color = Color(0, 0, 0, 1) text = "No portrait to preview." text_overrun_behavior = 1 -[node name="FitPreview_Toggle" type="Button" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/HBoxContainer"] +[node name="FitPreview_Toggle" type="Button" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/HBoxContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 0 @@ -368,22 +372,22 @@ icon = SubResource("ImageTexture_u1a6g") flat = true metadata/_edit_layout_mode = 1 -[node name="VBox" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection"] +[node name="VBox" type="VBoxContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection"] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 size_flags_stretch_ratio = 0.75 -[node name="Hbox" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/VBox"] +[node name="Hbox" type="HBoxContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/VBox"] layout_mode = 2 -[node name="PortraitSettingsTitle" type="Label" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/VBox/Hbox"] +[node name="PortraitSettingsTitle" type="Label" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/VBox/Hbox"] unique_name_in_owner = true layout_mode = 2 theme_type_variation = &"DialogicSubTitle" text = "Portrait Settings" -[node name="SwitchPortraitSettingsPosition" type="Button" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/VBox/Hbox"] +[node name="SwitchPortraitSettingsPosition" type="Button" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/VBox/Hbox"] unique_name_in_owner = true modulate = Color(1, 1, 1, 0.647059) layout_mode = 2 @@ -392,19 +396,19 @@ focus_mode = 0 icon = SubResource("ImageTexture_u1a6g") flat = true -[node name="Scroll" type="ScrollContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/VBox"] +[node name="Scroll" type="ScrollContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/VBox"] layout_mode = 2 size_flags_vertical = 3 size_flags_stretch_ratio = 0.4 -[node name="PortraitSettingsSection" type="VBoxContainer" parent="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/VBox/Scroll"] +[node name="PortraitSettingsSection" type="VBoxContainer" parent="Scroll/VBox/MainHSplit/Split/RightSection2/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="Spacer2" type="Control" parent="VBoxContainer/MainHSplit/Split/RightSection2"] +[node name="Spacer2" type="Control" parent="Scroll/VBox/MainHSplit/Split/RightSection2"] custom_minimum_size = Vector2(0, 20) layout_mode = 2 @@ -442,11 +446,11 @@ autowrap_mode = 3 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/RightSection2/RightSection/PortraitPreviewSection/FullPreviewAvailableRect" to="." method="_on_full_preview_available_rect_resized"] -[connection signal="toggled" from="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/HBoxContainer/FitPreview_Toggle" to="." method="_on_fit_preview_toggle_toggled"] -[connection signal="pressed" from="VBoxContainer/MainHSplit/Split/RightSection2/RightSection/VBox/Hbox/SwitchPortraitSettingsPosition" to="." method="_on_switch_portrait_settings_position_pressed"] +[connection signal="toggled" from="Scroll/VBox/TopSection/MainSettingsCollapse" to="." method="_on_main_settings_collapse_toggled"] +[connection signal="item_mouse_selected" from="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree" to="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree" method="_on_item_mouse_selected"] +[connection signal="index_pressed" from="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree/PortraitRightClickMenu" to="." method="_on_portrait_right_click_menu_index_pressed"] +[connection signal="pressed" from="Scroll/VBox/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitChangeInfo/ReferenceMangerButton" to="." method="_on_reference_manger_button_pressed"] +[connection signal="resized" from="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/FullPreviewAvailableRect" to="." method="_on_full_preview_available_rect_resized"] +[connection signal="toggled" from="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/PortraitPreviewSection/HBoxContainer/FitPreview_Toggle" to="." method="_on_fit_preview_toggle_toggled"] +[connection signal="pressed" from="Scroll/VBox/MainHSplit/Split/RightSection2/RightSection/VBox/Hbox/SwitchPortraitSettingsPosition" to="." method="_on_switch_portrait_settings_position_pressed"] [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 index dc787ed9f..d329d0659 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor_main_settings_section.gd +++ b/addons/dialogic/Editor/CharacterEditor/character_editor_main_settings_section.gd @@ -8,7 +8,7 @@ extends Control signal changed ## Reference to the character editor, set when instantiated -var character_editor:Control +var character_editor: Control ## If not empty, a hint icon is added to the section title. var hint_text := "" diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd index 864e3bc60..920f8a576 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd +++ b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_settings_section.gd @@ -12,10 +12,10 @@ signal changed signal update_preview ## Reference to the character editor, set when instantiated -var character_editor:Control +var character_editor: Control ## Reference to the selected portrait item. ## `selected_item.get_metadata(0)` can access the portraits data -var selected_item :TreeItem = null +var selected_item: TreeItem = null ## If not empty a hint icon is added to the section title var hint_text := "" diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd index 573358922..c6fcf4666 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd +++ b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd @@ -3,7 +3,7 @@ extends Tree ## Tree that displays the portrait list as a hirarchy -var editor = find_parent('Character Editor') +var editor := find_parent('Character Editor') var current_group_nodes := {} @@ -19,8 +19,8 @@ func clear_tree() -> void: 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) +func add_portrait_item(portrait_name: String, portrait_data: Dictionary, parent_item: TreeItem, previous_name := "") -> 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(): @@ -32,8 +32,8 @@ func add_portrait_item(portrait_name:String, portrait_data:Dictionary, parent_it 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) +func add_portrait_group(goup_name := "Group", parent_item: TreeItem = get_root(), previous_name := "") -> 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}) @@ -44,7 +44,7 @@ func add_portrait_group(goup_name:String = "Group", parent_item:TreeItem = get_r return item -func get_full_item_name(item:TreeItem) -> String: +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 @@ -52,9 +52,9 @@ func get_full_item_name(item:TreeItem) -> String: 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: +## 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 := "" @@ -64,13 +64,13 @@ func create_necessary_group_items(path:String) -> TreeItem: 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) + 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: +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())) @@ -80,7 +80,7 @@ func _on_item_mouse_selected(pos:Vector2, mouse_button_index:int) -> void: ## DRAG AND DROP ################################################################################ -func _get_drag_data(position:Vector2) -> Variant: +func _get_drag_data(position: Vector2) -> Variant: drop_mode_flags = DROP_MODE_INBETWEEN var preview := Label.new() preview.text = " "+get_selected().get_text(0) @@ -90,14 +90,14 @@ func _get_drag_data(position:Vector2) -> Variant: return get_selected() -func _can_drop_data(position:Vector2, data:Variant) -> bool: +func _can_drop_data(position: Vector2, data: Variant) -> bool: return data is TreeItem -func _drop_data(position:Vector2, item:Variant) -> void: +func _drop_data(position: Vector2, item: Variant) -> void: var to_item := get_item_at_position(position) if to_item: - var test_item:= to_item + var test_item := to_item while true: if test_item == item: return @@ -126,8 +126,8 @@ func _drop_data(position:Vector2, item:Variant) -> void: item.free() -func copy_branch_or_item(item:TreeItem, new_parent:TreeItem) -> TreeItem: - var new_item :TreeItem = null +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: @@ -136,4 +136,3 @@ func copy_branch_or_item(item:TreeItem, new_parent:TreeItem) -> TreeItem: for child in item.get_children(): copy_branch_or_item(child, new_item) return new_item - diff --git a/addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.gd b/addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.gd new file mode 100644 index 000000000..119f813d2 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.gd @@ -0,0 +1,126 @@ +@tool +extends Control + +var ListItem := load("res://addons/dialogic/Editor/Common/BrowserItem.tscn") + +enum Types {ALL, GENERAL, PRESET} +var current_type := Types.ALL +var current_info := {} + +var portrait_scenes_info := {} + +signal activate_part(part_info:Dictionary) + + +func _ready() -> void: + collect_portrait_scenes() + + %Search.right_icon = get_theme_icon("Search", "EditorIcons") + %CloseButton.icon = get_theme_icon("Close", "EditorIcons") + + get_parent().close_requested.connect(_on_close_button_pressed) + get_parent().visibility_changed.connect(func():if get_parent().visible: open()) + + +func collect_portrait_scenes() -> void: + for indexer in DialogicUtil.get_indexers(): + for element in indexer._get_portrait_scene_presets(): + portrait_scenes_info[element.get('path', '')] = element + + +func open() -> void: + collect_portrait_scenes() + load_parts() + + +func is_premade_portrait_scene(scene_path:String) -> bool: + return scene_path in portrait_scenes_info + + +func load_parts() -> void: + for i in %PartGrid.get_children(): + i.queue_free() + + %Search.placeholder_text = "Search for " + %Search.text = "" + match current_type: + Types.GENERAL: %Search.placeholder_text += "general portrait scenes" + Types.PRESET: %Search.placeholder_text += "portrait scene presets" + Types.ALL: %Search.placeholder_text += "general portrait scenes and presets" + + for info in portrait_scenes_info.values(): + var type: String = info.get('type', '_') + if (current_type == Types.GENERAL and type != "General") or (current_type == Types.PRESET and type != "Preset"): + continue + + var item: Node = ListItem.instantiate() + item.load_info(info) + %PartGrid.add_child(item) + item.set_meta('info', info) + item.clicked.connect(_on_item_clicked.bind(item, info)) + item.focused.connect(_on_item_clicked.bind(item, info)) + item.double_clicked.connect(emit_signal.bind('activate_part', info)) + + await get_tree().process_frame + + if %PartGrid.get_child_count() > 0: + %PartGrid.get_child(0).clicked.emit() + %PartGrid.get_child(0).grab_focus() + + +func _on_item_clicked(item: Node, info:Dictionary) -> void: + load_part_info(info) + + +func load_part_info(info:Dictionary) -> void: + current_info = info + %PartTitle.text = info.get('name', 'Unknown Part') + %PartAuthor.text = "by "+info.get('author', 'Anonymus') + %PartDescription.text = info.get('description', '') + + if info.get('preview_image', null) and ResourceLoader.exists(info.preview_image[0]): + %PreviewImage.texture = load(info.preview_image[0]) + %PreviewImage.show() + else: + %PreviewImage.hide() + + match info.type: + "General": + %ActivateButton.text = "Use this scene" + %TypeDescription.text = "This is a general use scene, it can be used directly." + "Preset": + %ActivateButton.text = "Customize this scene" + %TypeDescription.text = "This is a preset you can use for a custom portrait scene. Dialogic will promt you to save a copy of this scene that you can then use and customize." + "Default": + %ActivateButton.text = "Use default scene" + %TypeDescription.text = "" + "Custom": + %ActivateButton.text = "Select a custom scene" + %TypeDescription.text = "" + + if info.get("documentation", ""): + %DocumentationButton.show() + %DocumentationButton.uri = info.documentation + else: + %DocumentationButton.hide() + + +func _on_activate_button_pressed() -> void: + activate_part.emit(current_info) + + +func _on_close_button_pressed() -> void: + get_parent().hide() + + +func _on_search_text_changed(new_text: String) -> void: + for item in %PartGrid.get_children(): + if new_text.is_empty(): + item.show() + continue + + if new_text.to_lower() in item.get_meta('info').name.to_lower(): + item.show() + continue + + item.hide() diff --git a/addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.tscn b/addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.tscn new file mode 100644 index 000000000..3e4ecc186 --- /dev/null +++ b/addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.tscn @@ -0,0 +1,260 @@ +[gd_scene load_steps=11 format=3 uid="uid://b1wn8r84uh11b"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/portrait_scene_browser.gd" id="1_an6nc"] + +[sub_resource type="Gradient" id="Gradient_0o1u0"] +colors = PackedColorArray(0.100572, 0.303996, 0.476999, 1, 0.296448, 0.231485, 0.52887, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_gxpvv"] +gradient = SubResource("Gradient_0o1u0") +fill = 2 +fill_from = Vector2(0.478632, 1) +fill_to = Vector2(0, 0) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_we8bq"] +content_margin_left = 6.0 +content_margin_top = 3.0 +content_margin_right = 6.0 +content_margin_bottom = 3.0 +draw_center = false +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 1, 1, 0.615686) +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_3x0xw"] +content_margin_left = 6.0 +content_margin_top = 3.0 +content_margin_right = 6.0 +content_margin_bottom = 3.0 +draw_center = false +border_width_left = 3 +border_width_top = 3 +border_width_right = 3 +border_width_bottom = 3 +border_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 = 2.0 +expand_margin_top = 2.0 +expand_margin_right = 2.0 +expand_margin_bottom = 2.0 + +[sub_resource type="Image" id="Image_lwe0k"] +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_d2gam"] +image = SubResource("Image_lwe0k") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lf1ht"] +bg_color = Color(0.0588235, 0.0313726, 0.0980392, 1) +border_width_left = 5 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a5iyu"] +bg_color = Color(1, 1, 1, 1) +draw_center = false +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 +shadow_color = Color(0.992157, 0.992157, 0.992157, 0.101961) +shadow_size = 10 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_htwsp"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 + +[node name="PortraitSceneBrowser" 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_an6nc") + +[node name="BGColor" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = SubResource("GradientTexture2D_gxpvv") + +[node name="HSplitContainer" type="HSplitContainer" parent="."] +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 + +[node name="Margin" type="MarginContainer" parent="HSplitContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 1.5 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBox" type="VBoxContainer" parent="HSplitContainer/Margin"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="BrowserTitle" type="Label" parent="HSplitContainer/Margin/VBox"] +layout_mode = 2 +theme_type_variation = &"DialogicSubTitle" +theme_override_font_sizes/font_size = 25 +text = "Dialogic Portrait Scene Browser" + +[node name="HBox" type="HBoxContainer" parent="HSplitContainer/Margin/VBox"] +layout_mode = 2 + +[node name="Search" type="LineEdit" parent="HSplitContainer/Margin/VBox/HBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_styles/normal = SubResource("StyleBoxFlat_we8bq") +theme_override_styles/focus = SubResource("StyleBoxFlat_3x0xw") +placeholder_text = "Search" +right_icon = SubResource("ImageTexture_d2gam") + +[node name="ScrollContainer" type="ScrollContainer" parent="HSplitContainer/Margin/VBox"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="PartGrid" type="HFlowContainer" parent="HSplitContainer/Margin/VBox/ScrollContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Buttons" type="HBoxContainer" parent="HSplitContainer/Margin/VBox"] +layout_mode = 2 +alignment = 1 + +[node name="CloseButton" type="Button" parent="HSplitContainer/Margin/VBox/Buttons"] +unique_name_in_owner = true +layout_mode = 2 +text = "Close" +icon = SubResource("ImageTexture_d2gam") + +[node name="PanelContainer" type="PanelContainer" parent="HSplitContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_lf1ht") + +[node name="Control" type="Control" parent="HSplitContainer/PanelContainer"] +layout_mode = 2 + +[node name="Panel" type="Panel" parent="HSplitContainer/PanelContainer/Control"] +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_left = -4.0 +offset_right = 40.0 +offset_bottom = 71.0 +grow_vertical = 2 +rotation = 0.0349066 +theme_override_styles/panel = SubResource("StyleBoxFlat_lf1ht") + +[node name="MarginContainer" type="MarginContainer" parent="HSplitContainer/PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBox" type="VBoxContainer" parent="HSplitContainer/PanelContainer/MarginContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Panel" type="PanelContainer" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_a5iyu") + +[node name="Panel" type="PanelContainer" parent="HSplitContainer/PanelContainer/MarginContainer/VBox/Panel"] +clip_children = 1 +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_htwsp") + +[node name="PreviewImage" type="TextureRect" parent="HSplitContainer/PanelContainer/MarginContainer/VBox/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 2 +expand_mode = 5 +stretch_mode = 6 + +[node name="HFlowContainer" type="HFlowContainer" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +layout_mode = 2 + +[node name="PartTitle" type="Label" parent="HSplitContainer/PanelContainer/MarginContainer/VBox/HFlowContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 8 +theme_type_variation = &"DialogicTitle" +text = "Cool Style Part" + +[node name="PartAuthor" type="Label" parent="HSplitContainer/PanelContainer/MarginContainer/VBox/HFlowContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 8 +theme_type_variation = &"DialogicHintText" +text = "by Jowan" + +[node name="PartType" type="Label" parent="HSplitContainer/PanelContainer/MarginContainer/VBox/HFlowContainer"] +visible = false +layout_mode = 2 +size_flags_vertical = 8 +theme_type_variation = &"DialogicHintText" +text = "a style" + +[node name="PartDescription" type="Label" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicHintText2" +text = "A cool textbox layer" +autowrap_mode = 3 + +[node name="DocumentationButton" type="LinkButton" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +text = "Learn more" + +[node name="HSeparator" type="HSeparator" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +layout_mode = 2 + +[node name="ActivateButton" type="Button" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +text = "Use" + +[node name="TypeDescription" type="Label" parent="HSplitContainer/PanelContainer/MarginContainer/VBox"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DialogicHintText" +text = "A cool textbox layer" +autowrap_mode = 3 + +[connection signal="text_changed" from="HSplitContainer/Margin/VBox/HBox/Search" to="." method="_on_search_text_changed"] +[connection signal="pressed" from="HSplitContainer/Margin/VBox/Buttons/CloseButton" to="." method="_on_close_button_pressed"] +[connection signal="pressed" from="HSplitContainer/PanelContainer/MarginContainer/VBox/ActivateButton" to="." method="_on_activate_button_pressed"] diff --git a/addons/dialogic/Modules/StyleEditor/Components/StyleItem.gd b/addons/dialogic/Editor/Common/BrowserItem.gd similarity index 82% rename from addons/dialogic/Modules/StyleEditor/Components/StyleItem.gd rename to addons/dialogic/Editor/Common/BrowserItem.gd index 08bc8325f..7bfb3cfc4 100644 --- a/addons/dialogic/Modules/StyleEditor/Components/StyleItem.gd +++ b/addons/dialogic/Editor/Common/BrowserItem.gd @@ -6,7 +6,7 @@ signal middle_clicked signal double_clicked signal focused -var base_size = 1 +var base_size := 1 func _ready() -> void: @@ -14,7 +14,7 @@ func _ready() -> void: return %Name.add_theme_font_override("font", get_theme_font("bold", "EditorFonts")) - custom_minimum_size = base_size*Vector2(200, 150)*DialogicUtil.get_editor_scale() + custom_minimum_size = base_size * Vector2(200, 150) * DialogicUtil.get_editor_scale() %CurrentIcon.texture = get_theme_icon("Favorites", "EditorIcons") if %Image.texture == null: %Image.texture = get_theme_icon("ImportFail", "EditorIcons") @@ -23,7 +23,9 @@ func _ready() -> void: func load_info(info:Dictionary) -> void: %Name.text = info.name - if info.preview_image[0] == 'custom': + if not info.has("preview_image"): + pass + elif info.preview_image[0] == 'custom': await ready %Image.texture = get_theme_icon("CreateNewSceneFrom", "EditorIcons") %Image.stretch_mode = TextureRect.STRETCH_KEEP_CENTERED @@ -32,6 +34,9 @@ func load_info(info:Dictionary) -> void: DialogicUtil.get_dialogic_plugin().get_editor_interface().get_resource_previewer().queue_resource_preview(info.preview_image[0], self, 'set_scene_preview', null) elif ResourceLoader.exists(info.preview_image[0]): %Image.texture = load(info.preview_image[0]) + elif info.preview_image[0].is_valid_html_color(): + %Image.texture = null + %Panel.self_modulate = Color(info.preview_image[0]) if ResourceLoader.exists(info.get('icon', '')): %Icon.get_parent().show() @@ -54,11 +59,11 @@ func set_current(current:bool): %CurrentIcon.visible = current -func _on_mouse_entered(): +func _on_mouse_entered() -> void: %HoverBG.show() -func _on_mouse_exited(): +func _on_mouse_exited() -> void: %HoverBG.hide() @@ -72,10 +77,10 @@ func _on_gui_input(event): middle_clicked.emit() -func _on_focus_entered(): +func _on_focus_entered() -> void: $FocusFG.show() focused.emit() -func _on_focus_exited(): +func _on_focus_exited() -> void: $FocusFG.hide() diff --git a/addons/dialogic/Modules/StyleEditor/Components/StyleItem.tscn b/addons/dialogic/Editor/Common/BrowserItem.tscn similarity index 95% rename from addons/dialogic/Modules/StyleEditor/Components/StyleItem.tscn rename to addons/dialogic/Editor/Common/BrowserItem.tscn index 7478ddd50..4abc87bdd 100644 --- a/addons/dialogic/Modules/StyleEditor/Components/StyleItem.tscn +++ b/addons/dialogic/Editor/Common/BrowserItem.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://ddlxjde1cx035"] -[ext_resource type="Script" path="res://addons/dialogic/Modules/StyleEditor/Components/StyleItem.gd" id="1_is0qu"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Common/BrowserItem.gd" id="1_s3kf0"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pfw08"] bg_color = Color(1, 1, 1, 0.32549) @@ -45,7 +45,7 @@ expand_margin_top = 4.0 expand_margin_right = 4.0 expand_margin_bottom = 4.0 -[node name="Margin" type="MarginContainer"] +[node name="BrowserItem" type="MarginContainer"] custom_minimum_size = Vector2(200, 150) offset_left = 1.0 offset_top = 1.0 @@ -57,7 +57,7 @@ theme_override_constants/margin_left = 4 theme_override_constants/margin_top = 4 theme_override_constants/margin_right = 4 theme_override_constants/margin_bottom = 4 -script = ExtResource("1_is0qu") +script = ExtResource("1_s3kf0") [node name="HoverBG" type="Panel" parent="."] unique_name_in_owner = true @@ -87,7 +87,7 @@ layout_mode = 2 size_flags_vertical = 3 mouse_filter = 2 expand_mode = 1 -stretch_mode = 5 +stretch_mode = 6 [node name="CurrentIcon" type="TextureRect" parent="VBox/Panel/Image"] unique_name_in_owner = true diff --git a/addons/dialogic/Editor/Common/DCSS.gd b/addons/dialogic/Editor/Common/DCSS.gd index 93e60f3c9..2a1f95a2f 100644 --- a/addons/dialogic/Editor/Common/DCSS.gd +++ b/addons/dialogic/Editor/Common/DCSS.gd @@ -1,26 +1,26 @@ @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() +static func inline(style: Dictionary) -> StyleBoxFlat: + var scale: float = DialogicUtil.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 + 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]) + if typeof(style[property]) == TYPE_STRING and style[property] == "none": + s.set('draw_center', false) + else: + s.set('bg_color', style[property]) 'border': - var width:float = style[property] * scale + var width: float = style[property] * scale s.set('border_width_left', width) s.set('border_width_right', width) s.set('border_width_top', width) @@ -45,16 +45,3 @@ static func inline(style:Dictionary) -> StyleBoxFlat: '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 index 685bb2e72..22626199c 100644 --- a/addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd +++ b/addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd @@ -5,7 +5,7 @@ extends PanelContainer enum Modes {EDIT, ADD} var mode := Modes.EDIT -var item :TreeItem = null +var item: TreeItem = null func _ready() -> void: @@ -86,7 +86,7 @@ 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 = DialogicResourceUtil.get_character_directory() + var _character_directory := DialogicResourceUtil.get_character_directory() var icon := load("res://addons/dialogic/Editor/Images/Resources/character.svg") suggestions['(No one)'] = {'value':null, 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} @@ -99,7 +99,7 @@ func get_character_suggestions(search_text:String) -> Dictionary: return suggestions -func save(): +func save() -> void: if %Old.text.is_empty() or %New.text.is_empty(): return if %Where.selected == 1 and %Character.current_value == null: diff --git a/addons/dialogic/Editor/Common/broken_reference_manager.gd b/addons/dialogic/Editor/Common/broken_reference_manager.gd index 36627d0c2..b0d11cc3d 100644 --- a/addons/dialogic/Editor/Common/broken_reference_manager.gd +++ b/addons/dialogic/Editor/Common/broken_reference_manager.gd @@ -3,7 +3,7 @@ extends VSplitContainer ## This manager shows a list of changed references and allows searching for them and replacing them. -var reference_changes :Array[Dictionary] = []: +var reference_changes: Array[Dictionary] = []: set(changes): reference_changes = changes update_indicator() @@ -55,7 +55,7 @@ func open() -> void: %ChangeTree.set_column_custom_minimum_width(2, 50) var categories := {null:%ChangeTree.get_root()} for i in reference_changes: - var parent : TreeItem = null + var parent: TreeItem = null if !i.get('category', null) in categories: parent = %ChangeTree.create_item() parent.set_text(1, i.category) @@ -64,7 +64,7 @@ func open() -> void: else: parent = categories[i.get('category')] - var item :TreeItem = %ChangeTree.create_item(parent) + 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') @@ -98,8 +98,8 @@ func _on_change_tree_item_edited() -> void: func _on_check_button_pressed() -> void: - var to_be_checked :Array[Dictionary]= [] - var item :TreeItem = %ChangeTree.get_root() + var to_be_checked: Array[Dictionary]= [] + var item: TreeItem = %ChangeTree.get_root() while item.get_next_visible(): item = item.get_next_visible() @@ -142,7 +142,7 @@ func _process(delta: float) -> void: %Progress.value = progress_percent progress_mutex.unlock() else: - var finds := finder_thread.wait_to_finish() + var finds: Variant = finder_thread.wait_to_finish() display_search_results(finds) @@ -236,7 +236,7 @@ func search_timelines(regexes:Array[Array]) -> Array[Dictionary]: func _exit_tree() -> void: # Shutting of - if finder_thread.is_alive(): + if finder_thread and finder_thread.is_alive(): finder_thread.wait_to_finish() @@ -245,7 +245,7 @@ func get_line(string:String, at_index:int) -> String: func update_count_coloring() -> void: - var item :TreeItem = %ChangeTree.get_root() + var item: TreeItem = %ChangeTree.get_root() while item.get_next_visible(): item = item.get_next_visible() @@ -264,9 +264,9 @@ func update_count_coloring() -> void: func _on_replace_pressed() -> void: - var to_be_replaced :Array[Dictionary]= [] - var item :TreeItem = %ReferenceTree.get_root() - var affected_timelines :Array[String]= [] + 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() @@ -284,7 +284,7 @@ func _on_replace_pressed() -> void: 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 + 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) @@ -295,7 +295,7 @@ func replace(timelines:Array[String], replacement_info:Array[Dictionary]) -> voi %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_text: String = timeline_file.get_as_text() var timeline_events := timeline_text.split('\n') timeline_file.close() @@ -343,7 +343,7 @@ func update_indicator() -> void: func close() -> void: - var item :TreeItem = %ChangeTree.get_root() + var item: TreeItem = %ChangeTree.get_root() if item: while item.get_next_visible(): item = item.get_next_visible() diff --git a/addons/dialogic/Editor/Common/hint_tooltip_icon.gd b/addons/dialogic/Editor/Common/hint_tooltip_icon.gd index 940d2bd43..702fe9835 100644 --- a/addons/dialogic/Editor/Common/hint_tooltip_icon.gd +++ b/addons/dialogic/Editor/Common/hint_tooltip_icon.gd @@ -1,9 +1,12 @@ @tool extends TextureRect -@export_multiline var hint_text = "" +@export_multiline var hint_text := "" -func _ready(): +func _ready() -> void: + if owner and owner.get_parent() is SubViewport: + texture = null + return texture = get_theme_icon("NodeInfo", "EditorIcons") - modulate = get_theme_color("readonly_color", "Editor") + modulate = get_theme_color("contrast_color_1", "Editor") tooltip_text = hint_text diff --git a/addons/dialogic/Editor/Common/reference_manager.gd b/addons/dialogic/Editor/Common/reference_manager.gd index c33ca282e..915e594e6 100644 --- a/addons/dialogic/Editor/Common/reference_manager.gd +++ b/addons/dialogic/Editor/Common/reference_manager.gd @@ -7,16 +7,17 @@ func _ready() -> void: return add_theme_stylebox_override("panel", get_theme_stylebox("Background", "EditorStyles")) + $Tabs/Close.icon = get_theme_icon("Close", "EditorIcons") for tab in $Tabs/Tabs.get_children(): tab.add_theme_color_override("font_selected_color", get_theme_color("accent_color", "Editor")) - tab.add_theme_font_override("font", get_theme_font("main_button_font", "EditorFonts")) + tab.add_theme_font_override("font", get_theme_font("main", "EditorFonts")) tab.toggled.connect(tab_changed.bind(tab.get_index()+1)) func tab_changed(enabled:bool, index:int) -> void: for child in $Tabs.get_children(): - if child.get_index() == 0 or child.get_index() == index: + if child.get_index() == 0 or child.get_index() == index or child is Button: child.show() if child.get_index() == index: child.open() @@ -28,6 +29,10 @@ func tab_changed(enabled:bool, index:int) -> void: child.set_pressed_no_signal(index-1 == child.get_index()) -func open(): +func open() -> void: show() $Tabs/BrokenReferences.update_indicator() + + +func _on_close_pressed() -> void: + get_parent()._on_close_requested() diff --git a/addons/dialogic/Editor/Common/reference_manager.tscn b/addons/dialogic/Editor/Common/reference_manager.tscn index 1774073c1..0bd60734b 100644 --- a/addons/dialogic/Editor/Common/reference_manager.tscn +++ b/addons/dialogic/Editor/Common/reference_manager.tscn @@ -8,7 +8,7 @@ [sub_resource type="ButtonGroup" id="ButtonGroup_l6uiy"] -[sub_resource type="Image" id="Image_ii0d5"] +[sub_resource type="Image" id="Image_36731"] 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 224, 224, 224, 255, 224, 224, 224, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -18,9 +18,9 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_a0gfq"] -image = SubResource("Image_ii0d5") +image = SubResource("Image_36731") -[sub_resource type="Image" id="Image_k5gyt"] +[sub_resource type="Image" id="Image_0rvkq"] 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", @@ -29,10 +29,10 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_8oycd"] -image = SubResource("Image_k5gyt") +[sub_resource type="ImageTexture" id="ImageTexture_mr0fw"] +image = SubResource("Image_0rvkq") -[sub_resource type="Image" id="Image_qpnp8"] +[sub_resource type="Image" id="Image_5fmdt"] 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", @@ -42,7 +42,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_lce2m"] -image = SubResource("Image_qpnp8") +image = SubResource("Image_5fmdt") [node name="Manager" type="PanelContainer"] anchors_preset = 15 @@ -163,14 +163,14 @@ unique_name_in_owner = true layout_mode = 2 tooltip_text = "Match Case" toggle_mode = true -icon = SubResource("ImageTexture_8oycd") +icon = SubResource("ImageTexture_mr0fw") [node name="WholeWords" type="Button" parent="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel/VBox/PureTextFlags"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Whole World" toggle_mode = true -icon = SubResource("ImageTexture_8oycd") +icon = SubResource("ImageTexture_mr0fw") [node name="HBoxContainer4" type="HBoxContainer" parent="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel/VBox"] layout_mode = 2 @@ -298,12 +298,16 @@ layout_mode = 2 text = "You've renamed some identifier(s)! Use the \"Broken References\" tab to check if you have used this identifier (and fix it if so)." autowrap_mode = 3 +[node name="Close" type="Button" parent="Tabs"] +layout_mode = 2 +size_flags_horizontal = 4 +text = "Close" + [node name="HelpButton" type="LinkButton" parent="."] -custom_minimum_size = Vector2(0, 30) layout_mode = 2 size_flags_horizontal = 8 size_flags_vertical = 0 -text = "What is this about?" +text = "Documentation" uri = "https://docs.dialogic.pro/reference-manager.html" [connection signal="pressed" from="Tabs/BrokenReferences/ChangesList/VBox/HBoxContainer/AddButton" to="Tabs/BrokenReferences" method="_on_add_button_pressed"] @@ -317,3 +321,4 @@ uri = "https://docs.dialogic.pro/reference-manager.html" [connection signal="text_changed" from="Tabs/UniqueIdentifiers/VBox/Tools/Search" to="Tabs/UniqueIdentifiers" method="_on_search_text_changed"] [connection signal="button_clicked" from="Tabs/UniqueIdentifiers/VBox/IdentifierTable" to="Tabs/UniqueIdentifiers" method="_on_identifier_table_button_clicked"] [connection signal="item_edited" from="Tabs/UniqueIdentifiers/VBox/IdentifierTable" to="Tabs/UniqueIdentifiers" method="_on_identifier_table_item_edited"] +[connection signal="pressed" from="Tabs/Close" to="." method="_on_close_pressed"] diff --git a/addons/dialogic/Editor/Common/reference_manager_window.gd b/addons/dialogic/Editor/Common/reference_manager_window.gd index fae954c12..7bb42ec72 100644 --- a/addons/dialogic/Editor/Common/reference_manager_window.gd +++ b/addons/dialogic/Editor/Common/reference_manager_window.gd @@ -5,12 +5,12 @@ extends Window ## 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") +@onready var editors_manager := get_node("../EditorsManager") @onready var broken_manager := get_node("Manager/Tabs/BrokenReferences") enum Where {EVERYWHERE, BY_CHARACTER, TEXTS_ONLY} enum Types {TEXT, VARIABLE, PORTRAIT, CHARACTER_NAME, TIMELINE_NAME} -var icon_button :Button = null +var icon_button: Button = null func _ready() -> void: @@ -31,7 +31,7 @@ func _ready() -> void: icon_button.add_child(dot) - var old_changes :Array = DialogicUtil.get_editor_setting('reference_changes', []) + var old_changes: Array = DialogicUtil.get_editor_setting('reference_changes', []) if !old_changes.is_empty(): broken_manager.reference_changes = old_changes diff --git a/addons/dialogic/Editor/Common/side_bar.tscn b/addons/dialogic/Editor/Common/side_bar.tscn index a9dc0d0d0..72f82cd45 100644 --- a/addons/dialogic/Editor/Common/side_bar.tscn +++ b/addons/dialogic/Editor/Common/side_bar.tscn @@ -1,6 +1,8 @@ -[gd_scene load_steps=5 format=3 uid="uid://cwe3r2tbh2og1"] +[gd_scene load_steps=7 format=3 uid="uid://cwe3r2tbh2og1"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Common/sidebar.gd" id="1_jnq65"] +[ext_resource type="Texture2D" uid="uid://bff65e82555qr" path="res://addons/dialogic/Editor/Images/Pieces/close-icon.svg" id="2_54pks"] +[ext_resource type="Texture2D" uid="uid://dx3o2ild56i76" path="res://addons/dialogic/Editor/Images/Pieces/closed-icon.svg" id="2_ilyps"] [sub_resource type="Theme" id="Theme_pn0f4"] VBoxContainer/constants/separation = 4 @@ -20,24 +22,41 @@ theme = SubResource("Theme_pn0f4") split_offset = 100 script = ExtResource("1_jnq65") -[node name="VBox" type="VBoxContainer" parent="."] +[node name="VBoxHidden" type="VBoxContainer" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="OpenButton" type="Button" parent="VBoxHidden"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 3 +tooltip_text = "Show Sidebar" +theme_override_constants/icon_max_width = 20 +icon = ExtResource("2_ilyps") +flat = true +icon_alignment = 1 + +[node name="VBoxPrimary" type="VBoxContainer" parent="."] +unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -[node name="Margin" type="MarginContainer" parent="VBox"] +[node name="Margin" type="MarginContainer" parent="VBoxPrimary"] 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"] +[node name="VSplitContainer" type="VSplitContainer" parent="VBoxPrimary/Margin"] layout_mode = 2 -[node name="VBox" type="VBoxContainer" parent="VBox/Margin/VSplitContainer"] +[node name="VBox" type="VBoxContainer" parent="VBoxPrimary/Margin/VSplitContainer"] layout_mode = 2 size_flags_vertical = 3 -[node name="Logo" type="TextureRect" parent="VBox/Margin/VSplitContainer/VBox"] +[node name="Logo" type="TextureRect" parent="VBoxPrimary/Margin/VSplitContainer/VBox"] unique_name_in_owner = true modulate = Color(1, 1, 1, 0.623529) texture_filter = 6 @@ -46,49 +65,111 @@ layout_mode = 2 expand_mode = 3 stretch_mode = 4 -[node name="CurrentResource" type="Label" parent="VBox/Margin/VSplitContainer/VBox"] +[node name="HBox" type="HBoxContainer" parent="VBoxPrimary/Margin/VSplitContainer/VBox"] +layout_mode = 2 + +[node name="CurrentResource" type="LineEdit" parent="VBoxPrimary/Margin/VSplitContainer/VBox/HBox"] 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 +alignment = 1 +editable = false -[node name="Search" type="LineEdit" parent="VBox/Margin/VSplitContainer/VBox"] +[node name="CloseButton" type="Button" parent="VBoxPrimary/Margin/VSplitContainer/VBox/HBox"] unique_name_in_owner = true layout_mode = 2 +tooltip_text = "Hide Sidebar" +text = " " +icon = ExtResource("2_54pks") +flat = true +icon_alignment = 1 +expand_icon = true + +[node name="HBoxSearchSort" type="HBoxContainer" parent="VBoxPrimary/Margin/VSplitContainer/VBox"] +layout_mode = 2 + +[node name="Search" type="LineEdit" parent="VBoxPrimary/Margin/VSplitContainer/VBox/HBoxSearchSort"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "Filter Resources" placeholder_text = "Filter Resources" caret_blink = true caret_blink_interval = 0.5 -[node name="ResourcesList" type="ItemList" parent="VBox/Margin/VSplitContainer/VBox"] +[node name="SortOption" type="OptionButton" parent="VBoxPrimary/Margin/VSplitContainer/VBox/HBoxSearchSort"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Sort +- Default: Sort alphabetically split into Characters & Timeilnes. +- Folder: Sort alphabetically by parent directory. +- Path: Display full path relative to res://. +- None: Sort alphabetically with no categories." +text_overrun_behavior = 1 +clip_text = true +selected = 0 +item_count = 4 +popup/item_0/text = "Type" +popup/item_1/text = "Folder" +popup/item_1/id = 1 +popup/item_2/text = "Path" +popup/item_2/id = 2 +popup/item_3/text = "None" +popup/item_3/id = 3 + +[node name="ResourceTree" type="Tree" parent="VBoxPrimary/Margin/VSplitContainer/VBox"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -same_column_width = true +allow_rmb_select = true +hide_root = true +scroll_horizontal_enabled = false + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxPrimary/Margin/VSplitContainer/VBox"] +visible = false +layout_mode = 2 -[node name="ContentListSection" type="VBoxContainer" parent="VBox/Margin/VSplitContainer"] +[node name="Label" type="Label" parent="VBoxPrimary/Margin/VSplitContainer/VBox/HBoxContainer"] +layout_mode = 2 +size_flags_vertical = 1 +text = "Sort Order" +vertical_alignment = 1 + +[node name="SortOption" type="OptionButton" parent="VBoxPrimary/Margin/VSplitContainer/VBox/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +item_count = 1 +popup/item_0/text = "Alphabetical (All)" + +[node name="ContentListSection" type="VBoxContainer" parent="VBoxPrimary/Margin/VSplitContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -[node name="ContentList" type="ItemList" parent="VBox/Margin/VSplitContainer/ContentListSection"] +[node name="ContentList" type="ItemList" parent="VBoxPrimary/Margin/VSplitContainer/ContentListSection"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 +tooltip_text = "Label events in your timeline will appear here, allowing you to jump to them." 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="Button" parent="VBox"] +[node name="CurrentVersion" type="Button" parent="VBoxPrimary"] unique_name_in_owner = true layout_mode = 2 text = "Some Version" flat = true 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"] -[connection signal="pressed" from="VBox/CurrentVersion" to="." method="_on_current_version_pressed"] +[node name="RightClickMenu" type="PopupMenu" parent="."] +unique_name_in_owner = true +size = Vector2i(164, 100) + +[connection signal="gui_input" from="VBoxPrimary/Margin/VSplitContainer/VBox/Logo" to="." method="_on_logo_gui_input"] +[connection signal="text_changed" from="VBoxPrimary/Margin/VSplitContainer/VBox/HBoxSearchSort/Search" to="." method="_on_search_text_changed"] +[connection signal="text_submitted" from="VBoxPrimary/Margin/VSplitContainer/VBox/HBoxSearchSort/Search" to="." method="_on_search_text_submitted"] +[connection signal="pressed" from="VBoxPrimary/CurrentVersion" to="." method="_on_current_version_pressed"] +[connection signal="id_pressed" from="RightClickMenu" to="." method="_on_right_click_menu_id_pressed"] diff --git a/addons/dialogic/Editor/Common/sidebar.gd b/addons/dialogic/Editor/Common/sidebar.gd index 74fb1318e..339b6181d 100644 --- a/addons/dialogic/Editor/Common/sidebar.gd +++ b/addons/dialogic/Editor/Common/sidebar.gd @@ -1,5 +1,5 @@ @tool -extends Control +class_name DialogicSidebar extends Control ## Script that handles the editor sidebar. @@ -7,56 +7,136 @@ signal file_activated(file_path) signal content_item_activated(item_name) +signal show_sidebar(show: bool) + @onready var editors_manager = get_parent().get_parent() +@onready var resource_tree: Tree = %ResourceTree +var current_resource_list: Array = [] +enum SortMode { + TYPE, + FOLDER, + PATH, + NONE, +} + + +var sort_mode: SortMode = SortMode.TYPE -func _ready(): - if owner.get_parent() is SubViewport: +func _ready() -> void: + if owner != null and owner.get_parent() is SubViewport: + return + if editors_manager is SubViewportContainer: 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))) + resource_tree.item_activated.connect(_on_resources_tree_item_activated) + resource_tree.item_mouse_selected.connect(_on_resources_tree_item_clicked) + + + + %ContentList.item_selected.connect( + func(idx: int): content_item_activated.emit(%ContentList.get_item_text(idx)) + ) + + (%OpenButton as Button).pressed.connect(_show_sidebar) + (%CloseButton as Button).pressed.connect(_hide_sidebar) 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 + %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")) + %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) + %VBoxPrimary/Margin.set( + "theme_override_constants/margin_left", + get_theme_constant("base_margin", "Editor") * editor_scale + ) + %VBoxPrimary/Margin.set( + "theme_override_constants/margin_bottom", + get_theme_constant("base_margin", "Editor") * editor_scale + ) + + ## RIGHT CLICK MENU + %RightClickMenu.clear() + %RightClickMenu.add_icon_item(get_theme_icon("Remove", "EditorIcons"), "Remove From List", 1) + %RightClickMenu.add_separator() + %RightClickMenu.add_icon_item(get_theme_icon("ActionCopy", "EditorIcons"), "Copy Identifier", 4) + %RightClickMenu.add_separator() + %RightClickMenu.add_icon_item( + get_theme_icon("Filesystem", "EditorIcons"), "Show in FileSystem", 2 + ) + %RightClickMenu.add_icon_item( + get_theme_icon("ExternalLink", "EditorIcons"), "Open in External Program", 3 + ) + + ## SORT MENU + %SortOption.set_item_icon(0, get_theme_icon("AnimationTrackGroup", "EditorIcons")) + %SortOption.set_item_icon(1, get_theme_icon("Folder", "EditorIcons")) + %SortOption.set_item_icon(2, get_theme_icon("FolderBrowse", "EditorIcons")) + %SortOption.set_item_icon(3, get_theme_icon("AnimationTrackList", "EditorIcons")) + %SortOption.item_selected.connect(_on_sort_changed) + + await get_tree().process_frame + if DialogicUtil.get_editor_setting("sidebar_collapsed", false): + _hide_sidebar() + + %SortOption.select(DialogicUtil.get_editor_setting("sidebar_sort_mode", 0)) + sort_mode = DialogicUtil.get_editor_setting("sidebar_sort_mode", 0) + update_resource_list() + + +################################################################################ +## SHOW/HIDE SIDEBAR +################################################################################ + + +func _show_sidebar() -> void: + %VBoxPrimary.show() + %VBoxHidden.hide() + DialogicUtil.set_editor_setting("sidebar_collapsed", false) + show_sidebar.emit(true) + + +func _hide_sidebar() -> void: + %VBoxPrimary.hide() + %VBoxHidden.show() + DialogicUtil.set_editor_setting("sidebar_collapsed", true) + show_sidebar.emit(false) ################################################################################ ## RESOURCE LIST ################################################################################ -func _on_editors_resource_opened(resource:Resource) -> void: + +func _on_editors_resource_opened(resource: Resource) -> void: update_resource_list() + pass -func _on_editors_editor_changed(previous:DialogicEditor, current:DialogicEditor) -> void: +func _on_editors_editor_changed(previous: DialogicEditor, current: DialogicEditor) -> void: %ContentListSection.visible = current.current_resource is DialogicTimeline update_resource_list() -func clean_resource_list(resources_list:Array = []) -> PackedStringArray: - return PackedStringArray(resources_list.filter(func(x): return FileAccess.file_exists(x))) +func clean_resource_list(resources_list: Array = []) -> PackedStringArray: + return PackedStringArray(resources_list.filter(func(x): return ResourceLoader.exists(x))) -func update_resource_list(resources_list:PackedStringArray = []) -> void: - var filter :String = %Search.text +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 @@ -64,90 +144,230 @@ func update_resource_list(resources_list:PackedStringArray = []) -> void: var character_directory: Dictionary = DialogicResourceUtil.get_character_directory() var timeline_directory: Dictionary = DialogicResourceUtil.get_timeline_directory() if resources_list.is_empty(): - resources_list = DialogicUtil.get_editor_setting('last_resources', []) + resources_list = DialogicUtil.get_editor_setting("last_resources", []) if !current_file in resources_list: resources_list.append(current_file) resources_list = clean_resource_list(resources_list) %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_name in character_directory: - if character_directory[character_name] in resources_list: - if filter.is_empty() or filter.to_lower() in character_name.to_lower(): - %ResourcesList.add_item( - character_name, - load("res://addons/dialogic/Editor/Images/Resources/character.svg")) - %ResourcesList.set_item_metadata(idx, character_directory[character_name]) - %ResourcesList.set_item_tooltip(idx, character_directory[character_name]) - if character_directory[character_name] == current_file: - %ResourcesList.select(idx) - %ResourcesList.set_item_custom_fg_color(idx, get_theme_color("accent_color", "Editor")) - %CurrentResource.text = character_directory[character_name].get_file() - 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 + %CurrentResource.add_theme_color_override( + "font_uneditable_color", get_theme_color("disabled_font_color", "Editor") + ) + + resource_tree.clear() + var resource_list_items := [] + + var get_directory_items := func(directory:Dictionary, filter:String, icon:Texture2D) -> Array: + var items := [] + for item_name in directory: + if (directory[item_name] in resources_list) and (filter.is_empty() or filter.to_lower() in item_name.to_lower()): + var item := ResourceListItem.new() + item.text = item_name + item.icon = icon + item.metadata = directory[item_name] + item.tooltip = directory[item_name] + items.append(item) + return items + + var character_items: Array = get_directory_items.call(character_directory, filter, load("res://addons/dialogic/Editor/Images/Resources/character.svg")) + var timeline_items: Array = get_directory_items.call(timeline_directory, filter, get_theme_icon("TripleBar", "EditorIcons")) + + + # BUILD TREE + var root: TreeItem = resource_tree.create_item() + + if sort_mode == SortMode.TYPE: + character_items.sort_custom(_sort_by_item_text) + timeline_items.sort_custom(_sort_by_item_text) + if character_items.size() > 0: + var character_tree := add_folder_item("Characters", root) + for item in character_items: + add_item(item, character_tree, current_file) + + if timeline_items.size() > 0: + var timeline_tree := add_folder_item("Timelines", root) + for item in timeline_items: + add_item(item, timeline_tree, current_file) + + if sort_mode == SortMode.NONE: + var all_items := character_items + timeline_items + all_items.sort_custom(_sort_by_item_text) + for item in all_items: + var tree_item = resource_tree.create_item(root) + tree_item.set_text(0, item.text) + tree_item.set_icon(0, item.icon) + tree_item.set_metadata(0, item.metadata) + tree_item.set_tooltip_text(0, item.tooltip) + if item.metadata == current_file: + %CurrentResource.text = item.metadata.get_file() + resource_tree.set_selected(tree_item, 0) + + if sort_mode == SortMode.FOLDER: + var all_items := character_items + timeline_items + var dirs := {} + for item in all_items: + var dir := item.get_parent_directory() as String + if !dirs.has(dir): + dirs[dir] = [] + dirs[dir].append(item) + for dir in dirs: + var dir_item = resource_tree.create_item(root) + dir_item.set_text(0, dir) + dir_item.set_icon(0, get_theme_icon("Folder", "EditorIcons")) + dir_item.set_custom_bg_color(0, get_theme_color("base_color", "Editor")) + for item in dirs[dir]: + var tree_item = resource_tree.create_item(dir_item) + tree_item.set_text(0, item.text) + tree_item.set_icon(0, item.icon) + tree_item.set_metadata(0, item.metadata) + tree_item.set_tooltip_text(0, item.tooltip) + if item.metadata == current_file: + %CurrentResource.text = item.metadata.get_file() + resource_tree.set_selected(tree_item, 0) + + if sort_mode == SortMode.PATH: + var all_items := character_items + timeline_items + var dirs := {} + var regex := RegEx.new() + for item in all_items: + var path := (item.metadata.get_base_dir() as String).replace("res://", "") + regex.compile("(\\w+\\/)?\\w+$") + if !dirs.has(path): + dirs[path] = [] + dirs[path].append(item) + + for dir in dirs: + var dir_display := regex.search(dir).get_string(0) + var dir_item = resource_tree.create_item(root) + var dir_color = ProjectSettings.get_setting("file_customization/folder_colors").get("res://" + dir + "/", get_theme_color("base_color", "Editor")) + var default_color_used = true; + if dir_color as Color != null and Color(dir_color) != get_theme_color("base_color", "Editor"): + dir_color = Color(dir_color) + dir_color.a = 0.2 + dir_color = (dir_color as Color).to_html(true) + default_color_used = false + dir_item.set_text(0, dir_display) + dir_item.set_icon(0, get_theme_icon("Folder", "EditorIcons")) + dir_item.set_custom_bg_color(0, dir_color) + for item in dirs[dir]: + var tree_item = resource_tree.create_item(dir_item) + tree_item.set_text(0, item.text) + tree_item.set_icon(0, item.icon) + tree_item.set_metadata(0, item.metadata) + if !default_color_used: + dir_color = Color(dir_color) + dir_color.a = 0.1 + dir_color = (dir_color as Color).to_html(true) + tree_item.set_custom_bg_color(0, dir_color) + tree_item.set_tooltip_text(0, item.tooltip) + if item.metadata == current_file: + %CurrentResource.text = item.metadata.get_file() + resource_tree.set_selected(tree_item, 0) + 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) + %CurrentResource.add_theme_color_override( + "font_uneditable_color", get_theme_color("font_color", "Editor") + ) + 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 add_item(item:ResourceListItem, parent:TreeItem, current_file := "") -> TreeItem: + var tree_item := resource_tree.create_item(parent) + tree_item.set_text(0, item.text) + tree_item.set_icon(0, item.icon) + tree_item.set_metadata(0, item.metadata) + tree_item.set_tooltip_text(0, item.tooltip) + if item.metadata == current_file: + %CurrentResource.text = item.metadata.get_file() + resource_tree.set_selected(tree_item, 0) + return tree_item -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 add_folder_item(label: String, parent:TreeItem) -> TreeItem: + var folder_item := resource_tree.create_item(parent) + folder_item.set_text(0, label) + folder_item.set_icon(0, get_theme_icon("Folder", "EditorIcons")) + folder_item.set_custom_bg_color(0, get_theme_color("base_color", "Editor")) + return folder_item -func _on_search_text_changed(new_text:String) -> void: +func _on_resources_tree_item_activated() -> void: + if resource_tree.get_selected() == null: + return + var item := resource_tree.get_selected() + if item.get_metadata(0) == null: + return + edit_resource(item.get_metadata(0)) + + +func _on_resources_tree_item_clicked(_pos: Vector2, mouse_button_index: int) -> void: + if mouse_button_index == MOUSE_BUTTON_LEFT: + var selected_item := resource_tree.get_selected() + if selected_item == null: + return + if selected_item.get_metadata(0) == null: + return + var resource_item := load(selected_item.get_metadata(0)) + call_deferred("edit_resource", resource_item) + return + if mouse_button_index == MOUSE_BUTTON_MIDDLE: + remove_item_from_list(resource_tree.get_selected()) + return + if mouse_button_index == MOUSE_BUTTON_RIGHT: + if resource_tree.get_selected().get_metadata(0): + %RightClickMenu.popup_on_parent(Rect2(get_global_mouse_position(), Vector2())) + (%RightClickMenu as PopupMenu).set_meta("item_clicked", resource_tree.get_selected()) + return + + +func _on_search_text_changed(new_text: String) -> void: update_resource_list() + var tree_root := resource_tree.get_root() + var tree_items := tree_root.get_children() + if tree_items.size() == 0: + return + for item in tree_items: + if item.get_children().size() > 0: + resource_tree.set_selected(item.get_child(0), 0) + break + +func _on_search_text_submitted(new_text: String) -> void: + if resource_tree.get_selected() == null: + return + var item := resource_tree.get_selected() + if item.get_metadata(0) == null: + return + edit_resource(item.get_metadata(0)) + %Search.clear() -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 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: + +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) + editors_manager.open_editor(editors_manager.editors["HomePage"].node) -func update_content_list(list:PackedStringArray) -> void: +func update_content_list(list: PackedStringArray) -> void: var prev_selected := "" if %ContentList.is_anything_selected(): prev_selected = %ContentList.get_item_text(%ContentList.get_selected_items()[0]) %ContentList.clear() - %ContentList.add_item('~ Top') + %ContentList.add_item("~ Top") for i in list: - if i.is_empty(): continue + if i.is_empty(): + continue %ContentList.add_item(i) if i == prev_selected: - %ContentList.select(%ContentList.item_count-1) + %ContentList.select(%ContentList.item_count - 1) if list.is_empty(): return @@ -165,3 +385,96 @@ func update_content_list(list:PackedStringArray) -> void: DialogicResourceUtil.set_label_cache(label_directory) + +func remove_item_from_list(item: TreeItem) -> void: + var new_list := [] + for entry in DialogicUtil.get_editor_setting("last_resources", []): + if entry != item.get_metadata(0): + new_list.append(entry) + DialogicUtil.set_editor_setting("last_resources", new_list) + update_resource_list(new_list) + + +func _on_right_click_menu_id_pressed(id: int) -> void: + match id: + 1: # REMOVE ITEM FROM LIST + remove_item_from_list(%RightClickMenu.get_meta("item_clicked")) + 2: # OPEN IN FILESYSTEM + EditorInterface.get_file_system_dock().navigate_to_path( + %RightClickMenu.get_meta("item_clicked").get_metadata(0) + ) + 3: # OPEN IN EXTERNAL EDITOR + OS.shell_open( + ProjectSettings.globalize_path( + %RightClickMenu.get_meta("item_clicked").get_metadata(0) + ) + ) + 4: # COPY IDENTIFIER + DisplayServer.clipboard_set( + DialogicResourceUtil.get_unique_identifier( + %RightClickMenu.get_meta("item_clicked").get_metadata(0) + ) + ) + + +func _on_sort_changed(idx: int) -> void: + if (SortMode as Dictionary).values().has(idx): + sort_mode = idx + DialogicUtil.set_editor_setting("sidebar_sort_mode", idx) + update_resource_list() + else: + sort_mode = SortMode.TYPE + print("Invalid sort mode: ", idx) + + +func _sort_by_item_text(a: ResourceListItem, b: ResourceListItem) -> bool: + return a.text < b.text + + +func edit_resource(resource_item: Variant) -> void: + if resource_item is Resource: + editors_manager.edit_resource(resource_item) + else: + editors_manager.edit_resource(load(resource_item)) + + + + +class ResourceListItem: + extends Object + + var text: String + var index: int = -1 + var icon: Texture + var metadata: String + var tooltip: String + + func _to_string() -> String: + return JSON.stringify( + { + "text": text, + "index": index, + "icon": icon.resource_path, + "metadata": metadata, + "tooltip": tooltip, + "parent_dir": get_parent_directory() + }, + "\t", + false + ) + + func add_to_item_list(item_list: ItemList, current_file: String) -> void: + item_list.add_item(text, icon) + item_list.set_item_metadata(item_list.item_count - 1, metadata) + item_list.set_item_tooltip(item_list.item_count - 1, tooltip) + + func get_parent_directory() -> String: + return (metadata.get_base_dir() as String).split("/")[-1] + + func current_file(sidebar: Control, resource_list: ItemList, current_file: String) -> void: + if metadata == current_file: + resource_list.select(index) + resource_list.set_item_custom_fg_color( + index, resource_list.get_theme_color("accent_color", "Editor") + ) + sidebar.find_child("CurrentResource").text = metadata.get_file() diff --git a/addons/dialogic/Editor/Common/toolbar.gd b/addons/dialogic/Editor/Common/toolbar.gd index 1921802b7..e49c1ea04 100644 --- a/addons/dialogic/Editor/Common/toolbar.gd +++ b/addons/dialogic/Editor/Common/toolbar.gd @@ -6,12 +6,11 @@ extends HBoxContainer ################################################################################ ## EDITOR BUTTONS/LABELS ################################################################################ -func _ready(): +func _ready() -> void: if owner.get_parent() is SubViewport: return - var editor_scale := DialogicUtil.get_editor_scale() - %CustomButtons.custom_minimum_size.y = 33*editor_scale - + %CustomButtons.custom_minimum_size.y = 33 * DialogicUtil.get_editor_scale() + for child in get_children(): if child is Button: child.queue_free() @@ -35,7 +34,7 @@ func add_custom_button(label:String, icon:Texture) -> Button: 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 diff --git a/addons/dialogic/Editor/Common/unique_identifiers_manager.gd b/addons/dialogic/Editor/Common/unique_identifiers_manager.gd index 28a47354e..fc7c993e0 100644 --- a/addons/dialogic/Editor/Common/unique_identifiers_manager.gd +++ b/addons/dialogic/Editor/Common/unique_identifiers_manager.gd @@ -48,7 +48,7 @@ func fill_table() -> void: func _on_identifier_table_item_edited() -> void: var item: TreeItem = %IdentifierTable.get_edited() - var new_identifier : String = item.get_text(1) + var new_identifier: String = item.get_text(1) if new_identifier == item.get_metadata(1): diff --git a/addons/dialogic/Editor/Common/update_install_window.gd b/addons/dialogic/Editor/Common/update_install_window.gd index 7cdda8b16..0d5a49809 100644 --- a/addons/dialogic/Editor/Common/update_install_window.gd +++ b/addons/dialogic/Editor/Common/update_install_window.gd @@ -1,22 +1,22 @@ @tool extends Control -var current_info : Dictionary = {} +var current_info := {} @onready var editor_view := find_parent('EditorView') -func _ready(): +func _ready() -> void: await editor_view.ready theme = editor_view.theme %Install.icon = editor_view.get_theme_icon("AssetLib", "EditorIcons") %LoadingIcon.texture = editor_view.get_theme_icon("KeyTrackScale", "EditorIcons") %InstallWarning.modulate = editor_view.get_theme_color("warning_color", "Editor") - + %CloseButton.icon = editor_view.get_theme_icon("Close", "EditorIcons") DialogicUtil.get_dialogic_plugin().get_editor_interface().get_resource_filesystem().resources_reimported.connect(_on_resources_reimported) -func open(): +func open() -> void: get_parent().popup_centered_ratio(0.5) get_parent().mode = Window.MODE_WINDOWED get_parent().move_to_foreground() @@ -55,7 +55,7 @@ func load_info(info:Dictionary, update_type:int) -> void: %Install.disabled = true %UpdateName.text = info.name - %Content.text = markdown_to_bbcode('#'+info.body.get_slice('#', 1)).strip_edges() + %Content.text = markdown_to_bbcode(info.body).get_slice("\n[font_size", 0).strip_edges() %ShortInfo.text = "Published on "+info.published_at.substr(0, info.published_at.find('T'))+" by "+info.author.login if info.has("html_url"): %ReadFull.uri = info.html_url @@ -76,11 +76,11 @@ func load_info(info:Dictionary, update_type:int) -> void: else: %Reactions.hide() -func _on_window_close_requested(): +func _on_window_close_requested() -> void: get_parent().visible = false -func _on_install_pressed(): +func _on_install_pressed() -> void: find_parent('UpdateManager').request_update_download() %InfoLabel.text = "Downloading. This can take a moment." @@ -88,7 +88,7 @@ func _on_install_pressed(): %LoadingIcon.create_tween().set_loops().tween_property(%LoadingIcon, 'rotation', 2*PI, 1).from(0) -func _on_refresh_pressed(): +func _on_refresh_pressed() -> void: find_parent('UpdateManager').request_update_check() @@ -154,7 +154,7 @@ func markdown_to_bbcode(text:String) -> String: while res: text = text.replace(res.get_string(), '[code][bgcolor='+get_theme_color("box_selection_fill_color", "Editor").to_html()+']'+res.get_string('text').strip_edges()+'[/bgcolor][/code]') res = big_code_regex.search(text) - + return text @@ -163,14 +163,18 @@ func _on_content_meta_clicked(meta:Variant) -> void: OS.shell_open(str(meta)) -func _on_install_mouse_entered(): +func _on_install_mouse_entered() -> void: if not %Install.disabled: %InstallWarning.show() -func _on_install_mouse_exited(): +func _on_install_mouse_exited() -> void: %InstallWarning.hide() -func _on_restart_pressed(): +func _on_restart_pressed() -> void: DialogicUtil.get_dialogic_plugin().get_editor_interface().restart_editor(true) + + +func _on_close_button_pressed() -> void: + get_parent().hide() diff --git a/addons/dialogic/Editor/Common/update_install_window.tscn b/addons/dialogic/Editor/Common/update_install_window.tscn index d4c2d2e51..9c7788f4d 100644 --- a/addons/dialogic/Editor/Common/update_install_window.tscn +++ b/addons/dialogic/Editor/Common/update_install_window.tscn @@ -290,9 +290,19 @@ layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 7 +[node name="Close" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="CloseButton" type="Button" parent="VBoxContainer/Close"] +unique_name_in_owner = true +layout_mode = 2 +text = "Close" + [connection signal="pressed" from="VBoxContainer/HBoxContainer2/VBox/ScrollContainer/VBoxContainer/Panel/Refresh" to="." method="_on_refresh_pressed"] [connection signal="meta_clicked" from="VBoxContainer/HBoxContainer2/VBox/ScrollContainer/VBoxContainer/Content" to="." method="_on_content_meta_clicked"] [connection signal="pressed" from="VBoxContainer/HBoxContainer2/VBox/ScrollContainer/VBoxContainer/HBoxContainer/PanelContainer/HBox/Restart" to="." method="_on_restart_pressed"] [connection signal="mouse_entered" from="VBoxContainer/HBoxContainer2/VBox/ScrollContainer/VBoxContainer/HBoxContainer/PanelContainer/HBox/Install" to="." method="_on_install_mouse_entered"] [connection signal="mouse_exited" from="VBoxContainer/HBoxContainer2/VBox/ScrollContainer/VBoxContainer/HBoxContainer/PanelContainer/HBox/Install" to="." method="_on_install_mouse_exited"] [connection signal="pressed" from="VBoxContainer/HBoxContainer2/VBox/ScrollContainer/VBoxContainer/HBoxContainer/PanelContainer/HBox/Install" to="." method="_on_install_pressed"] +[connection signal="pressed" from="VBoxContainer/Close/CloseButton" to="." method="_on_close_button_pressed"] diff --git a/addons/dialogic/Editor/Common/update_manager.gd b/addons/dialogic/Editor/Common/update_manager.gd index d1296a33d..36a116ec5 100644 --- a/addons/dialogic/Editor/Common/update_manager.gd +++ b/addons/dialogic/Editor/Common/update_manager.gd @@ -11,13 +11,13 @@ enum DownloadResult {SUCCESS, FAILURE} enum ReleaseState {ALPHA, BETA, STABLE} const REMOTE_RELEASES_URL := "https://api.github.com/repos/dialogic-godot/dialogic/releases" -const TEMP_FILE_NAME = "user://temp.zip" +const TEMP_FILE_NAME := "user://temp.zip" -var current_version : String = "" +var current_version := "" var update_info: Dictionary var current_info: Dictionary -var version_indicator :Button +var version_indicator: Button func _ready() -> void: request_update_check() @@ -43,14 +43,14 @@ func _on_UpdateCheck_request_completed(result:int, response_code:int, headers:Pa return # Work out the next version from the releases information on GitHub - var response :Variant= JSON.parse_string(body.get_string_from_utf8()) + var response: Variant = JSON.parse_string(body.get_string_from_utf8()) if typeof(response) != TYPE_ARRAY: return var current_release_info := get_release_tag_info(get_current_version()) # GitHub releases are in order of creation, not order of version - var versions :Array = (response as Array).filter(compare_versions.bind(current_release_info)) + var versions: Array = (response as Array).filter(compare_versions.bind(current_release_info)) if versions.size() > 0: update_info = versions[0] update_check_completed.emit(UpdateCheckResult.UPDATE_AVAILABLE) @@ -92,13 +92,13 @@ func get_release_tag_info(release_tag:String) -> Dictionary: release_tag = release_tag.substr(0, release_tag.find('(')) release_tag = release_tag.to_lower() - var regex := RegEx.create_from_string('(?\\d+\\.\\d+)(-(?alpha|beta)-)?(?(2)(?\\d*)|\\.(?\\d*))?') + var regex := RegEx.create_from_string(r"(?\d+\.\d+)(-(?alpha|beta)-)?(?(2)(?\d*)|\.(?\d*))?") var result: RegExMatch = regex.search(release_tag) if !result: return {} - var info:Dictionary = {'tag':release_tag} + var info: Dictionary = {'tag':release_tag} info['major'] = float(result.get_string('major')) info['minor'] = int(result.get_string('minor')) @@ -141,7 +141,7 @@ func _on_DownloadRequest_completed(result:int, response_code:int, headers:Packed zip_reader.open(TEMP_FILE_NAME) var files: PackedStringArray = zip_reader.get_files() - var base_path = files[0].path_join('addons/') + var base_path: String = files[0].path_join('addons/') for path in files: if not "dialogic/" in path: continue @@ -162,14 +162,14 @@ func _on_DownloadRequest_completed(result:int, response_code:int, headers:Packed ###################### SOME UI MANAGEMENT ##################################### ################################################################################ -func setup_version_indicator(): +func setup_version_indicator() -> void: version_indicator = %Sidebar.get_node('%CurrentVersion') version_indicator.pressed.connect($Window/UpdateInstallWindow.open) version_indicator.text = get_current_version() func _on_update_check_completed(result:int): - var result_color : Color + var result_color: Color match result: UpdateCheckResult.UPDATE_AVAILABLE: result_color = version_indicator.get_theme_color("warning_color", "Editor") diff --git a/addons/dialogic/Editor/Events/BranchEnd.gd b/addons/dialogic/Editor/Events/BranchEnd.gd index 0500cf639..d43ebe345 100644 --- a/addons/dialogic/Editor/Events/BranchEnd.gd +++ b/addons/dialogic/Editor/Events/BranchEnd.gd @@ -16,7 +16,7 @@ var selected := false func _ready() -> void: $Icon.icon = get_theme_icon("GuiSpinboxUpdown", "EditorIcons") - $Spacer.custom_minimum_size.x = 90*DialogicUtil.get_editor_scale() + $Spacer.custom_minimum_size.x = 90 * DialogicUtil.get_editor_scale() visual_deselect() parent_node_changed() @@ -59,7 +59,7 @@ func update_hidden_events_indicator(hidden_events_count:int = 0) -> void: ## Called by the visual timeline editor func set_indent(indent: int) -> void: - $Indent.custom_minimum_size = Vector2(indent_size * indent*DialogicUtil.get_editor_scale(), 0) + $Indent.custom_minimum_size = Vector2(indent_size * indent * DialogicUtil.get_editor_scale(), 0) $Indent.visible = indent != 0 current_indent_level = indent queue_redraw() diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.gd b/addons/dialogic/Editor/Events/EventBlock/event_block.gd index bce4a1463..f5c39d365 100644 --- a/addons/dialogic/Editor/Events/EventBlock/event_block.gd +++ b/addons/dialogic/Editor/Events/EventBlock/event_block.gd @@ -6,7 +6,7 @@ extends MarginContainer signal content_changed() ## REFERENCES -var resource : DialogicEvent +var resource: DialogicEvent var editor_reference # for choice and condition var end_node: Node = null: @@ -14,7 +14,7 @@ var end_node: Node = null: return end_node set(node): end_node = node - %CollapseButton.visible = true if end_node else false + %ToggleChildrenVisibilityButton.visible = true if end_node else false ## FLAGS @@ -40,7 +40,7 @@ var current_indent_level := 1 #region UI AND LOGIC INITIALIZATION ################################################################################ -func _ready(): +func _ready() -> void: if get_parent() is SubViewport: return @@ -63,8 +63,12 @@ func initialize_ui() -> void: %Warning.position = Vector2(-5 * _scale, -10 * _scale) # Expand Button - %ExpandButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") - %ExpandButton.modulate = get_theme_color("readonly_color", "Editor") + %ToggleBodyVisibilityButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") + %ToggleBodyVisibilityButton.set("theme_override_colors/icon_normal_color", get_theme_color("contrast_color_2", "Editor")) + %ToggleBodyVisibilityButton.set("theme_override_colors/icon_hover_color", get_theme_color("accent_color", "Editor")) + %ToggleBodyVisibilityButton.set("theme_override_colors/icon_pressed_color", get_theme_color("contrast_color_2", "Editor")) + %ToggleBodyVisibilityButton.set("theme_override_colors/icon_hover_pressed_color", get_theme_color("accent_color", "Editor")) + %ToggleBodyVisibilityButton.add_theme_stylebox_override('hover_pressed', StyleBoxEmpty.new()) # Icon Panel %IconPanel.tooltip_text = resource.event_name @@ -86,9 +90,9 @@ func initialize_ui() -> void: %Header.add_theme_constant_override("custom_constants/separation", 5 * _scale) # Collapse Button - %CollapseButton.toggled.connect(_on_collapse_toggled) - %CollapseButton.icon = get_theme_icon("Collapse", "EditorIcons") - %CollapseButton.hide() + %ToggleChildrenVisibilityButton.toggled.connect(_on_collapse_toggled) + %ToggleChildrenVisibilityButton.icon = get_theme_icon("Collapse", "EditorIcons") + %ToggleChildrenVisibilityButton.hide() %Body.add_theme_constant_override("margin_left", icon_size * _scale) @@ -103,7 +107,7 @@ func initialize_logic() -> void: content_changed.connect(recalculate_field_visibility) - _on_ExpandButton_toggled(resource.expand_by_default or resource.created_by_button) + _on_ToggleBodyVisibility_toggled(resource.expand_by_default or resource.created_by_button) #endregion @@ -138,7 +142,7 @@ func set_warning(text:String= "") -> void: func set_indent(indent: int) -> void: - add_theme_constant_override("margin_left", indent_size*indent*DialogicUtil.get_editor_scale()) + add_theme_constant_override("margin_left", indent_size * indent * DialogicUtil.get_editor_scale()) current_indent_level = indent #endregion @@ -161,7 +165,8 @@ var FIELD_SCENES := { DialogicEvent.ValueType.NUMBER: "res://addons/dialogic/Editor/Events/Fields/field_number.tscn", DialogicEvent.ValueType.VECTOR2: "res://addons/dialogic/Editor/Events/Fields/field_vector2.tscn", DialogicEvent.ValueType.VECTOR3: "res://addons/dialogic/Editor/Events/Fields/field_vector3.tscn", - DialogicEvent.ValueType.VECTOR4: "res://addons/dialogic/Editor/Events/Fields/field_vector4.tscn" + DialogicEvent.ValueType.VECTOR4: "res://addons/dialogic/Editor/Events/Fields/field_vector4.tscn", + DialogicEvent.ValueType.COLOR: "res://addons/dialogic/Editor/Events/Fields/field_color.tscn" } func build_editor(build_header:bool = true, build_body:bool = false) -> void: @@ -189,7 +194,7 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: ### -------------------------------------------------------------------- ### 1. CREATE A NODE OF THE CORRECT TYPE FOR THE PROPERTY - var editor_node : Control + var editor_node: Control ### LINEBREAK if p.name == "linebreak": @@ -213,12 +218,13 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: elif p.field_type == resource.ValueType.BUTTON: editor_node = Button.new() editor_node.text = p.display_info.text + editor_node.tooltip_text = p.display_info.get('tooltip', '') 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.custom_minimum_size.x = 30 * DialogicUtil.get_editor_scale() editor_node.pressed.connect(p.display_info.callable) ## CUSTOM @@ -318,13 +324,15 @@ func recalculate_field_visibility() -> void: else: if _evaluate_visibility_condition(p): if p.node != null: + if p.node.visible == false and p.has("property"): + p.node._set_value(resource.get(p.property)) p.node.show() if p.location == 1: has_any_enabled_body_content = true else: if p.node != null: p.node.hide() - %ExpandButton.visible = has_any_enabled_body_content + %ToggleBodyVisibilityButton.visible = has_any_enabled_body_content func set_property(property_name:String, value:Variant) -> void: @@ -368,7 +376,7 @@ func _on_resource_ui_update_needed() -> void: func _on_collapse_toggled(toggled:bool) -> void: collapsed = toggled - var timeline_editor = find_parent('VisualEditor') + var timeline_editor: Node = 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 @@ -376,15 +384,15 @@ func _on_collapse_toggled(toggled:bool) -> void: -func _on_ExpandButton_toggled(button_pressed:bool) -> void: +func _on_ToggleBodyVisibility_toggled(button_pressed:bool) -> void: if button_pressed and !body_was_build: build_editor(false, true) - %ExpandButton.set_pressed_no_signal(button_pressed) + %ToggleBodyVisibilityButton.set_pressed_no_signal(button_pressed) if button_pressed: - %ExpandButton.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") + %ToggleBodyVisibilityButton.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") else: - %ExpandButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") + %ToggleBodyVisibilityButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") expanded = button_pressed %Body.visible = button_pressed @@ -398,11 +406,11 @@ func _on_EventNode_gui_input(event:InputEvent) -> void: 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) + _on_ToggleBodyVisibility_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') + 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 == "": diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.tscn b/addons/dialogic/Editor/Events/EventBlock/event_block.tscn index f0b363c0c..5bd715bd9 100644 --- a/addons/dialogic/Editor/Events/EventBlock/event_block.tscn +++ b/addons/dialogic/Editor/Events/EventBlock/event_block.tscn @@ -11,7 +11,7 @@ corner_radius_top_right = 5 corner_radius_bottom_right = 5 corner_radius_bottom_left = 5 -[sub_resource type="Image" id="Image_ng2y4"] +[sub_resource type="Image" id="Image_mem38"] 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", @@ -21,7 +21,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_rc1wh"] -image = SubResource("Image_ng2y4") +image = SubResource("Image_mem38") [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ee4ub"] @@ -91,18 +91,22 @@ stretch_mode = 5 unique_name_in_owner = true layout_mode = 2 -[node name="ExpandButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] +[node name="ToggleBodyVisibilityButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] unique_name_in_owner = true -modulate = Color(0, 0, 0, 1) +custom_minimum_size = Vector2(20, 0) layout_mode = 2 size_flags_horizontal = 0 tooltip_text = "Fold/Unfold Settings" +theme_override_styles/normal = SubResource("StyleBoxEmpty_ee4ub") +theme_override_styles/hover = SubResource("StyleBoxEmpty_ee4ub") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_ee4ub") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_ee4ub") theme_override_styles/focus = SubResource("StyleBoxEmpty_ee4ub") toggle_mode = true icon = SubResource("ImageTexture_rc1wh") flat = true -[node name="CollapseButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] +[node name="ToggleChildrenVisibilityButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] unique_name_in_owner = true visible = false layout_mode = 2 @@ -126,4 +130,4 @@ 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"] +[connection signal="toggled" from="PanelContainer/VBoxContainer/Header/ToggleBodyVisibilityButton" to="." method="_on_ToggleBodyVisibility_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 index a2b42b9ec..0b765f633 100644 --- a/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd +++ b/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd @@ -1,9 +1,9 @@ @tool extends PopupMenu -var current_event : Node = null +var current_event: Node = null -func _ready(): +func _ready() -> void: clear() add_icon_item(get_theme_icon("Duplicate", "EditorIcons"), "Duplicate") add_separator() diff --git a/addons/dialogic/Editor/Events/Fields/array_part.gd b/addons/dialogic/Editor/Events/Fields/array_part.gd index 97352d8ad..20c7c72c8 100644 --- a/addons/dialogic/Editor/Events/Fields/array_part.gd +++ b/addons/dialogic/Editor/Events/Fields/array_part.gd @@ -11,138 +11,18 @@ var value_type: int = -1 var current_value: Variant func _ready() -> void: - %ValueType.options = [{ - 'label': 'String', - 'icon': ["String", "EditorIcons"], - 'value': TYPE_STRING - },{ - 'label': 'Number (int)', - 'icon': ["int", "EditorIcons"], - 'value': TYPE_INT - },{ - 'label': 'Number (float)', - 'icon': ["float", "EditorIcons"], - 'value': TYPE_FLOAT - },{ - 'label': 'Boolean', - 'icon': ["bool", "EditorIcons"], - 'value': TYPE_BOOL - },{ - 'label': 'Expression', - 'icon': ["Variant", "EditorIcons"], - 'value': TYPE_MAX - } - ] - %ValueType.symbol_only = true - %ValueType.value_changed.connect(_on_type_changed.bind()) - %ValueType.tooltip_text = "Change type" - + %FlexValue.value_changed.connect(emit_signal.bind("value_changed")) %Delete.icon = get_theme_icon("Remove", "EditorIcons") func set_value(value:Variant): - change_field_type(deduce_type(value)) - %ValueType.set_value(deduce_type(value)) - current_value = value - match value_type: - TYPE_BOOL: - value_field.button_pressed = value - TYPE_STRING: - value_field.text = value - TYPE_FLOAT, TYPE_INT: - value_field.set_value(value) - TYPE_MAX, _: - value_field.text = value.trim_prefix('@') - - -func deduce_type(value:Variant) -> int: - if value is String and value.begins_with('@'): - return TYPE_MAX - else: - return typeof(value) - - -func _on_type_changed(prop:String, type:Variant) -> void: - if type == value_type: - return - - match type: - TYPE_BOOL: - if typeof(current_value) == TYPE_STRING: - current_value = DialogicUtil.str_to_bool(current_value) - elif value_type == TYPE_FLOAT or value_type == TYPE_INT: - current_value = bool(current_value) - else: - current_value = true if current_value else false - set_value(current_value) - TYPE_STRING: - current_value = str(current_value).trim_prefix('@') - set_value(current_value) - TYPE_FLOAT, TYPE_INT: - current_value = float(current_value) - set_value(current_value) - TYPE_MAX,_: - current_value = var_to_str(current_value) - set_value('@'+current_value) - - - emit_signal.call_deferred('value_changed') + %FlexValue.set_value(value) func get_value() -> Variant: - return current_value + return %FlexValue.current_value func _on_delete_pressed() -> void: queue_free() value_changed.emit() - - -func change_field_type(type:int) -> void: - if type == value_type: - return - - value_type = type - - if value_field: - value_field.queue_free() - match type: - TYPE_BOOL: - value_field = CheckBox.new() - value_field.toggled.connect(_on_bool_toggled) - TYPE_STRING: - value_field = LineEdit.new() - value_field.text_changed.connect(_on_str_text_changed) - value_field.expand_to_text_length = true - TYPE_FLOAT, TYPE_INT: - value_field = load("res://addons/dialogic/Editor/Events/Fields/field_number.tscn").instantiate() - if type == TYPE_FLOAT: - value_field.use_float_mode() - else: - value_field.use_int_mode() - value_field.value_changed.connect(_on_number_value_changed.bind(type == TYPE_INT)) - TYPE_MAX, _: - value_field = LineEdit.new() - value_field.expand_to_text_length = true - value_field.text_changed.connect(_on_expression_changed) - $Value.add_child(value_field) - $Value.move_child(value_field, 1) - -func _on_bool_toggled(value:bool) -> void: - current_value = value - value_changed.emit() - -func _on_str_text_changed(value:String) -> void: - current_value = value - value_changed.emit() - -func _on_expression_changed(value:String) -> void: - current_value = '@'+value - value_changed.emit() - -func _on_number_value_changed(prop:String, value:float, int := false) -> void: - if int: - current_value = int(value) - else: - current_value = value - value_changed.emit() diff --git a/addons/dialogic/Editor/Events/Fields/array_part.tscn b/addons/dialogic/Editor/Events/Fields/array_part.tscn index 72ae7ebdc..7ec2d25a4 100644 --- a/addons/dialogic/Editor/Events/Fields/array_part.tscn +++ b/addons/dialogic/Editor/Events/Fields/array_part.tscn @@ -1,26 +1,9 @@ -[gd_scene load_steps=7 format=3 uid="uid://ch4j2lesn1sis"] +[gd_scene load_steps=5 format=3 uid="uid://ch4j2lesn1sis"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/array_part.gd" id="1"] -[ext_resource type="Theme" uid="uid://d3g4i4dshtdpu" path="res://addons/dialogic/Editor/Events/styles/InputFieldsStyle.tres" id="2"] -[ext_resource type="PackedScene" uid="uid://d3bhehatwoio" path="res://addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn" id="3_otpho"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fe32l"] -content_margin_left = 2.0 -content_margin_top = 2.0 -content_margin_right = 2.0 -content_margin_bottom = 2.0 -draw_center = false -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.282353, 0.486275, 0.458824, 1) -corner_radius_top_left = 4 -corner_radius_top_right = 4 -corner_radius_bottom_right = 4 -corner_radius_bottom_left = 4 - -[sub_resource type="Image" id="Image_sbk5s"] +[ext_resource type="PackedScene" uid="uid://dl08ubinx6ugu" path="res://addons/dialogic/Editor/Events/Fields/field_flex_value.tscn" id="3_s4j7i"] + +[sub_resource type="Image" id="Image_dcsrk"] 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", @@ -29,30 +12,28 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_16rly"] -image = SubResource("Image_sbk5s") +[sub_resource type="ImageTexture" id="ImageTexture_cpbga"] +image = SubResource("Image_dcsrk") [node name="ArrayValue" type="PanelContainer"] offset_left = 2.0 offset_right = 76.0 offset_bottom = 24.0 -theme_override_styles/panel = SubResource("StyleBoxFlat_fe32l") +theme_type_variation = &"DialogicEventEditGroup" script = ExtResource("1") [node name="Value" type="HBoxContainer" parent="."] layout_mode = 2 -theme = ExtResource("2") -[node name="ValueType" parent="Value" instance=ExtResource("3_otpho")] +[node name="FlexValue" parent="Value" instance=ExtResource("3_s4j7i")] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "Change type" -text = "" [node name="Delete" type="Button" parent="Value"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_16rly") +tooltip_text = "Remove" +icon = SubResource("ImageTexture_cpbga") flat = true [connection signal="pressed" from="Value/Delete" to="." method="_on_delete_pressed"] diff --git a/addons/dialogic/Editor/Events/Fields/dictionary_part.gd b/addons/dialogic/Editor/Events/Fields/dictionary_part.gd index f6a7277ab..7c28572d7 100644 --- a/addons/dialogic/Editor/Events/Fields/dictionary_part.gd +++ b/addons/dialogic/Editor/Events/Fields/dictionary_part.gd @@ -1,39 +1,44 @@ @tool -extends HBoxContainer +extends PanelContainer -## Event block field part for the Array field. +## Event block field part for the Dictionary field. signal value_changed() func set_key(value:String) -> void: - $Key.text = str(value) + %Key.text = str(value) func get_key() -> String: - return $Key.text + return %Key.text -func set_value(value:String): - $Value.text = str(value) +func set_value(value:Variant) -> void: + %FlexValue.set_value(value) -func get_value() -> String: - return $Value.text +func get_value() -> Variant: + return %FlexValue.current_value func _ready() -> void: - $Delete.icon = get_theme_icon("Remove", "EditorIcons") + %Delete.icon = get_theme_icon("Remove", "EditorIcons") -func _on_Delete_pressed() -> void: - queue_free() +func focus_key() -> void: + %Key.grab_focus() + + +func _on_key_text_changed(new_text: String) -> void: value_changed.emit() -func _on_Key_text_changed(new_text:String) -> void: +func _on_flex_value_value_changed() -> void: value_changed.emit() -func _on_Value_text_changed(new_text:String) -> void: +func _on_delete_pressed() -> void: + queue_free() value_changed.emit() + diff --git a/addons/dialogic/Editor/Events/Fields/dictionary_part.tscn b/addons/dialogic/Editor/Events/Fields/dictionary_part.tscn index aa012bb62..dc1f961f8 100644 --- a/addons/dialogic/Editor/Events/Fields/dictionary_part.tscn +++ b/addons/dialogic/Editor/Events/Fields/dictionary_part.tscn @@ -1,9 +1,9 @@ [gd_scene load_steps=5 format=3 uid="uid://b27yweami3mxi"] -[ext_resource type="Theme" uid="uid://d3g4i4dshtdpu" path="res://addons/dialogic/Editor/Events/styles/InputFieldsStyle.tres" id="1_4ehmb"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/dictionary_part.gd" id="2_q88pg"] +[ext_resource type="PackedScene" uid="uid://dl08ubinx6ugu" path="res://addons/dialogic/Editor/Events/Fields/field_flex_value.tscn" id="3_p082d"] -[sub_resource type="Image" id="Image_esvau"] +[sub_resource type="Image" id="Image_dcsrk"] 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", @@ -12,27 +12,43 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_bywig"] -image = SubResource("Image_esvau") +[sub_resource type="ImageTexture" id="ImageTexture_cpbga"] +image = SubResource("Image_dcsrk") -[node name="Value" type="HBoxContainer"] -theme = ExtResource("1_4ehmb") +[node name="DictionaryPart" type="PanelContainer"] +offset_left = 1.0 +offset_top = -1.0 +offset_right = 131.0 +offset_bottom = 32.0 +theme_type_variation = &"DialogicEventEditGroup" script = ExtResource("2_q88pg") -[node name="Key" type="LineEdit" parent="."] +[node name="HBox" type="HBoxContainer" parent="."] layout_mode = 2 -size_flags_horizontal = 3 -expand_to_text_length = true -[node name="Value" type="LineEdit" parent="."] +[node name="Key" type="LineEdit" parent="HBox"] +unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 +theme_type_variation = &"DialogicEventEdit" expand_to_text_length = true +select_all_on_focus = true + +[node name="Label" type="Label" parent="HBox"] +layout_mode = 2 +text = ":" + +[node name="FlexValue" parent="HBox" instance=ExtResource("3_p082d")] +unique_name_in_owner = true +layout_mode = 2 -[node name="Delete" type="Button" parent="."] +[node name="Delete" type="Button" parent="HBox"] +unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_bywig") +tooltip_text = "Remove" +icon = SubResource("ImageTexture_cpbga") +flat = true -[connection signal="text_changed" from="Key" to="." method="_on_Key_text_changed"] -[connection signal="text_changed" from="Value" to="." method="_on_Value_text_changed"] -[connection signal="pressed" from="Delete" to="." method="_on_Delete_pressed"] +[connection signal="text_changed" from="HBox/Key" to="." method="_on_key_text_changed"] +[connection signal="value_changed" from="HBox/FlexValue" to="." method="_on_flex_value_value_changed"] +[connection signal="pressed" from="HBox/Delete" to="." method="_on_delete_pressed"] diff --git a/addons/dialogic/Editor/Events/Fields/field_array.gd b/addons/dialogic/Editor/Events/Fields/field_array.gd index 6e0d59c5f..301ade502 100644 --- a/addons/dialogic/Editor/Events/Fields/field_array.gd +++ b/addons/dialogic/Editor/Events/Fields/field_array.gd @@ -7,7 +7,7 @@ extends DialogicVisualEditorField const ArrayValue := "res://addons/dialogic/Editor/Events/Fields/array_part.tscn" -func _ready(): +func _ready() -> void: %Add.icon = get_theme_icon("Add", "EditorIcons") %Add.pressed.connect(_on_AddButton_pressed) @@ -39,7 +39,7 @@ func recalculate_values() -> void: func _on_AddButton_pressed() -> void: - var x :Control = load(ArrayValue).instantiate() + var x: Control = load(ArrayValue).instantiate() add_child(x) x.set_value("") x.value_changed.connect(recalculate_values) diff --git a/addons/dialogic/Editor/Events/Fields/field_array.tscn b/addons/dialogic/Editor/Events/Fields/field_array.tscn index 7cffed150..ff5ac6c64 100644 --- a/addons/dialogic/Editor/Events/Fields/field_array.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_array.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_array.gd" id="2"] -[sub_resource type="Image" id="Image_u0aqk"] +[sub_resource type="Image" id="Image_dcsrk"] 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", @@ -11,8 +11,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_7iwuk"] -image = SubResource("Image_u0aqk") +[sub_resource type="ImageTexture" id="ImageTexture_cpbga"] +image = SubResource("Image_dcsrk") [node name="Field_Array" type="HFlowContainer"] offset_right = 329.0 @@ -23,5 +23,6 @@ script = ExtResource("2") [node name="Add" type="Button" parent="."] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "Add another value" -icon = SubResource("ImageTexture_7iwuk") +tooltip_text = "Add value" +icon = SubResource("ImageTexture_cpbga") +flat = true diff --git a/addons/dialogic/Editor/Events/Fields/field_bool_button.gd b/addons/dialogic/Editor/Events/Fields/field_bool_button.gd index b4373e79f..1593be3d8 100644 --- a/addons/dialogic/Editor/Events/Fields/field_bool_button.gd +++ b/addons/dialogic/Editor/Events/Fields/field_bool_button.gd @@ -17,6 +17,8 @@ func _ready() -> void: func _load_display_info(info:Dictionary) -> void: if info.has('editor_icon'): + if not is_inside_tree(): + await ready self.icon = callv('get_theme_icon', info.editor_icon) else: self.icon = info.get('icon', null) diff --git a/addons/dialogic/Editor/Events/Fields/field_color.gd b/addons/dialogic/Editor/Events/Fields/field_color.gd new file mode 100644 index 000000000..707dfc51b --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_color.gd @@ -0,0 +1,30 @@ +@tool +extends DialogicVisualEditorField + +## Event block field for color values. + +#region MAIN METHODS +################################################################################ + +func _ready() -> void: + self.color_changed.connect(_on_value_changed) + + +func _load_display_info(info:Dictionary) -> void: + self.edit_alpha = info.get("edit_alpha", true) + + +func _set_value(value:Variant) -> void: + if value is Color: + self.color = Color(value) + +#endregion + + +#region SIGNAL METHODS +################################################################################ + +func _on_value_changed(value: Color) -> void: + value_changed.emit(property_name, value) + +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/field_color.tscn b/addons/dialogic/Editor/Events/Fields/field_color.tscn new file mode 100644 index 000000000..5fd3ff287 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_color.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://4e0kjekan5e7"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_color.gd" id="1_l666a"] + +[node name="Field_Color" type="ColorPickerButton"] +custom_minimum_size = Vector2(48, 0) +offset_right = 64.0 +offset_bottom = 31.0 +theme_type_variation = &"DialogicEventEdit" +text = " " +color = Color(1, 1, 1, 1) +script = ExtResource("1_l666a") diff --git a/addons/dialogic/Editor/Events/Fields/field_condition.gd b/addons/dialogic/Editor/Events/Fields/field_condition.gd index d7a149fb8..db40270b5 100644 --- a/addons/dialogic/Editor/Events/Fields/field_condition.gd +++ b/addons/dialogic/Editor/Events/Fields/field_condition.gd @@ -3,8 +3,8 @@ extends DialogicVisualEditorField ## Event block field for displaying conditions in either a simple or complex way. -var _current_value1 :Variant = "" -var _current_value2 :Variant = "" +var _current_value1: Variant = "" +var _current_value2: Variant = "" #region MAIN METHODS ################################################################################ @@ -21,7 +21,7 @@ func _set_value(value:Variant) -> void: -func _autofocus(): +func _autofocus() -> void: %Value1Variable.grab_focus() #endregion @@ -96,7 +96,7 @@ func value_type_changed(property:String, value_type:int, value_name:String) -> v get_node('%'+value_name+'Text').hide() get_node('%'+value_name+'Number').hide() get_node('%'+value_name+'Bool').hide() - var current_val :Variant = "" + var current_val: Variant = "" if '1' in value_name: current_val = _current_value1 else: @@ -237,7 +237,7 @@ func _on_complex_editor_text_changed(new_text:String) -> void: func get_variable_suggestions(filter:String) -> Dictionary: var suggestions := {} - var vars :Dictionary= ProjectSettings.get_setting('dialogic/variables', {}) + 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/field_condition.tscn b/addons/dialogic/Editor/Events/Fields/field_condition.tscn index ea515f7fd..8024c5b40 100644 --- a/addons/dialogic/Editor/Events/Fields/field_condition.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_condition.tscn @@ -7,7 +7,7 @@ [ext_resource type="PackedScene" uid="uid://dm5hxmhyyxgq" path="res://addons/dialogic/Editor/Events/Fields/field_bool_check.tscn" id="5_1x02a"] [ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="6_5a2xd"] -[sub_resource type="Image" id="Image_cgfp5"] +[sub_resource type="Image" id="Image_je1w7"] 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", @@ -16,8 +16,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_4jujf"] -image = SubResource("Image_cgfp5") +[sub_resource type="ImageTexture" id="ImageTexture_81s3d"] +image = SubResource("Image_je1w7") [node name="Field_Condition" type="HBoxContainer"] offset_right = 77.0 @@ -94,7 +94,7 @@ unique_name_in_owner = true layout_mode = 2 tooltip_text = "Use complex expression" toggle_mode = true -icon = SubResource("ImageTexture_4jujf") +icon = SubResource("ImageTexture_81s3d") [connection signal="value_changed" from="SimpleEditor/Value1Variable" to="." method="_on_value_1_variable_value_changed"] [connection signal="text_changed" from="ComplexEditor" to="." method="_on_complex_editor_text_changed"] diff --git a/addons/dialogic/Editor/Events/Fields/field_dictionary.gd b/addons/dialogic/Editor/Events/Fields/field_dictionary.gd index 194f130a5..f76ef2794 100644 --- a/addons/dialogic/Editor/Events/Fields/field_dictionary.gd +++ b/addons/dialogic/Editor/Events/Fields/field_dictionary.gd @@ -1,42 +1,40 @@ @tool extends DialogicVisualEditorField -## Event block field for editing arrays. +## Event block field for editing dictionaries. -const PairValue = "res://addons/dialogic/Editor/Events/Fields/dictionary_part.tscn" +const DictionaryValue = "res://addons/dialogic/Editor/Events/Fields/dictionary_part.tscn" -func _ready(): +func _ready() -> void: %Add.icon = get_theme_icon("Add", "EditorIcons") func _set_value(value:Variant) -> void: - for child in %Values.get_children(): - child.queue_free() + for child in get_children(): + if child != %Add: + child.queue_free() - var dict : Dictionary + var dict: Dictionary # attempt to take dictionary values, create a fresh one if not possible if typeof(value) == TYPE_DICTIONARY: dict = value elif typeof(value) == TYPE_STRING: if value.begins_with('{'): - var result = JSON.parse_string(value) + var result: Variant = JSON.parse_string(value) if result != null: dict = result as Dictionary - else: - dict = Dictionary() - else: - dict = Dictionary() var keys := dict.keys() var values := dict.values() for index in dict.size(): - var x :Node = load(PairValue).instantiate() - %Values.add_child(x) - x.set_key(keys[index]) + var x: Node = load(DictionaryValue).instantiate() + add_child(x) x.set_value(values[index]) + x.set_key(keys[index]) x.value_changed.connect(recalculate_values) + move_child(%Add, -1) func _on_value_changed(value:Variant) -> void: @@ -45,16 +43,18 @@ func _on_value_changed(value:Variant) -> void: func recalculate_values() -> void: var dict := {} - for child in %Values.get_children(): - if !child.is_queued_for_deletion(): + for child in get_children(): + if child != %Add and !child.is_queued_for_deletion(): dict[child.get_key()] = child.get_value() _on_value_changed(dict) func _on_AddButton_pressed() -> void: - var x :Control = load(PairValue).instantiate() - %Values.add_child(x) + var x: Control = load(DictionaryValue).instantiate() + add_child(x) x.set_key("") x.set_value("") x.value_changed.connect(recalculate_values) + x.focus_key() recalculate_values() + move_child(%Add, -1) diff --git a/addons/dialogic/Editor/Events/Fields/field_dictionary.tscn b/addons/dialogic/Editor/Events/Fields/field_dictionary.tscn index 3bcaa1188..c5c73e9a7 100644 --- a/addons/dialogic/Editor/Events/Fields/field_dictionary.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_dictionary.tscn @@ -1,9 +1,8 @@ -[gd_scene load_steps=5 format=3 uid="uid://c74bnmhefu72w"] +[gd_scene load_steps=4 format=3 uid="uid://c74bnmhefu72w"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_dictionary.gd" id="1_p4kmu"] -[ext_resource type="PackedScene" uid="uid://b27yweami3mxi" path="res://addons/dialogic/Editor/Events/Fields/dictionary_part.tscn" id="2_fg1gy"] -[sub_resource type="Image" id="Image_5s534"] +[sub_resource type="Image" id="Image_dcsrk"] 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", @@ -12,35 +11,18 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_bnwpy"] -image = SubResource("Image_5s534") +[sub_resource type="ImageTexture" id="ImageTexture_cpbga"] +image = SubResource("Image_dcsrk") -[node name="Pairs" type="VBoxContainer"] -script = ExtResource("1_p4kmu") - -[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 +[node name="Field_Dictionary" type="HFlowContainer"] size_flags_horizontal = 3 +script = ExtResource("1_p4kmu") -[node name="Add" type="Button" parent="Editing"] -unique_name_in_owner = true -layout_mode = 2 -icon = SubResource("ImageTexture_bnwpy") - -[node name="Values" type="VBoxContainer" parent="."] +[node name="Add" type="Button" parent="."] unique_name_in_owner = true layout_mode = 2 +tooltip_text = "Add key-value pair" +icon = SubResource("ImageTexture_cpbga") +flat = true -[node name="Value" parent="Values" instance=ExtResource("2_fg1gy")] -layout_mode = 2 - -[node name="Value2" parent="Values" instance=ExtResource("2_fg1gy")] -layout_mode = 2 - -[connection signal="pressed" from="Editing/Add" to="." method="_on_AddButton_pressed"] +[connection signal="pressed" from="Add" to="." method="_on_AddButton_pressed"] diff --git a/addons/dialogic/Editor/Events/Fields/field_file.gd b/addons/dialogic/Editor/Events/Fields/field_file.gd index 15d3dd8b9..1227dfaf6 100644 --- a/addons/dialogic/Editor/Events/Fields/field_file.gd +++ b/addons/dialogic/Editor/Events/Fields/field_file.gd @@ -8,8 +8,8 @@ extends DialogicVisualEditorField @export var file_filter := "" @export var placeholder := "" -@export var file_mode : EditorFileDialog.FileMode = EditorFileDialog.FILE_MODE_OPEN_FILE -var resource_icon:Texture: +@export var file_mode: EditorFileDialog.FileMode = EditorFileDialog.FILE_MODE_OPEN_FILE +var resource_icon: Texture: get: return resource_icon set(new_icon): @@ -21,8 +21,8 @@ var resource_icon:Texture: %Field.theme_type_variation = "LineEditWithIcon" var max_width := 200 -var current_value : String -var hide_reset:bool = false +var current_value: String +var hide_reset := false #endregion @@ -49,13 +49,15 @@ func _load_display_info(info:Dictionary) -> void: placeholder = info.get('placeholder', '') resource_icon = info.get('icon', null) await ready + if resource_icon == null and info.has('editor_icon'): resource_icon = callv('get_theme_icon', info.editor_icon) -func _set_value(value:Variant) -> void: +func _set_value(value: Variant) -> void: current_value = value - var text := value + var text: String = value + if file_mode != EditorFileDialog.FILE_MODE_OPEN_DIR: text = value.get_file() %Field.tooltip_text = value @@ -70,9 +72,11 @@ func _set_value(value:Variant) -> void: %Field.custom_minimum_size.x = 0 %Field.expand_to_text_length = true - %Field.text = text + if not %Field.text == text: + value_changed.emit(property_name, current_value) + %Field.text = text - %ClearButton.visible = !value.is_empty() and !hide_reset + %ClearButton.visible = not value.is_empty() and not hide_reset #endregion @@ -87,12 +91,12 @@ func _on_OpenButton_pressed() -> void: func _on_file_dialog_selected(path:String) -> void: _set_value(path) - emit_signal("value_changed", property_name, path) + value_changed.emit(property_name, path) func clear_path() -> void: _set_value("") - emit_signal("value_changed", property_name, "") + value_changed.emit(property_name, "") #endregion @@ -100,16 +104,22 @@ func clear_path() -> void: #region DRAG AND DROP ################################################################################ -func _can_drop_data_fw(at_position: Vector2, data: Variant) -> bool: +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 _drop_data_fw(_at_position: Vector2, data: Variant) -> void: + var file: String = data.files[0] + _on_file_dialog_selected(file) #endregion @@ -117,11 +127,13 @@ func _drop_data_fw(at_position: Vector2, data: Variant) -> void: #region VISUALS FOR FOCUS ################################################################################ -func _on_field_focus_entered(): +func _on_field_focus_entered() -> void: $FocusStyle.show() -func _on_field_focus_exited(): + +func _on_field_focus_exited() -> void: $FocusStyle.hide() - _on_file_dialog_selected(%Field.text) + var field_text: String = %Field.text + _on_file_dialog_selected(field_text) #endregion diff --git a/addons/dialogic/Editor/Events/Fields/field_file.tscn b/addons/dialogic/Editor/Events/Fields/field_file.tscn index c0e51dace..8b47de7de 100644 --- a/addons/dialogic/Editor/Events/Fields/field_file.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_file.tscn @@ -8,7 +8,7 @@ [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6b7on"] -[sub_resource type="Image" id="Image_kg01j"] +[sub_resource type="Image" id="Image_bpoe1"] 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", @@ -17,10 +17,10 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_j8aof"] -image = SubResource("Image_kg01j") +[sub_resource type="ImageTexture" id="ImageTexture_dkuon"] +image = SubResource("Image_bpoe1") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_raavq"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yv1pn"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 @@ -67,20 +67,20 @@ expand_to_text_length = true [node name="OpenButton" type="Button" parent="BG/HBox"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_j8aof") +icon = SubResource("ImageTexture_dkuon") flat = true [node name="ClearButton" type="Button" parent="BG/HBox"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_j8aof") +icon = SubResource("ImageTexture_dkuon") flat = true [node name="FocusStyle" type="Panel" parent="."] visible = false layout_mode = 2 mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_raavq") +theme_override_styles/panel = SubResource("StyleBoxFlat_yv1pn") [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"] diff --git a/addons/dialogic/Editor/Events/Fields/field_flex_value.gd b/addons/dialogic/Editor/Events/Fields/field_flex_value.gd new file mode 100644 index 000000000..be7a98d6a --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_flex_value.gd @@ -0,0 +1,154 @@ +@tool +extends HBoxContainer + +## Event block field part for a value that can change type. + +signal value_changed + +var value_field: Node +var value_type: int = -1 + +var current_value: Variant + +func _ready() -> void: + %ValueType.options = [{ + 'label': 'String', + 'icon': ["String", "EditorIcons"], + 'value': TYPE_STRING + },{ + 'label': 'Number (int)', + 'icon': ["int", "EditorIcons"], + 'value': TYPE_INT + },{ + 'label': 'Number (float)', + 'icon': ["float", "EditorIcons"], + 'value': TYPE_FLOAT + },{ + 'label': 'Boolean', + 'icon': ["bool", "EditorIcons"], + 'value': TYPE_BOOL + },{ + 'label': 'Expression', + 'icon': ["Variant", "EditorIcons"], + 'value': TYPE_MAX + } + ] + %ValueType.symbol_only = true + %ValueType.value_changed.connect(_on_type_changed.bind()) + %ValueType.tooltip_text = "Change type" + + +func set_value(value:Variant): + change_field_type(deduce_type(value)) + %ValueType.set_value(deduce_type(value)) + current_value = value + match value_type: + TYPE_BOOL: + value_field.button_pressed = value + TYPE_STRING: + value_field.text = value + TYPE_FLOAT, TYPE_INT: + value_field.set_value(value) + TYPE_MAX, _: + value_field.text = value.trim_prefix('@') + + +func deduce_type(value:Variant) -> int: + if value is String and value.begins_with('@'): + return TYPE_MAX + else: + return typeof(value) + + +func _on_type_changed(prop:String, type:Variant) -> void: + if type == value_type: + return + + match type: + TYPE_BOOL: + if typeof(current_value) == TYPE_STRING: + current_value = DialogicUtil.str_to_bool(current_value) + elif value_type == TYPE_FLOAT or value_type == TYPE_INT: + current_value = bool(current_value) + else: + current_value = true if current_value else false + set_value(current_value) + TYPE_STRING: + current_value = str(current_value).trim_prefix('@') + set_value(current_value) + TYPE_FLOAT: + current_value = float(current_value) + set_value(current_value) + TYPE_INT: + current_value = int(current_value) + set_value(current_value) + TYPE_MAX,_: + current_value = var_to_str(current_value) + set_value('@'+current_value) + + + emit_signal.call_deferred('value_changed') + + +func get_value() -> Variant: + return current_value + + +func _on_delete_pressed() -> void: + queue_free() + value_changed.emit() + + +func change_field_type(type:int) -> void: + if type == value_type: + return + + value_type = type + + if value_field: + value_field.queue_free() + match type: + TYPE_BOOL: + value_field = CheckBox.new() + value_field.toggled.connect(_on_bool_toggled) + TYPE_STRING: + value_field = LineEdit.new() + value_field.theme_type_variation = "DialogicEventEdit" + value_field.text_changed.connect(_on_str_text_changed) + value_field.expand_to_text_length = true + TYPE_FLOAT, TYPE_INT: + value_field = load("res://addons/dialogic/Editor/Events/Fields/field_number.tscn").instantiate() + if type == TYPE_FLOAT: + value_field.use_float_mode() + else: + value_field.use_int_mode() + value_field.value_changed.connect(_on_number_value_changed.bind(type == TYPE_INT)) + TYPE_MAX, _: + value_field = LineEdit.new() + value_field.expand_to_text_length = true + value_field.text_changed.connect(_on_expression_changed) + add_child(value_field) + move_child(value_field, 1) + + +func _on_bool_toggled(value:bool) -> void: + current_value = value + value_changed.emit() + + +func _on_str_text_changed(value:String) -> void: + current_value = value + value_changed.emit() + + +func _on_expression_changed(value:String) -> void: + current_value = '@'+value + value_changed.emit() + + +func _on_number_value_changed(prop:String, value:float, int := false) -> void: + if int: + current_value = int(value) + else: + current_value = value + value_changed.emit() diff --git a/addons/dialogic/Editor/Events/Fields/field_flex_value.tscn b/addons/dialogic/Editor/Events/Fields/field_flex_value.tscn new file mode 100644 index 000000000..2d461865f --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_flex_value.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=3 format=3 uid="uid://dl08ubinx6ugu"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_flex_value.gd" id="1_m5nnp"] +[ext_resource type="PackedScene" uid="uid://d3bhehatwoio" path="res://addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn" id="3_h10fc"] + +[node name="FlexValue" type="HBoxContainer"] +offset_right = 65.0 +offset_bottom = 22.0 +script = ExtResource("1_m5nnp") + +[node name="ValueType" parent="." instance=ExtResource("3_h10fc")] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Change type" +text = "" diff --git a/addons/dialogic/Editor/Events/Fields/field_number.gd b/addons/dialogic/Editor/Events/Fields/field_number.gd index ff3527d9d..c2733b9f2 100644 --- a/addons/dialogic/Editor/Events/Fields/field_number.gd +++ b/addons/dialogic/Editor/Events/Fields/field_number.gd @@ -4,12 +4,12 @@ extends DialogicVisualEditorField ## Event block field for integers and floats. Improved version of the native spinbox. -@export var allow_string : bool = false +@export var allow_string: bool = false @export var step: float = 0.1 @export var enforce_step: bool = true @export var limit_value: bool = false -@export var min: float = 0.0 -@export var max: float = 999.0 +@export var min: float = -INF +@export var max: float = INF @export var only_positive: bool = false @export var value = 0.0 @export var prefix: String = "" @@ -26,7 +26,6 @@ func _ready() -> void: update_prefix(prefix) update_suffix(suffix) - $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('panel', 'DialogicEventEdit')) func _load_display_info(info: Dictionary) -> void: @@ -161,6 +160,8 @@ func _on_decrement_button_down(button: NodePath) -> void: func _on_value_text_submitted(new_text: String, no_signal:= false) -> void: + if new_text.is_empty() and not allow_string: + new_text = "0.0" if new_text.is_valid_float(): var final_value := new_text.to_float() diff --git a/addons/dialogic/Editor/Events/Fields/field_number.tscn b/addons/dialogic/Editor/Events/Fields/field_number.tscn index bc4a8f23c..5053a87d8 100644 --- a/addons/dialogic/Editor/Events/Fields/field_number.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_number.tscn @@ -40,10 +40,10 @@ grow_horizontal = 2 grow_vertical = 2 theme_override_constants/separation = 0 script = ExtResource("1_0jdnn") -prefix = null [node name="Value_Panel" type="PanelContainer" parent="."] layout_mode = 2 +theme_type_variation = &"DialogicEventEdit" [node name="Layout" type="HBoxContainer" parent="Value_Panel"] layout_mode = 2 @@ -106,26 +106,17 @@ shortcut_keys_enabled = false drag_and_drop_selection_enabled = false text_direction = 1 -[node name="HBoxContainer" type="HBoxContainer" parent="Value_Panel/Layout"] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="Spacer" type="MarginContainer" parent="Value_Panel/Layout/HBoxContainer"] -layout_mode = 2 -mouse_filter = 2 -theme_override_constants/margin_right = 1 - -[node name="Spin" type="VBoxContainer" parent="Value_Panel/Layout/HBoxContainer"] +[node name="Spin" type="VBoxContainer" parent="Value_Panel/Layout"] unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 0 alignment = 1 -[node name="Increment" type="Button" parent="Value_Panel/Layout/HBoxContainer/Spin"] +[node name="Increment" type="Button" parent="Value_Panel/Layout/Spin"] layout_mode = 2 size_flags_vertical = 3 auto_translate = false -focus_neighbor_left = NodePath("../../../Value") +focus_neighbor_left = NodePath("../../Value") focus_neighbor_top = NodePath(".") focus_neighbor_bottom = NodePath("../Decrement") theme_override_colors/icon_hover_color = Color(0.412738, 0.550094, 0.760917, 1) @@ -139,11 +130,11 @@ icon = ExtResource("3_v5cne") flat = true vertical_icon_alignment = 2 -[node name="Decrement" type="Button" parent="Value_Panel/Layout/HBoxContainer/Spin"] +[node name="Decrement" type="Button" parent="Value_Panel/Layout/Spin"] layout_mode = 2 size_flags_vertical = 3 auto_translate = false -focus_neighbor_left = NodePath("../../../Value") +focus_neighbor_left = NodePath("../../Value") focus_neighbor_top = NodePath("../Increment") focus_neighbor_bottom = NodePath(".") theme_override_colors/icon_hover_color = Color(0.412738, 0.550094, 0.760917, 1) @@ -157,17 +148,13 @@ icon = ExtResource("4_ph52o") flat = true vertical_icon_alignment = 2 -[node name="Spacer" type="Control" parent="."] -custom_minimum_size = Vector2(3, 0) -layout_mode = 2 - [connection signal="gui_input" from="Value_Panel/Layout/Prefix" to="." method="_on_sublabel_clicked"] [connection signal="focus_entered" from="Value_Panel/Layout/Value" to="." method="_on_value_focus_entered"] [connection signal="focus_exited" from="Value_Panel/Layout/Value" to="." method="_on_value_focus_exited"] [connection signal="gui_input" from="Value_Panel/Layout/Value" to="." method="_on_gui_input"] [connection signal="text_submitted" from="Value_Panel/Layout/Value" to="." method="_on_value_text_submitted"] [connection signal="gui_input" from="Value_Panel/Layout/Suffix" to="." method="_on_sublabel_clicked"] -[connection signal="button_down" from="Value_Panel/Layout/HBoxContainer/Spin/Increment" to="." method="_on_increment_button_down" binds= [NodePath("%Spin/Increment")]] -[connection signal="gui_input" from="Value_Panel/Layout/HBoxContainer/Spin/Increment" to="." method="_on_gui_input"] -[connection signal="button_down" from="Value_Panel/Layout/HBoxContainer/Spin/Decrement" to="." method="_on_decrement_button_down" binds= [NodePath("%Spin/Decrement")]] -[connection signal="gui_input" from="Value_Panel/Layout/HBoxContainer/Spin/Decrement" to="." method="_on_gui_input"] +[connection signal="button_down" from="Value_Panel/Layout/Spin/Increment" to="." method="_on_increment_button_down" binds= [NodePath("%Spin/Increment")]] +[connection signal="gui_input" from="Value_Panel/Layout/Spin/Increment" to="." method="_on_gui_input"] +[connection signal="button_down" from="Value_Panel/Layout/Spin/Decrement" to="." method="_on_decrement_button_down" binds= [NodePath("%Spin/Decrement")]] +[connection signal="gui_input" from="Value_Panel/Layout/Spin/Decrement" to="." method="_on_gui_input"] diff --git a/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd index eb25d3987..16a315f14 100644 --- a/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd +++ b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd @@ -60,6 +60,7 @@ func _load_display_info(info:Dictionary) -> void: placeholder_text = info.get('placeholder', 'Select Resource') mode = info.get("mode", 0) resource_icon = info.get('icon', null) + %Search.tooltip_text = info.get('tooltip_text', '') await ready if resource_icon == null and info.has('editor_icon'): resource_icon = callv('get_theme_icon', info.editor_icon) @@ -75,7 +76,10 @@ func _autofocus() -> void: ################################################################################ func _ready() -> void: - %Focus.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) + var focus := get_theme_stylebox("focus", "LineEdit") + if has_theme_stylebox("focus", "DialogicEventEdit"): + focus = get_theme_stylebox('focus', 'DialogicEventEdit') + %Focus.add_theme_stylebox_override('panel', focus) %Search.text_changed.connect(_on_Search_text_changed) %Search.text_submitted.connect(_on_Search_text_entered) @@ -126,8 +130,8 @@ func _on_Search_text_changed(new_text:String, just_update:bool = false) -> void: var suggestions: Dictionary = get_suggestions_func.call(new_text) - var line_length = 0 - var idx: int = 0 + var line_length := 0 + var idx := 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(): var curr_line_length: int = 0 @@ -178,7 +182,7 @@ func _on_Search_text_changed(new_text:String, just_update:bool = false) -> void: %Suggestions.size.x = max(%PanelContainer.size.x, line_length) -func suggestion_selected(index : int, position:=Vector2(), button_index:=MOUSE_BUTTON_LEFT) -> void: +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): diff --git a/addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn index c4ce234c0..1f4e2200e 100644 --- a/addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn @@ -6,7 +6,7 @@ [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_vennf"] -[sub_resource type="Image" id="Image_jcy4w"] +[sub_resource type="Image" id="Image_5e7lo"] 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", @@ -15,10 +15,10 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_8v6yx"] -image = SubResource("Image_jcy4w") +[sub_resource type="ImageTexture" id="ImageTexture_g63da"] +image = SubResource("Image_5e7lo") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2yd2x"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g74jb"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 @@ -116,7 +116,7 @@ layout_mode = 2 focus_mode = 0 toggle_mode = true shortcut_in_tooltip = false -icon = SubResource("ImageTexture_8v6yx") +icon = SubResource("ImageTexture_g63da") flat = true [node name="Focus" type="Panel" parent="PanelContainer"] @@ -124,7 +124,7 @@ unique_name_in_owner = true visible = false layout_mode = 2 mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_2yd2x") +theme_override_styles/panel = SubResource("StyleBoxFlat_g74jb") metadata/_edit_use_anchors_ = true [connection signal="focus_entered" from="." to="." method="_on_focus_entered"] diff --git a/addons/dialogic/Editor/Events/Fields/field_options_fixed.gd b/addons/dialogic/Editor/Events/Fields/field_options_fixed.gd index f88866eda..ff404698e 100644 --- a/addons/dialogic/Editor/Events/Fields/field_options_fixed.gd +++ b/addons/dialogic/Editor/Events/Fields/field_options_fixed.gd @@ -3,10 +3,7 @@ extends DialogicVisualEditorField ## Event block field for constant options. For varying options use ComplexPicker. - -@export var placeholder_text := "Placeholder Text" - -var options : Array = [] +var options: Array = [] ## if true, only the symbol will be displayed. In the dropdown text will be visible. ## Useful for making UI simpler diff --git a/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd b/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd index 5b63d31e5..aaeb4c4d5 100644 --- a/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd +++ b/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd @@ -5,10 +5,6 @@ extends DialogicVisualEditorField @onready var code_completion_helper: Node = find_parent('EditorsManager').get_node('CodeCompletionHelper') -var previous_width := 0 -var height_recalculation_queued := false - -var line_count := 0 #region MAIN METHODS ################################################################################ @@ -16,7 +12,6 @@ var line_count := 0 func _ready() -> void: self.text_changed.connect(_on_text_changed) self.syntax_highlighter = code_completion_helper.text_syntax_highlighter - resized.connect(_resized) func _load_display_info(info:Dictionary) -> void: @@ -25,7 +20,6 @@ func _load_display_info(info:Dictionary) -> void: func _set_value(value:Variant) -> void: self.text = str(value) - queue_height_recalculation() func _autofocus() -> void: @@ -39,42 +33,6 @@ func _autofocus() -> void: func _on_text_changed(value := "") -> void: value_changed.emit(property_name, self.text) - _request_code_completion(true) - queue_height_recalculation() - - -func _resized() -> void: - if previous_width != size.x: - queue_height_recalculation() - previous_width = size.x - -#endregion - - -#region HEIGHT CALCULATION -################################################################################ - -func queue_height_recalculation(): - if !is_node_ready(): - await _ready() - await get_tree().process_frame - if !height_recalculation_queued: - height_recalculation_queued = true - recalculate_height.call_deferred() - - -## This shouldn't be necessary bug [fit_content_height] creates a crash. -## TODO Remove again once https://github.com/godotengine/godot/issues/80546 is fixed. -func recalculate_height() -> void: - height_recalculation_queued = false - var font: Font = get_theme_font("font") - var text_size = font.get_multiline_string_size(self.text+' ', HORIZONTAL_ALIGNMENT_LEFT, size.x-14, get_theme_font_size("font_size")) - custom_minimum_size.y = text_size.y+20+4*(ceil(text_size.y/get_theme_font_size("font_size"))) - self.scroll_vertical = 0 - if get_parent().get_child(get_index()).get_visible_line_count() != line_count: - if find_parent("VisualEditor"): - find_parent("VisualEditor").indent_events() - line_count = get_parent().get_child(get_index()).get_visible_line_count() #endregion diff --git a/addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn b/addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn index b73197b80..6ab8fb660 100644 --- a/addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn @@ -1,6 +1,5 @@ -[gd_scene load_steps=5 format=3 uid="uid://dyp7m2nvab1aj"] +[gd_scene load_steps=4 format=3 uid="uid://dyp7m2nvab1aj"] -[ext_resource type="StyleBox" uid="uid://cu8otiwksn8ma" path="res://addons/dialogic/Editor/Events/styles/TextBackground.tres" id="1_xq18n"] [ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd" id="2_ww6ga"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_text_multiline.gd" id="3_q7600"] @@ -12,8 +11,9 @@ offset_right = 413.0 offset_bottom = 15.0 size_flags_horizontal = 3 size_flags_vertical = 3 -theme_override_styles/normal = ExtResource("1_xq18n") +theme_type_variation = &"DialogicTextEventTextEdit" wrap_mode = 1 +scroll_fit_content_height = true syntax_highlighter = SubResource("SyntaxHighlighter_2q5dk") symbol_lookup_on_click = true delimiter_strings = Array[String]([]) diff --git a/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd b/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd index c6378eec4..765f86236 100644 --- a/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd +++ b/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd @@ -4,7 +4,7 @@ extends DialogicVisualEditorField ## Event block field for a single line of text. -var placeholder :String= "": +var placeholder := "": set(value): placeholder = value self.placeholder_text = placeholder @@ -25,7 +25,7 @@ func _set_value(value:Variant) -> void: self.text = str(value) -func _autofocus(): +func _autofocus() -> void: grab_focus() #endregion diff --git a/addons/dialogic/Editor/HomePage/home_page.gd b/addons/dialogic/Editor/HomePage/home_page.gd index c666c3282..15aff2522 100644 --- a/addons/dialogic/Editor/HomePage/home_page.gd +++ b/addons/dialogic/Editor/HomePage/home_page.gd @@ -3,7 +3,7 @@ extends DialogicEditor ## A Main page in the dialogic editor. -var tips : Array = [] +var tips: Array = [] @@ -11,7 +11,7 @@ func _get_icon() -> Texture: return load("res://addons/dialogic/Editor/Images/plugin-icon.svg") -func _ready(): +func _ready() -> void: self_modulate = get_theme_color("font_color", "Editor") self_modulate.a = 0.2 @@ -30,7 +30,7 @@ func _ready(): -func _register(): +func _register() -> void: editors_manager.register_simple_editor(self) self.alternative_text = "Welcome to dialogic!" @@ -44,7 +44,7 @@ func _open(extra_info:Variant="") -> void: tips = tips.filter(func(item): return !item.is_empty()) randomize() - var tip :String = tips[randi()%len(tips)] + 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: diff --git a/addons/dialogic/Editor/HomePage/home_page.tscn b/addons/dialogic/Editor/HomePage/home_page.tscn index a7733f26b..0272d3457 100644 --- a/addons/dialogic/Editor/HomePage/home_page.tscn +++ b/addons/dialogic/Editor/HomePage/home_page.tscn @@ -102,7 +102,7 @@ corner_radius_top_right = 5 corner_radius_bottom_right = 5 corner_radius_bottom_left = 5 -[sub_resource type="Image" id="Image_ubn0t"] +[sub_resource type="Image" id="Image_e1dkh"] 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", @@ -111,8 +111,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_jsefb"] -image = SubResource("Image_ubn0t") +[sub_resource type="ImageTexture" id="ImageTexture_sr7s6"] +image = SubResource("Image_e1dkh") [node name="HomePage" type="TextureRect"] self_modulate = Color(0, 0, 0, 0.2) @@ -234,7 +234,7 @@ modulate = Color(1, 1, 1, 0.501961) layout_mode = 2 size_flags_vertical = 8 theme_override_font_sizes/font_size = 10 -text = "2.0-Alpha-13 (Godot 4.2+)" +text = "2.0-Alpha-15 WIP (Godot 4.2+)" horizontal_alignment = 2 [node name="ScrollContainer" type="ScrollContainer" parent="CenterContainer/HomePageBox/BottomPanel"] @@ -305,7 +305,7 @@ layout_mode = 2 theme_type_variation = &"DialogicLink" text = " Donate" underline = 2 -uri = "https://www.patreon.com/coppolaemilio" +uri = "https://www.patreon.com/JowanSpooner" [node name="CenterContainer2" type="VBoxContainer" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer"] layout_mode = 2 @@ -369,5 +369,5 @@ 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_jsefb") +icon = SubResource("ImageTexture_sr7s6") expand_icon = true diff --git a/addons/dialogic/Editor/HomePage/tips.txt b/addons/dialogic/Editor/HomePage/tips.txt index c8b6f07be..4ca5ec764 100644 --- a/addons/dialogic/Editor/HomePage/tips.txt +++ b/addons/dialogic/Editor/HomePage/tips.txt @@ -4,8 +4,8 @@ If there are events you never need, you can hide them from the list in the edito 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->Text -You can use scenes as portraits! This gives you basically limiteless freedom.; https://dialogic-docs.coppolaemilio.com/custom-portraits.html +There are a number of cool text effects like [pause=x], [speed=x] and [portrait=x]. Try them out!; +You can use scenes as portraits! This gives you basically limitless freedom.; https://dialogic-docs.coppolaemilio.com/custom-portraits.html 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/Pieces/close-icon.svg b/addons/dialogic/Editor/Images/Pieces/close-icon.svg new file mode 100644 index 000000000..3e3f7af3b --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/close-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/dialogic/Editor/Images/Pieces/close-icon.svg.import b/addons/dialogic/Editor/Images/Pieces/close-icon.svg.import new file mode 100644 index 000000000..cbc36f218 --- /dev/null +++ b/addons/dialogic/Editor/Images/Pieces/close-icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bff65e82555qr" +path="res://.godot/imported/close-icon.svg-c630c93ada599b08938f4854f5376f2f.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Pieces/close-icon.svg" +dest_files=["res://.godot/imported/close-icon.svg-c630c93ada599b08938f4854f5376f2f.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/Inspector/inspector_plugin.gd b/addons/dialogic/Editor/Inspector/inspector_plugin.gd new file mode 100644 index 000000000..0a40bc388 --- /dev/null +++ b/addons/dialogic/Editor/Inspector/inspector_plugin.gd @@ -0,0 +1,15 @@ +@tool +extends EditorInspectorPlugin + + +func _can_handle(object: Object) -> bool: + return true + + +func _parse_property(object: Object, type: Variant.Type, name: String, hint_type: PropertyHint, hint_string: String, usage_flags: int, wide: bool) -> bool: + if type == TYPE_OBJECT and hint_type == PROPERTY_HINT_RESOURCE_TYPE: + if hint_string == "DialogicTimeline": + var editor: EditorProperty = load("res://addons/dialogic/Editor/Inspector/timeline_inspector_field.gd").new() + add_property_editor(name, editor) + return true + return false diff --git a/addons/dialogic/Editor/Inspector/timeline_inspector_field.gd b/addons/dialogic/Editor/Inspector/timeline_inspector_field.gd new file mode 100644 index 000000000..6e617fead --- /dev/null +++ b/addons/dialogic/Editor/Inspector/timeline_inspector_field.gd @@ -0,0 +1,82 @@ +@tool +extends EditorProperty + +var field: Control = null +var button: Button = null +# An internal value of the property. +var current_value: DialogicTimeline = null +# A guard against internal changes when the property is updated. +var updating = false + + +func _init() -> void: + var hbox := HBoxContainer.new() + add_child(hbox) + + field = load("res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn").instantiate() + hbox.add_child(field) + field.placeholder_text = "No Timeline" + field.size_flags_horizontal = Control.SIZE_EXPAND_FILL + field.size_flags_vertical = Control.SIZE_SHRINK_CENTER + field.mode = field.Modes.IDENTIFIER + field.fit_text_length = false + field.valid_file_drop_extension = ".dtl" + field.value_changed.connect(_on_field_value_changed) + field.get_suggestions_func = get_timeline_suggestions + + button = Button.new() + hbox.add_child(button) + button.hide() + button.pressed.connect(_on_button_pressed, CONNECT_DEFERRED) + + +func _on_field_value_changed(property:String, value:Variant) -> void: + # Ignore the signal if the property is currently being updated. + if updating: + return + + var new_value: DialogicTimeline = null + if value: + new_value = DialogicResourceUtil.get_timeline_resource(value) + + if current_value != new_value: + current_value = new_value + if current_value: + button.show() + else: + button.hide() + emit_changed(get_edited_property(), current_value) + + +func _update_property() -> void: + field.resource_icon = get_theme_icon("TripleBar", "EditorIcons") + button.icon = get_theme_icon("ExternalLink", "EditorIcons") + + # Read the current value from the property. + var new_value = get_edited_object()[get_edited_property()] + if (new_value == current_value): + return + + # Update the control with the new value. + updating = true + current_value = new_value + if current_value: + field.set_value(DialogicResourceUtil.get_unique_identifier(current_value.resource_path)) + button.show() + else: + button.hide() + field.set_value("") + updating = false + + +func get_timeline_suggestions(filter:String) -> Dictionary: + var suggestions := {} + var timeline_directory := DialogicResourceUtil.get_timeline_directory() + for identifier in timeline_directory.keys(): + suggestions[identifier] = {'value': identifier, 'tooltip':timeline_directory[identifier], 'editor_icon': ["TripleBar", "EditorIcons"]} + return suggestions + + +func _on_button_pressed() -> void: + if current_value: + EditorInterface.edit_resource(current_value) diff --git a/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd b/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd index 1b51f30d4..f5f11e3a6 100644 --- a/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd +++ b/addons/dialogic/Editor/Settings/HintLabelStylingScript.gd @@ -2,7 +2,7 @@ extends Label # Called when the node enters the scene tree for the first time. -func _ready(): +func _ready() -> void: # don't load the label settings when opening as a scene # prevents HUGE diffs if owner.get_parent() is SubViewport: diff --git a/addons/dialogic/Editor/Settings/csv_file.gd b/addons/dialogic/Editor/Settings/csv_file.gd index 28db80e5b..3f8fb3a98 100644 --- a/addons/dialogic/Editor/Settings/csv_file.gd +++ b/addons/dialogic/Editor/Settings/csv_file.gd @@ -55,7 +55,7 @@ func _init(file_path: String, original_locale: String, separator_enabled: bool) var locale_array_line := PackedStringArray(["keys", original_locale]) lines.append(locale_array_line) - if not FileAccess.file_exists(file_path): + if not ResourceLoader.exists(file_path): is_new_file = true # The "keys" and original locale are the only columns in a new file. diff --git a/addons/dialogic/Editor/Settings/settings_editor.gd b/addons/dialogic/Editor/Settings/settings_editor.gd index 7c51b4d8d..3d87c4abc 100644 --- a/addons/dialogic/Editor/Settings/settings_editor.gd +++ b/addons/dialogic/Editor/Settings/settings_editor.gd @@ -4,7 +4,7 @@ extends DialogicEditor ## Editor that contains all settings var button_group := ButtonGroup.new() -var registered_sections :Array[DialogicSettingsPage] = [] +var registered_sections: Array[DialogicSettingsPage] = [] func _get_title() -> String: @@ -15,12 +15,12 @@ func _get_icon() -> Texture: return get_theme_icon("PluginScript", "EditorIcons") -func _register(): +func _register() -> void: editors_manager.register_simple_editor(self) self.alternative_text = "Customize dialogic and it's behaviour" -func _ready(): +func _ready() -> void: if get_parent() is SubViewport: return @@ -38,7 +38,7 @@ func _ready(): func register_settings_section(path:String) -> void: - var section :Control = load(path).instantiate() + var section: Control = load(path).instantiate() registered_sections.append(section) @@ -71,7 +71,7 @@ func add_registered_sections() -> void: if !section.short_info.is_empty(): - var tooltip_hint :Control = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate() + 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) @@ -91,7 +91,7 @@ func add_registered_sections() -> void: inner_vbox.add_child(panel) - var info_section :Control = section._get_info_section() + 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 @@ -156,13 +156,13 @@ func _open(extra_information:Variant = null) -> void: open_tab(%SettingsContent.get_node(extra_information)) -func _close(): +func _close() -> void: for child in %SettingsContent.get_children(): if child.get_meta('section').has_method('_about_to_close'): child.get_meta('section')._about_to_close() -func refresh(): +func refresh() -> void: 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_general.gd b/addons/dialogic/Editor/Settings/settings_general.gd index 172095862..07500c4e1 100644 --- a/addons/dialogic/Editor/Settings/settings_general.gd +++ b/addons/dialogic/Editor/Settings/settings_general.gd @@ -41,11 +41,11 @@ func _refresh() -> void: %SectionList.create_item() var cached_events := DialogicResourceUtil.get_event_cache() var sections := [] - var section_order :Array = DialogicUtil.get_editor_setting('event_section_order', ['Main', 'Logic', 'Timeline', 'Audio', 'Godot','Other', 'Helper']) + var section_order: Array = DialogicUtil.get_editor_setting('event_section_order', ['Main', 'Logic', 'Flow', 'Audio', 'Visuals','Other', 'Helper']) for ev in cached_events: if !ev.event_category in sections: sections.append(ev.event_category) - var item :TreeItem = %SectionList.create_item(null) + 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")) @@ -86,10 +86,9 @@ 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 + button.custom_minimum_size = Vector2(50 ,50) * DialogicUtil.get_editor_scale() %Colors.add_child(button) button.color = DialogicUtil.get_color(color) button.color_changed.connect(_on_color_change) @@ -141,13 +140,13 @@ 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') + 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 mode: int = %ExtensionMode.selected - var file : FileAccess + 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: diff --git a/addons/dialogic/Editor/Settings/settings_modules.gd b/addons/dialogic/Editor/Settings/settings_modules.gd index a870a6923..008453e8c 100644 --- a/addons/dialogic/Editor/Settings/settings_modules.gd +++ b/addons/dialogic/Editor/Settings/settings_modules.gd @@ -37,6 +37,8 @@ func _refresh() -> void: func _on_refresh_pressed() -> void: + DialogicUtil.get_indexers(true, true) + DialogicResourceUtil.update_event_cache() load_modules_tree() @@ -61,7 +63,7 @@ func _on_search_text_changed(new_text:String) -> void: filter.text = "" filter.set_meta("counter", 0) - var hidden_events :Array= DialogicUtil.get_editor_setting('hidden_event_buttons', []) + 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(): @@ -86,7 +88,7 @@ func _on_search_text_changed(new_text:String) -> void: if sub_child.visible: child.add_button(0, sub_child.get_icon(0), counter, false, sub_child.get_text(0)) if sub_child.get_metadata(0) and sub_child.get_metadata(0)['type'] == 'Event' and sub_child.get_metadata(0)['hidden']: - var color : Color = sub_child.get_icon_modulate(0) + var color: Color = sub_child.get_icon_modulate(0) color.a = 0.5 child.set_button_color(0, counter, color) else: @@ -99,26 +101,26 @@ func _on_search_text_changed(new_text:String) -> void: func load_modules_tree() -> void: %Tree.clear() - var root :TreeItem = %Tree.create_item() + var root: TreeItem = %Tree.create_item() var cached_events := DialogicResourceUtil.get_event_cache() var hidden_events: Array = DialogicUtil.get_editor_setting('hidden_event_buttons', []) var indexers := DialogicUtil.get_indexers() for i in indexers: - var module_item :TreeItem = %Tree.create_item(root) + var module_item: TreeItem = %Tree.create_item(root) module_item.set_text(0, i.get_script().resource_path.trim_suffix('/index.gd').get_file()) module_item.set_metadata(0, {'type':'Module'}) # Events for ev in i._get_events(): - if not FileAccess.file_exists(ev): + if not ResourceLoader.exists(ev): continue - var event_item : TreeItem = %Tree.create_item(module_item) + 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 + 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: @@ -128,7 +130,7 @@ func load_modules_tree() -> void: # Subsystems for subsys in i._get_subsystems(): - var subsys_item : TreeItem = %Tree.create_item(module_item) + 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")) @@ -138,7 +140,7 @@ func load_modules_tree() -> void: # Style scenes for style in i._get_layout_parts(): - var style_item : TreeItem = %Tree.create_item(module_item) + 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")) @@ -148,7 +150,7 @@ func load_modules_tree() -> void: # Text Effects for effect in i._get_text_effects(): - var effect_item : TreeItem = %Tree.create_item(module_item) + 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")) @@ -158,7 +160,7 @@ func load_modules_tree() -> void: # Text Modifiers for mod in i._get_text_modifiers(): - var mod_item : TreeItem = %Tree.create_item(module_item) + 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")) @@ -168,7 +170,7 @@ func load_modules_tree() -> void: # Settings for settings in i._get_settings_pages(): - var settings_item : TreeItem = %Tree.create_item(module_item) + 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")) @@ -178,7 +180,7 @@ func load_modules_tree() -> void: # Editors for editor in i._get_editors(): - var editor_item : TreeItem = %Tree.create_item(module_item) + 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")) @@ -200,7 +202,7 @@ func _on_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_ind 'Event': # Visibility item clicked if id == 0: - var meta :Dictionary= item.get_metadata(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)) @@ -208,7 +210,7 @@ func _on_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_ind %VisibilityToggle.button_pressed = true else: item.set_button(0, 0, get_theme_icon("GuiVisibilityHidden", "EditorIcons")) - var color : Color = item.get_icon_modulate(0) + 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(): @@ -219,9 +221,9 @@ func _on_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_button_ind func _on_tree_item_selected() -> void: - var selected_item :TreeItem = %Tree.get_selected() + var selected_item: TreeItem = %Tree.get_selected() - var metadata :Variant = selected_item.get_metadata(0) + var metadata: Variant = selected_item.get_metadata(0) %Title.text = selected_item.get_text(0) %EventDefaultsPanel.hide() @@ -272,14 +274,14 @@ func _on_tree_item_selected() -> void: %GeneralInfo.text = "" -func _on_external_link_pressed(): +func _on_external_link_pressed() -> void: if %ExternalLink.has_meta('url'): OS.shell_open(%ExternalLink.get_meta('url')) func change_event_visibility(event:DialogicEvent, visibility:bool) -> void: if event: - var list :Array= DialogicUtil.get_editor_setting('hidden_event_buttons', []) + var list: Array= DialogicUtil.get_editor_setting('hidden_event_buttons', []) if visibility: list.erase(event.event_name) else: @@ -298,7 +300,7 @@ func _on_visibility_toggle_toggled(button_pressed:bool) -> void: 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) + 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) @@ -314,21 +316,27 @@ 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 event_default_overrides: Dictionary = ProjectSettings.get_setting('dialogic/event_default_overrides', {}) var params := event.get_shortcode_parameters() for prop in params: + var current_value: Variant = params[prop].default + if event_default_overrides.get(event.event_name, {}).has(params[prop].property): + current_value = event_default_overrides.get(event.event_name, {}).get(params[prop].property) + # Label var label := Label.new() label.text = prop.capitalize() %EventDefaults.add_child(label) - # 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) + var reset := Button.new() + reset.icon = get_theme_icon("Clear", "EditorIcons") + reset.flat = true + + %EventDefaults.add_child(reset) + # Editing field + var editor_node: Node = null match typeof(event.get(params[prop].property)): TYPE_STRING: editor_node = LineEdit.new() @@ -356,7 +364,7 @@ func load_event_settings(event:DialogicEvent) -> void: 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 = load("res://addons/dialogic/Editor/Events/Fields/field_vector2.tscn").instantiate() editor_node.set_value(current_value) editor_node.property_name = params[prop].property editor_node.value_changed.connect(_on_event_default_value_changed) @@ -367,17 +375,23 @@ func load_event_settings(event:DialogicEvent) -> void: 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 = load("res://addons/dialogic/Editor/Events/Fields/field_array.tscn").instantiate() editor_node.set_value(current_value) editor_node.property_name = params[prop].property editor_node.value_changed.connect(_on_event_default_value_changed) + TYPE_DICTIONARY: + editor_node = load("res://addons/dialogic/Editor/Events/Fields/field_dictionary.tscn").instantiate() + editor_node.set_value(current_value) + editor_node.property_name = params[prop].property + editor_node.value_changed.connect(_on_event_default_value_changed) %EventDefaults.add_child(editor_node) + reset.pressed.connect(reset_event_default_override.bind(prop, editor_node, params[prop].default)) func set_event_default_override(prop:String, value:Variant) -> void: - var event_default_overrides :Dictionary = ProjectSettings.get_setting('dialogic/event_default_overrides', {}) - var event :DialogicEvent = %Tree.get_selected().get_metadata(0).event + 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] = {} @@ -387,6 +401,29 @@ func set_event_default_override(prop:String, value:Variant) -> void: ProjectSettings.set_setting('dialogic/event_default_overrides', event_default_overrides) +func reset_event_default_override(prop:String, node:Node, default:Variant) -> void: + var event_default_overrides: Dictionary = ProjectSettings.get_setting('dialogic/event_default_overrides', {}) + var event: DialogicEvent = %Tree.get_selected().get_metadata(0).event + + if not event_default_overrides.has(event.event_name): + return + + event_default_overrides[event.event_name].erase(prop) + + ProjectSettings.set_setting('dialogic/event_default_overrides', event_default_overrides) + + if node is CheckBox: + node.button_pressed = default + elif node is LineEdit: + node.text = default + elif node.has_method('set_value'): + node.set_value(default) + elif node is ColorPickerButton: + node.color = default + elif node is OptionButton: + node.select(default) + elif node is SpinBox: + node.value = default func _on_event_default_string_submitted(text:String, prop:String) -> void: @@ -398,7 +435,7 @@ func _on_event_default_option_selected(index:int, option_button:OptionButton, pr 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: +func _on_event_default_value_changed(prop:String, value:Variant) -> void: set_event_default_override(prop, value) func _on_event_default_bool_toggled(value:bool, prop:String) -> void: diff --git a/addons/dialogic/Editor/Settings/settings_modules.tscn b/addons/dialogic/Editor/Settings/settings_modules.tscn index bd5bf0ae2..e43a96ae7 100644 --- a/addons/dialogic/Editor/Settings/settings_modules.tscn +++ b/addons/dialogic/Editor/Settings/settings_modules.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_modules.gd" id="1_l2hk0"] -[sub_resource type="Image" id="Image_pu0o6"] +[sub_resource type="Image" id="Image_570p8"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -12,9 +12,9 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_lce2m"] -image = SubResource("Image_pu0o6") +image = SubResource("Image_570p8") -[sub_resource type="Image" id="Image_g84xy"] +[sub_resource type="Image" id="Image_ihhvm"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -24,7 +24,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_137g7"] -image = SubResource("Image_g84xy") +image = SubResource("Image_ihhvm") [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_315cl"] content_margin_left = 4.0 @@ -213,7 +213,7 @@ text = "Edit event defaults:" [node name="EventDefaults" type="GridContainer" parent="Scroll/Settings/EventDefaultsPanel/VBox"] unique_name_in_owner = true layout_mode = 2 -columns = 2 +columns = 3 [node name="GeneralInfo" type="Label" parent="Scroll/Settings"] unique_name_in_owner = true diff --git a/addons/dialogic/Editor/Settings/settings_translation.gd b/addons/dialogic/Editor/Settings/settings_translation.gd index 5e40a6585..fe0714edd 100644 --- a/addons/dialogic/Editor/Settings/settings_translation.gd +++ b/addons/dialogic/Editor/Settings/settings_translation.gd @@ -8,7 +8,7 @@ enum TranslationModes {PER_PROJECT, PER_TIMELINE, NONE} enum SaveLocationModes {INSIDE_TRANSLATION_FOLDER, NEXT_TO_TIMELINE, NONE} var loading := false -@onready var settings_editor :Control = find_parent('Settings') +@onready var settings_editor: Control = find_parent('Settings') ## The default CSV filename that contains the translations for character ## properties. @@ -185,7 +185,6 @@ func _handle_glossary_translation( translation_mode: TranslationModes, translation_folder_path: String, orig_locale: String) -> void: - var glossary_csv_path := "" var glossary_csv: DialogicCsvFile = null var glossary_paths: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) @@ -207,6 +206,7 @@ func _handle_glossary_translation( var file_name := path_parts[-1] csv_name = "dialogic_" + file_name + '_translation.csv' + var glossary_csv_path := "" # Get glossary CSV file path. match save_location_mode: SaveLocationModes.INSIDE_TRANSLATION_FOLDER: @@ -228,13 +228,10 @@ func _handle_glossary_translation( glossary_csv.add_translation_keys_to_glossary(glossary) ResourceSaver.save(glossary) - match translation_mode: - TranslationModes.PER_PROJECT: - pass - - TranslationModes.PER_TIMELINE: - glossary_csv.update_csv_file_on_disk() - glossary_csv = null + #If per-file mode is used, save this csv and begin a new one + if translation_mode == TranslationModes.PER_TIMELINE: + glossary_csv.update_csv_file_on_disk() + glossary_csv = null # If a Per-Project glossary is still open, we need to save it. if glossary_csv != null: @@ -380,7 +377,7 @@ func update_csv_files() -> void: %StatusMessage.text = status_message.format(status_message_args) ProjectSettings.set_setting(_USED_LOCALES_SETTING, _unique_locales) - get_locales("") + ## Iterates over all character resource files and creates or updates CSV files ## that contain the translations for character properties. @@ -452,7 +449,7 @@ func collect_translations() -> void: for file_path: String in translation_files: # If the file path is not valid, we must clean it up. - if FileAccess.file_exists(file_path): + if ResourceLoader.exists(file_path): found_file_paths.append(file_path) else: removed_translation_files += 1 @@ -460,10 +457,10 @@ func collect_translations() -> void: if not file_path in all_translation_files: all_translation_files.append(file_path) - var path_without_suffix := file_path.trim_suffix('.translation') - var path_parts := path_without_suffix.split(".") - var locale_part := path_parts[-1] - _collect_locale(locale_part) + + var path_without_suffix := file_path.trim_suffix('.translation') + var locale_part := path_without_suffix.split(".")[-1] + _collect_locale(locale_part) var valid_translation_files := PackedStringArray(all_translation_files) @@ -517,7 +514,6 @@ func erase_translations() -> void: var files: PackedStringArray = ProjectSettings.get_setting('internationalization/locale/translations', []) var translation_files := Array(files) ProjectSettings.set_setting(_USED_LOCALES_SETTING, []) - get_locales("") var deleted_csv_files := 0 var deleted_translation_files := 0 @@ -661,4 +657,4 @@ func _collect_locale(locale: String) -> void: if _unique_locales.has(locale): return - _unique_locales.append(locale) \ No newline at end of file + _unique_locales.append(locale) diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd index 7d3b92a2d..8a12cd5e3 100644 --- a/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd @@ -15,17 +15,20 @@ var completion_word_regex := RegEx.new() 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() +# To find the value of a paramater that is being typed +var completion_shortcode_value_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 +var text_event: DialogicTextEvent = null -func _ready(): +func _ready() -> void: # 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)) + completion_shortcode_value_regex.compile(r'(\[|\s)[^\[\s=]*="(?[^"$]*)'+String.chr(0xFFFF)) text_syntax_highlighter.mode = text_syntax_highlighter.Modes.TEXT_EVENT_ONLY @@ -42,6 +45,11 @@ 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 currently typed parameter +func get_code_completion_parameter_value(text:CodeEdit) -> String: + var result := completion_shortcode_value_regex.search(get_code_completion_line(text)) + return result.get_string('value') if result else "" + # Helper that gets the symbol before the current word func get_code_completion_prev_symbol(text:CodeEdit) -> String: @@ -98,8 +106,8 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG # 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. + # The value key is used to store a potential closing string for the completion. + # The completion will check if the string is already present and add it otherwise. # Shortcode event suggestions if mode == Modes.FULL_HIGHLIGHTING and syntax_highlighter.line_is_shortcode_event(text.get_caret_line()): @@ -134,7 +142,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG text.add_code_completion_option(CodeEdit.KIND_MEMBER, param, param+'="' , shortcode_events[code].event_color.lerp(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) == '"': + elif symbol == '=' or symbol == '"': var current_parameter_gex := completion_shortcode_param_getter_regex.search(line) if !current_parameter_gex: text.update_code_completion_options(false) @@ -148,17 +156,12 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG if typeof(shortcode_events[code].get_shortcode_parameters()[current_parameter].default) == TYPE_BOOL: suggest_bool(text, shortcode_events[code].event_color.lerp(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(syntax_highlighter.normal_color, 0.3), text.get_theme_icon("GuiScrollArrowRight", "EditorIcons"), '" ') + text.add_code_completion_option(CodeEdit.KIND_VARIABLE, word, word, shortcode_events[code].event_color.lerp(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(): - if suggestions[key].has('text_alt'): - text.add_code_completion_option(CodeEdit.KIND_MEMBER, key, suggestions[key].text_alt[0], shortcode_events[code].event_color.lerp(syntax_highlighter.normal_color, 0.3), suggestions[key].get('icon', null), '" ') - else: - text.add_code_completion_option(CodeEdit.KIND_MEMBER, key, str(suggestions[key].value), shortcode_events[code].event_color.lerp(syntax_highlighter.normal_color, 0.3), suggestions[key].get('icon', null), '" ') - + var suggestions: Dictionary = shortcode_events[code].get_shortcode_parameters()[current_parameter]['suggestions'].call() + suggest_custom_suggestions(suggestions, text, shortcode_events[code].event_color.lerp(syntax_highlighter.normal_color, 0.3)) # Force update and showing of the popup text.update_code_completion_options(true) @@ -184,7 +187,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG # Helper that adds all characters as options func suggest_characters(text:CodeEdit, type := CodeEdit.KIND_MEMBER, text_event_start:=false) -> void: for character in DialogicResourceUtil.get_character_directory(): - var result :String = character + var result: String = character if " " in character: result = '"'+character+'"' if text_event_start and load(DialogicResourceUtil.get_character_directory()[character]).portraits.is_empty(): @@ -223,8 +226,16 @@ func suggest_variables(text:CodeEdit): # 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"), '" ') + text.add_code_completion_option(CodeEdit.KIND_VARIABLE, 'true', 'true', color, text.get_theme_icon("GuiChecked", "EditorIcons"), '" ') + text.add_code_completion_option(CodeEdit.KIND_VARIABLE, 'false', 'false', color, text.get_theme_icon("GuiUnchecked", "EditorIcons"), '" ') + + +func suggest_custom_suggestions(suggestions:Dictionary, text:CodeEdit, color:Color) -> void: + for key in suggestions.keys(): + if suggestions[key].has('text_alt'): + text.add_code_completion_option(CodeEdit.KIND_VARIABLE, key, suggestions[key].text_alt[0], color, suggestions[key].get('icon', null), '" ') + else: + text.add_code_completion_option(CodeEdit.KIND_VARIABLE, key, str(suggestions[key].value), color, suggestions[key].get('icon', null), '" ') # Filters the list of all possible options, depending on what was typed @@ -239,6 +250,10 @@ func filter_code_completion_candidates(candidates:Array, text:CodeEdit) -> Array 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_VARIABLE: + var current_param_value := get_code_completion_parameter_value(text) + if current_param_value.is_empty() or current_param_value.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) @@ -252,17 +267,32 @@ func filter_code_completion_candidates(candidates:Array, text:CodeEdit) -> Array # 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()) + + var word := get_code_completion_word(text) + if code_completion.kind == CodeEdit.KIND_VARIABLE: + word = get_code_completion_parameter_value(text) + 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)# + + # Something has changed between 4.2 and 4.3 + # Probably about how carets are reset when text is removed or idk. + # To keep compatibility with 4.2 for at least a while this should do the trick: + # TODO: Remove once compatibility for 4.2 is dropped. + if Engine.get_version_info().hex >= 0x040300: + text.set_caret_column(text.get_caret_column()) + else: + 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: + var next_letter := text.get_line(text.get_caret_line()).substr(text.get_caret_column(), len(code_completion['default_value'])) + if next_letter == code_completion['default_value'] or next_letter[0] == code_completion['default_value'][0]: text.set_caret_column(text.get_caret_column()+1) + else: + text.insert_text_at_caret(code_completion['default_value']) #endregion diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd index 56f6b83bd..400dbe236 100644 --- a/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd @@ -10,39 +10,44 @@ 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*"(?([^=]|\\\\=)*)(?\w*)(?[^\]]*)?") +var shortcode_param_regex := RegEx.create_from_string(r'((?[^\s=]*)\s*=\s*"(?([^=]|\\=)*)(? void: + update_colors() + DialogicUtil.get_dialogic_plugin().get_editor_interface().get_base_control().theme_changed.connect(update_colors) - code_flow_color = editor_settings.get("text_editor/theme/highlighting/control_flow_keyword_color") - boolean_operator_color = code_flow_color.lightened(0.5) - variable_color = editor_settings.get('text_editor/theme/highlighting/engine_type_color') - string_color = editor_settings.get('text_editor/theme/highlighting/string_color') - character_name_color = editor_settings.get('text_editor/theme/highlighting/symbol_color').lerp(normal_color, 0.3) - character_portrait_color = character_name_color.lerp(normal_color, 0.5) + +func update_colors() -> void: + if not DialogicUtil.get_dialogic_plugin(): + return + var editor_settings: EditorSettings = DialogicUtil.get_dialogic_plugin().get_editor_interface().get_editor_settings() + normal_color = editor_settings.get('text_editor/theme/highlighting/text_color') + translation_id_color = editor_settings.get('text_editor/theme/highlighting/comment_color') + + code_flow_color = editor_settings.get("text_editor/theme/highlighting/control_flow_keyword_color") + boolean_operator_color = code_flow_color.lightened(0.5) + variable_color = editor_settings.get('text_editor/theme/highlighting/engine_type_color') + string_color = editor_settings.get('text_editor/theme/highlighting/string_color') + character_name_color = editor_settings.get('text_editor/theme/highlighting/symbol_color').lerp(normal_color, 0.3) + character_portrait_color = character_name_color.lerp(normal_color, 0.5) func _get_line_syntax_highlighting(line:int) -> Dictionary: @@ -176,7 +181,7 @@ func color_region(dict:Dictionary, color:Color, line:String, start:String, end:S if end.is_empty(): region_regex.compile("(?[^\n]+)') -func _ready(): +func _ready() -> void: await find_parent('EditorView').ready syntax_highlighter = code_completion_helper.syntax_highlighter timeline_editor.editors_manager.sidebar.content_item_activated.connect(_on_content_item_clicked) -func _on_text_editor_text_changed(): + +func _on_text_editor_text_changed() -> void: timeline_editor.current_resource_state = DialogicEditor.ResourceStates.UNSAVED request_code_completion(true) $UpdateTimer.start() -func clear_timeline(): +func clear_timeline() -> void: text = '' update_content_list() @@ -36,11 +37,11 @@ func load_timeline(timeline:DialogicTimeline) -> void: update_content_list() -func save_timeline(): +func save_timeline() -> void: if !timeline_editor.current_resource: return - var text_array:Array = text_timeline_to_array(text) + var text_array: Array = text_timeline_to_array(text) timeline_editor.current_resource.events = text_array timeline_editor.current_resource.events_processed = false @@ -60,8 +61,8 @@ func text_timeline_to_array(text:String) -> Array: while idx < len(lines)-1: idx += 1 - var line :String = lines[idx] - var line_stripped :String = line.strip_edges(true, true) + var line: String = lines[idx] + var line_stripped: String = line.strip_edges(true, true) events.append(line) return events @@ -90,6 +91,9 @@ func _gui_input(event): # Toggle the selected lines as comments func toggle_comment() -> void: var cursor: Vector2 = Vector2(get_caret_column(), get_caret_line()) + var selection := Rect2i( + Vector2i(get_selection_origin_line(), get_selection_origin_column()), + Vector2i(get_caret_line(), get_caret_column())) var from: int = cursor.y var to: int = cursor.y if has_selection(): @@ -97,14 +101,27 @@ func toggle_comment() -> void: to = get_selection_to_line() var lines: PackedStringArray = text.split("\n") - var will_comment: bool = not lines[from].begins_with("# ") + var will_comment: bool = false + for i in range(from, to+1): + if not lines[i].begins_with("#"): + will_comment = true + for i in range(from, to + 1): - lines[i] = "# " + lines[i] if will_comment else lines[i].substr(2) + if will_comment: + lines[i] = "#" + lines[i] + else: + lines[i] = lines[i].trim_prefix("#") text = "\n".join(lines) - select(from, 0, to, get_line_width(to)) - set_caret_line(cursor.y) - set_caret_column(cursor.x) + if will_comment: + cursor.x += 1 + selection.position.y += 1 + selection.size.y += 1 + else: + cursor.x -= 1 + selection.position.y -= 1 + selection.size.y -= 1 + select(selection.position.x, selection.position.y, selection.size.x, selection.size.y) text_changed.emit() @@ -182,12 +199,12 @@ func _drop_data(at_position:Vector2, data:Variant) -> void: insert_text_at_caret(result) -func _on_update_timer_timeout(): +func _on_update_timer_timeout() -> void: update_content_list() -func update_content_list(): - var labels :PackedStringArray = [] +func update_content_list() -> void: + 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) @@ -208,6 +225,52 @@ func _on_content_item_clicked(label:String) -> void: return +func _search_timeline(search_text:String) -> bool: + set_search_text(search_text) + queue_redraw() + set_meta("current_search", search_text) + + return search(search_text, 0, 0, 0).y != -1 + + +func _search_navigate_down() -> void: + search_navigate(false) + + +func _search_navigate_up() -> void: + search_navigate(true) + + +func search_navigate(navigate_up := false) -> void: + if not has_meta("current_search"): + return + var pos: Vector2i + var search_from_line := 0 + var search_from_column := 0 + if has_selection(): + if navigate_up: + search_from_line = get_selection_from_line() + search_from_column = get_selection_from_column()-1 + if search_from_column == -1: + if search_from_line == 0: + search_from_line = get_line_count() + else: + search_from_line -= 1 + search_from_column = max(get_line(search_from_line).length()-1,0) + else: + search_from_line = get_selection_to_line() + search_from_column = get_selection_to_column() + else: + search_from_line = get_caret_line() + search_from_column = get_caret_column() + + pos = search(get_meta("current_search"), 4 if navigate_up else 0, search_from_line, search_from_column) + select(pos.y, pos.x, pos.y, pos.x+len(get_meta("current_search"))) + set_caret_line(pos.y) + center_viewport_to_caret() + queue_redraw() + + ################################################################################ ## AUTO COMPLETION ################################################################################ diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd index db48ec1d0..7318bd4e4 100644 --- a/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.gd @@ -1,36 +1,35 @@ @tool extends Button -@export var visible_name:String = "" -@export var event_id:String = '' -@export var event_icon:Texture : +@export var visible_name := "" +@export var event_id := "" +@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 = '' +@export var event_sorting_index: int = 0 +@export var resource: DialogicEvent +@export var dialogic_color_name := "" 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() - + + 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 + var nstyle: StyleBoxFlat = get_parent().get_theme_stylebox('normal', 'Button').duplicate() + nstyle.border_width_left = 5 * DialogicUtil.get_editor_scale() add_theme_stylebox_override('normal', nstyle) - var hstyle :StyleBoxFlat= get_parent().get_theme_stylebox('hover', 'Button').duplicate() - hstyle.border_width_left = 5 *scale + var hstyle: StyleBoxFlat = get_parent().get_theme_stylebox('hover', 'Button').duplicate() + hstyle.border_width_left = 5 * DialogicUtil.get_editor_scale() add_theme_stylebox_override('hover', hstyle) set_color(resource.event_color) @@ -47,7 +46,7 @@ func set_color(color:Color) -> void: func toggle_name(on:= false) -> void: if !on: text = "" - custom_minimum_size = Vector2(40, 40)*DialogicUtil.get_editor_scale() + 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) @@ -56,9 +55,9 @@ func toggle_name(on:= false) -> void: 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() + 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(): +func _on_button_down() -> void: find_parent('VisualEditor').get_node('%TimelineArea').start_dragging(1, resource) diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd index d6041907e..0baa46b57 100644 --- a/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd @@ -7,8 +7,8 @@ extends ScrollContainer enum DragTypes {NOTHING, NEW_EVENT, EXISTING_EVENTS} -var drag_type : DragTypes = DragTypes.NOTHING -var drag_data : Variant +var drag_type: DragTypes = DragTypes.NOTHING +var drag_data: Variant var drag_to_position := 0 var dragging := false @@ -59,7 +59,7 @@ func _process(delta:float) -> void: queue_redraw() -func finish_dragging(): +func finish_dragging() -> void: dragging = false if get_global_rect().has_point(get_global_mouse_position()): drag_completed.emit(drag_type, drag_to_position, drag_data) @@ -74,16 +74,15 @@ func finish_dragging(): ################################################################################ func _draw() -> void: - var _scale := DialogicUtil.get_editor_scale() - var line_width := 5 * _scale - var horizontal_line_length := 100*_scale + var line_width := 5 * DialogicUtil.get_editor_scale() + var horizontal_line_length := 100 * DialogicUtil.get_editor_scale() var color_multiplier := Color(1,1,1,0.25) var selected_color_multiplier := Color(1,1,1,1) ## Draw Event Lines for idx in range($Timeline.get_child_count()): - var block : Control = $Timeline.get_child(idx) + var block: Control = $Timeline.get_child(idx) if not "resource" in block: continue diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd index fbc5e8c36..bd350cbe1 100644 --- a/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd @@ -23,17 +23,18 @@ signal timeline_loaded var _batches := [] var _building_timeline := false var _timeline_changed_while_loading := false - +var _initialized := false ################## TIMELINE EVENT MANAGEMENT ################################### ################################################################################ -var selected_items : Array = [] +var selected_items: Array = [] +var drag_allowed := false #region CREATE/SAVE/LOAD ################################################################################ -func something_changed(): +func something_changed() -> void: timeline_editor.current_resource_state = DialogicEditor.ResourceStates.UNSAVED @@ -58,7 +59,8 @@ func save_timeline() -> void: 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) + var error: int = ResourceSaver.save(timeline_editor.current_resource, timeline_editor.current_resource.resource_path) + if error != OK: print('[Dialogic] Saving error: ', error) @@ -102,7 +104,7 @@ func load_timeline(resource:DialogicTimeline) -> void: %TimelineArea.scroll_vertical = 0 -func batch_events(array, size, batch_number): +func batch_events(array: Array, size: int, batch_number: int) -> Array: return array.slice((batch_number - 1) * size, batch_number * size) @@ -110,7 +112,7 @@ func batch_events(array, size, batch_number): var opener_events_stack := [] func load_batch(data:Array) -> void: - var current_batch :Array = _batches.pop_front() + var current_batch: Array = _batches.pop_front() if current_batch: for i in current_batch: if i is DialogicEndBranchEvent: @@ -121,7 +123,8 @@ func load_batch(data:Array) -> void: opener_events_stack.push_back(piece) batch_loaded.emit() -func _on_batch_loaded(): + +func _on_batch_loaded() -> void: if _timeline_changed_while_loading: return if _batches.size() > 0: @@ -131,15 +134,17 @@ func _on_batch_loaded(): 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(): +func clear_timeline_nodes() -> void: deselect_all_items() for event in %Timeline.get_children(): event.free() @@ -149,8 +154,7 @@ func clear_timeline_nodes(): #region SETUP ################################################################################ -func _ready(): - DialogicUtil.get_dialogic_plugin().dialogic_save.connect(save_timeline) +func _ready() -> void: event_node = load("res://addons/dialogic/Editor/Events/EventBlock/event_block.tscn") batch_loaded.connect(_on_batch_loaded) @@ -159,25 +163,39 @@ func _ready(): timeline_editor.editors_manager.sidebar.content_item_activated.connect(_on_content_item_clicked) %Timeline.child_order_changed.connect(update_content_list) + var editor_scale := DialogicUtil.get_editor_scale() + %RightSidebar.size.x = DialogicUtil.get_editor_setting("dialogic/editor/right_sidebar_width", 200 * editor_scale) + $View.split_offset = -DialogicUtil.get_editor_setting("dialogic/editor/right_sidebar_width", 200 * editor_scale) + sidebar_collapsed = DialogicUtil.get_editor_setting("dialogic/editor/right_sidebar_collapsed", false) + + load_event_buttons() + _on_right_sidebar_resized() + _initialized = true + func load_event_buttons() -> void: + sidebar_collapsed = DialogicUtil.get_editor_setting("dialogic/editor/right_sidebar_collapsed", false) + # 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 := DialogicResourceUtil.get_event_cache() + + for child in %RightSidebar.get_child(0).get_children(): + child.get_parent().remove_child(child) + child.queue_free() # Event buttons - var buttonScene := load("res://addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.tscn") + var button_scene := load("res://addons/dialogic/Editor/TimelineEditor/VisualEditor/AddEventButton.tscn") + var scripts := DialogicResourceUtil.get_event_cache() 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 @@ -192,7 +210,7 @@ func load_event_buttons() -> void: if event_resource.event_name in hidden_buttons: continue - var button :Button = buttonScene.instantiate() + var button: Button = button_scene.instantiate() button.resource = event_resource button.visible_name = event_resource.event_name button.event_icon = event_resource._get_icon() @@ -219,28 +237,27 @@ func load_event_buttons() -> void: section.add_child(button_container) sections[event_resource.event_category] = button_container - %RightSidebar.get_child(0).add_child(section) - + %RightSidebar.get_child(0).add_child(section, true) sections[event_resource.event_category].add_child(button) + button.toggle_name(!sidebar_collapsed) # 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)) + var sections_order: Array = DialogicUtil.get_editor_setting('event_section_order', + ['Main', 'Flow', 'Logic', 'Audio', 'Visual','Other', 'Helper']) + + sections_order.reverse() + for section_name in sections_order: + if %RightSidebar.get_child(0).has_node(section_name): + %RightSidebar.get_child(0).move_child(%RightSidebar.get_child(0).get_node(section_name), 0) # Resize RightSidebar - var _scale := DialogicUtil.get_editor_scale() - %RightSidebar.custom_minimum_size.x = 50 * _scale + %RightSidebar.custom_minimum_size.x = 50 * DialogicUtil.get_editor_scale() - $View.split_offset = -200*_scale _on_right_sidebar_resized() #endregion @@ -260,12 +277,20 @@ func _on_content_item_clicked(label:String) -> void: return -func update_content_list(): - var labels :PackedStringArray = [] +func update_content_list() -> void: + if not is_inside_tree(): + return + + 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) + + #endregion @@ -273,7 +298,7 @@ func update_content_list(): ################################################################################# # SIGNAL handles input on the events mainly for selection and moving events -func _on_event_block_gui_input(event, item: Node): +func _on_event_block_gui_input(event: InputEvent, item: Node) -> void: if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.is_pressed(): if len(selected_items) > 1 and item in selected_items and !Input.is_key_pressed(KEY_CTRL): @@ -283,9 +308,11 @@ func _on_event_block_gui_input(event, item: Node): elif len(selected_items) > 1 or Input.is_key_pressed(KEY_CTRL): select_item(item) + drag_allowed = true + if len(selected_items) > 0 and event is InputEventMouseMotion: if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): - if !%TimelineArea.dragging and !get_viewport().gui_is_dragging(): + if !%TimelineArea.dragging and !get_viewport().gui_is_dragging() and drag_allowed: sort_selection() %TimelineArea.start_dragging(%TimelineArea.DragTypes.EXISTING_EVENTS, selected_items) @@ -293,7 +320,7 @@ func _on_event_block_gui_input(event, item: Node): ## Activated by TimelineArea drag_completed func _on_timeline_area_drag_completed(type:int, index:int, data:Variant) -> void: if type == %TimelineArea.DragTypes.NEW_EVENT: - var resource :DialogicEvent = data.duplicate() + var resource: DialogicEvent = data.duplicate() resource._load_custom_defaults() add_event_undoable(resource, index) @@ -322,7 +349,7 @@ func add_event_node(event_resource:DialogicEvent, at_index:int = -1, auto_select var block: Control = event_node.instantiate() block.resource = event_resource - event_resource._editor_node = block + event_resource.editor_node = block event_resource._enter_visual_editor(timeline_editor) block.content_changed.connect(something_changed) @@ -353,7 +380,7 @@ func add_event_node(event_resource:DialogicEvent, at_index:int = -1, auto_select 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() + 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 @@ -365,13 +392,13 @@ func create_end_branch_event(at_index:int, parent_node:Node) -> Node: # 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): +func add_event_with_end_branch(resource, at_index:int=-1, auto_select:bool = false, indent:bool = false) -> void: var event := add_event_node(resource, at_index, auto_select, indent) create_end_branch_event(at_index+1, event) ## Adds an event (either single nodes or with end branches) to the timeline with UndoRedo support -func add_event_undoable(event_resource: DialogicEvent, at_index: int = -1): +func add_event_undoable(event_resource: DialogicEvent, at_index: int = -1) -> void: 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(event_resource, at_index, true, true)) @@ -447,7 +474,7 @@ func add_events_indexed(indexed_events:Dictionary) -> void: # now create the visual block. deselect_all_items() if event_resource is DialogicEndBranchEvent: - var idx :String = indexed_events[event_idx].trim_prefix('<>') + 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) @@ -460,6 +487,7 @@ func add_events_indexed(indexed_events:Dictionary) -> void: selected_items = events visual_update_selection() indent_events() + something_changed() ## Deletes events based on an indexed dictionary @@ -478,8 +506,8 @@ func delete_events_indexed(indexed_events:Dictionary) -> void: %Timeline.get_child(idx-idx_shift).get_parent().remove_child(%Timeline.get_child(idx-idx_shift)) idx_shift += 1 - something_changed() indent_events() + something_changed() func delete_selected_events() -> void: @@ -504,7 +532,6 @@ func cut_events_indexed(indexed_events:Dictionary) -> void: select_events_indexed(indexed_events) copy_selected_events() delete_events_indexed(indexed_events) - indent_events() func copy_selected_events() -> void: @@ -526,12 +553,12 @@ func copy_selected_events() -> void: func get_clipboard_data() -> Array: - var clipboard_parse :Variant= str_to_var(DisplayServer.clipboard_get()) + var clipboard_parse: Variant = str_to_var(DisplayServer.clipboard_get()) if clipboard_parse is Dictionary: 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!") + print("[Dialogic] Be careful when copying from another project!") if clipboard_parse.has('events'): return clipboard_parse.events return [] @@ -578,7 +605,7 @@ func select_item(item: Node, multi_possible:bool = true) -> void: if len(selected_items) == 0: selected_items = [item] else: - var index :int= selected_items[-1].get_index() + var index: int = selected_items[-1].get_index() var goal_idx := item.get_index() while true: if index < goal_idx: index += 1 @@ -652,7 +679,7 @@ func _add_event_button_pressed(event_resource:DialogicEvent, force_resource := f else: at_index = %Timeline.get_child_count() - var resource :DialogicEvent = null + var resource: DialogicEvent = null if force_resource: resource = event_resource else: @@ -809,8 +836,8 @@ func offset_blocks_by_index(blocks:Array, offset:int): func scroll_to_piece(piece_index:int) -> void: await get_tree().process_frame - var height :float = %Timeline.get_child(min(piece_index, %Timeline.get_child_count()-1)).position.y - if height < %TimelineArea.scroll_vertical or height > %TimelineArea.scroll_vertical+%TimelineArea.size.y-(200*DialogicUtil.get_editor_scale()): + var height: float = %Timeline.get_child(min(piece_index, %Timeline.get_child_count()-1)).position.y + if height < %TimelineArea.scroll_vertical or height > %TimelineArea.scroll_vertical+%TimelineArea.size.y: %TimelineArea.scroll_vertical = height @@ -877,7 +904,7 @@ func indent_events() -> void: ################################################################################ func _on_event_popup_menu_index_pressed(index:int) -> void: - var item :Control = %EventPopupMenu.current_event + var item: Control = %EventPopupMenu.current_event if index == 0: if not item in selected_items: selected_items = [item] @@ -905,34 +932,51 @@ func _on_event_popup_menu_index_pressed(index:int) -> void: TimelineUndoRedo.add_undo_method(add_events_indexed.bind(events_indexed)) TimelineUndoRedo.commit_action() indent_events() - something_changed() -func _on_right_sidebar_resized(): +func _on_right_sidebar_resized() -> void: var _scale := DialogicUtil.get_editor_scale() - if %RightSidebar.size.x < 160*_scale and !sidebar_collapsed: + + if %RightSidebar.size.x < 160 * _scale and (not sidebar_collapsed or not _initialized): 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: + + elif %RightSidebar.size.x > 160 * _scale and (sidebar_collapsed or not _initialized): 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) + + if _initialized: + DialogicUtil.set_editor_setting("dialogic/editor/right_sidebar_width", %RightSidebar.size.x) + DialogicUtil.set_editor_setting("dialogic/editor/right_sidebar_collapsed", sidebar_collapsed) + #endregion @@ -945,22 +989,25 @@ func duplicate_selected() -> void: var at_index: int = selected_items[-1].get_index()+1 TimelineUndoRedo.create_action("[D] Duplicate "+str(len(events))+" event(s).") TimelineUndoRedo.add_do_method(add_events_at_index.bind(events, at_index)) - TimelineUndoRedo.add_do_method(something_changed) TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(at_index, len(events))) - TimelineUndoRedo.add_undo_method(something_changed) TimelineUndoRedo.commit_action() func _input(event:InputEvent) -> void: + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed == false: + drag_allowed = false + # 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 @@ -997,7 +1044,7 @@ func _input(event:InputEvent) -> void: get_viewport().set_input_as_handled() ## Some shortcuts should be disabled when writing text. - var focus_owner : Control = get_viewport().gui_get_focus_owner() + var focus_owner: Control = get_viewport().gui_get_focus_owner() if focus_owner is TextEdit or focus_owner is LineEdit or (focus_owner is Button and focus_owner.get_parent_control().name == "Spin"): return @@ -1067,6 +1114,7 @@ func _input(event:InputEvent) -> void: 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).") @@ -1093,8 +1141,8 @@ func _input(event:InputEvent) -> void: func get_previous_character(double_previous := false) -> DialogicCharacter: - var character :DialogicCharacter = null - var idx :int = %Timeline.get_child_count() + var character: DialogicCharacter = null + var idx: int = %Timeline.get_child_count() if idx == 0: return null if len(selected_items): @@ -1123,3 +1171,109 @@ func get_previous_character(double_previous := false) -> DialogicCharacter: return character #endregion + +#region SEARCH +################################################################################ + +var search_results := {} +func _search_timeline(search_text:String) -> bool: + for event in search_results: + if is_instance_valid(search_results[event]): + search_results[event].set_search_text("") + search_results[event].deselect() + search_results[event].queue_redraw() + search_results.clear() + + for block in %Timeline.get_children(): + if block.resource is DialogicTextEvent: + var text_field: TextEdit = block.get_node("%BodyContent").find_child("Field_Text_Multiline", true, false) + text_field.set_search_text(search_text) + if text_field.search(search_text, 0, 0, 0).x != -1: + search_results[block] = text_field + text_field.queue_redraw() + set_meta("current_search", search_text) + search_navigate(false) + return not search_results.is_empty() + + +func _search_navigate_down() -> void: + search_navigate(false) + + +func _search_navigate_up() -> void: + search_navigate(true) + + +func search_navigate(navigate_up := false) -> void: + var search_text: String = get_meta("current_search", "") + + if search_results.is_empty() or %Timeline.get_child_count() == 0: + return + if selected_items.is_empty(): + select_item(%Timeline.get_child(0), false) + + while not selected_items[0] in search_results: + select_item(%Timeline.get_child(wrapi(selected_items[0].get_index()+1, 0, %Timeline.get_child_count()-1)), false) + + var event: Node = selected_items[0] + var counter := 0 + while true: + counter += 1 + var field: TextEdit = search_results[event] + field.queue_redraw() + var result := search_text_field(field, search_text, navigate_up) + var current_line := field.get_selection_from_line() if field.has_selection() else -1 + var current_column := field.get_selection_from_column() if field.has_selection() else -1 + var next_is_in_this_event := false + if result.y == -1: + next_is_in_this_event = false + elif navigate_up: + if current_line == -1: + current_line = field.get_line_count()-1 + current_column = field.get_line(current_line).length() + next_is_in_this_event = result.x < current_column or result.y < current_line + else: + next_is_in_this_event = result.x > current_column or result.y > current_line + + if next_is_in_this_event: + if not event in selected_items: + select_item(event, false) + %TimelineArea.ensure_control_visible(event) + event._on_ToggleBodyVisibility_toggled(true) + field.select(result.y, result.x, result.y, result.x+len(search_text)) + break + + else: + field.deselect() + var index := search_results.keys().find(event) + event = search_results.keys()[wrapi(index+(-1 if navigate_up else 1), 0, search_results.size())] + + if counter > 5: + print("[Dialogic] Search failed.") + break + + +func search_text_field(field:TextEdit, search_text := "", navigate_up:= false) -> Vector2i: + var search_from_line: int = 0 + var search_from_column: int = 0 + if field.has_selection(): + if navigate_up: + search_from_line = field.get_selection_from_line() + search_from_column = field.get_selection_from_column()-1 + if search_from_column == -1: + search_from_line -= 1 + if search_from_line == -1: + return Vector2i(-1, -1) + search_from_column = field.get_line(search_from_line).length()-1 + else: + search_from_line = field.get_selection_to_line() + search_from_column = field.get_selection_to_column() + else: + if navigate_up: + search_from_line = field.get_line_count()-1 + search_from_column = field.get_line(search_from_line).length()-1 + + var search := field.search(search_text, 4 if navigate_up else 0, search_from_line, search_from_column) + return search + +#endregion diff --git a/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd index 2658995f3..3c1d170b6 100644 --- a/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd +++ b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd @@ -2,7 +2,7 @@ extends Control func _ready() -> void: print("[Dialogic] Testing scene was started.") - if !ProjectSettings.get_setting('internationalization/locale/test', "").is_empty(): + if not ProjectSettings.get_setting('internationalization/locale/test', "").is_empty(): print("Testing locale is: ", ProjectSettings.get_setting('internationalization/locale/test')) $PauseIndictator.hide() @@ -14,8 +14,8 @@ func _ready() -> void: scene.position = get_viewport_rect().size/2.0 randomize() - var current_timeline: String = DialogicUtil.get_editor_setting('current_timeline_path', null) - if !current_timeline: + var current_timeline: String = DialogicUtil.get_editor_setting("current_timeline_path", "") + if not current_timeline: get_tree().quit() DialogicUtil.autoload().start(current_timeline) DialogicUtil.autoload().timeline_ended.connect(get_tree().quit) @@ -40,5 +40,5 @@ func _input(event:InputEvent) -> void: var is_auto_skip_enabled := auto_skip.enabled auto_skip.disable_on_unread_text = false - auto_skip.enabled = !is_auto_skip_enabled + auto_skip.enabled = not is_auto_skip_enabled diff --git a/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd b/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd index 189416a6d..e5302a7ee 100644 --- a/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd +++ b/addons/dialogic/Editor/TimelineEditor/timeline_editor.gd @@ -4,8 +4,11 @@ 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 +enum EditorMode {VISUAL, TEXT} + +var current_editor_mode := EditorMode.VISUAL +var play_timeline_button: Button = null + ## Overwrite. Register to the editor manager in here. func _register() -> void: @@ -39,11 +42,11 @@ func _register() -> void: current_editor_mode = DialogicUtil.get_editor_setting('timeline_editor_mode', 0) match current_editor_mode: - 0: + EditorMode.VISUAL: %VisualEditor.show() %TextEditor.hide() %SwitchEditorMode.text = "Text Editor" - 1: + EditorMode.TEXT: %VisualEditor.hide() %TextEditor.show() %SwitchEditorMode.text = "Visual Editor" @@ -65,9 +68,9 @@ func _open_resource(resource:Resource) -> void: current_resource = resource current_resource_state = ResourceStates.SAVED match current_editor_mode: - 0: + EditorMode.VISUAL: %VisualEditor.load_timeline(current_resource) - 1: + EditorMode.TEXT: %TextEditor.load_timeline(current_resource) $NoTimelineScreen.hide() %TimelineName.text = DialogicResourceUtil.get_unique_identifier(current_resource.resource_path) @@ -77,26 +80,32 @@ func _open_resource(resource:Resource) -> void: ## If this editor supports editing resources, save them here (overwrite in subclass) func _save() -> void: match current_editor_mode: - 0: + EditorMode.VISUAL: %VisualEditor.save_timeline() - 1: + EditorMode.TEXT: %TextEditor.save_timeline() func _input(event: InputEvent) -> void: - var keycode := KEY_F5 - if OS.get_name() == "macOS": - keycode = KEY_B - if event is InputEventKey and event.keycode == keycode and event.pressed: - if Input.is_key_pressed(KEY_CTRL): - play_timeline() + if event is InputEventKey: + var keycode := KEY_F5 + if OS.get_name() == "macOS": + keycode = KEY_B + if event.keycode == keycode and event.pressed: + if Input.is_key_pressed(KEY_CTRL): + play_timeline() + + if event.keycode == KEY_F and event.pressed: + if Input.is_key_pressed(KEY_CTRL): + if is_ancestor_of(get_viewport().gui_get_focus_owner()): + search_timeline() ## Method to play the current timeline. Connected to the button in the sidebar. -func play_timeline(): +func play_timeline() -> void: _save() - var dialogic_plugin = DialogicUtil.get_dialogic_plugin() + var dialogic_plugin := DialogicUtil.get_dialogic_plugin() # Save the current opened timeline DialogicUtil.set_editor_setting('current_timeline_path', current_resource.resource_path) @@ -105,32 +114,32 @@ func play_timeline(): ## Method to switch from visual to text editor (and vice versa). Connected to the button in the sidebar. -func toggle_editor_mode(): +func toggle_editor_mode() -> void: match current_editor_mode: - 0: - current_editor_mode = 1 + EditorMode.VISUAL: + current_editor_mode = EditorMode.TEXT %VisualEditor.save_timeline() %VisualEditor.hide() %TextEditor.show() %TextEditor.load_timeline(current_resource) %SwitchEditorMode.text = "Visual Editor" - 1: - current_editor_mode = 0 + EditorMode.TEXT: + current_editor_mode = EditorMode.VISUAL %TextEditor.save_timeline() %TextEditor.hide() %VisualEditor.load_timeline(current_resource) %VisualEditor.show() %SwitchEditorMode.text = "Text Editor" - + _on_search_text_changed(%Search.text) DialogicUtil.set_editor_setting('timeline_editor_mode', current_editor_mode) -func _on_resource_unsaved(): +func _on_resource_unsaved() -> void: if current_resource: current_resource.set_meta("timeline_not_saved", true) -func _on_resource_saved(): +func _on_resource_saved() -> void: if current_resource: current_resource.set_meta("timeline_not_saved", false) @@ -145,21 +154,22 @@ func new_timeline(path:String) -> void: editors_manager.edit_resource(new_timeline) -func _ready(): +func _ready() -> void: $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 - + %SwitchEditorMode.custom_minimum_size.x = 200 * DialogicUtil.get_editor_scale() + %SearchClose.icon = get_theme_icon("Close", "EditorIcons") + %SearchUp.icon = get_theme_icon("MoveUp", "EditorIcons") + %SearchDown.icon = get_theme_icon("MoveDown", "EditorIcons") -func _on_create_timeline_button_pressed(): +func _on_create_timeline_button_pressed() -> void: editors_manager.show_add_resource_dialog( new_timeline, '*.dtl; DialogicTimeline', @@ -168,13 +178,61 @@ func _on_create_timeline_button_pressed(): ) -func _clear(): +func _clear() -> void: current_resource = null current_resource_state = ResourceStates.SAVED match current_editor_mode: - 0: + EditorMode.VISUAL: %VisualEditor.clear_timeline_nodes() - 1: + EditorMode.TEXT: %TextEditor.clear_timeline() $NoTimelineScreen.show() play_timeline_button.disabled = true + + +func get_current_editor() -> Node: + if current_editor_mode == 1: + return %TextEditor + return %VisualEditor + +#region SEARCH + +func search_timeline() -> void: + %SearchSection.show() + if get_viewport().gui_get_focus_owner() is TextEdit: + %Search.text = get_viewport().gui_get_focus_owner().get_selected_text() + _on_search_text_changed(%Search.text) + else: + %Search.text = "" + %Search.grab_focus() + + +func _on_close_search_pressed() -> void: + %SearchSection.hide() + %Search.text = "" + _on_search_text_changed('') + + +func _on_search_text_changed(new_text: String) -> void: + var editor: Node = null + var anything_found: bool = get_current_editor()._search_timeline(new_text) + if anything_found or new_text.is_empty(): + %SearchLabel.hide() + %Search.add_theme_color_override("font_color", get_theme_color("font_color", "Editor")) + else: + %SearchLabel.show() + %SearchLabel.add_theme_color_override("font_color", get_theme_color("error_color", "Editor")) + %Search.add_theme_color_override("font_color", get_theme_color("error_color", "Editor")) + %SearchLabel.text = "No Match" + + +func _on_search_down_pressed() -> void: + get_current_editor()._search_navigate_down() + + +func _on_search_up_pressed() -> void: + get_current_editor()._search_navigate_up() + +#endregion + + diff --git a/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn b/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn index 799d24d1e..ba77fb4f6 100644 --- a/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn +++ b/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn @@ -6,7 +6,7 @@ [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_3cd31"] +[sub_resource type="Image" id="Image_43fqw"] 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", @@ -15,13 +15,13 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_wvrw5"] -image = SubResource("Image_3cd31") +[sub_resource type="ImageTexture" id="ImageTexture_lvr8x"] +image = SubResource("Image_43fqw") [sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_7lpql"] script = ExtResource("4_1t6bf") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3migc"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lpeon"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 @@ -62,11 +62,11 @@ 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. +tooltip_text = "This unique identifier is based on the file name. You can change it in the Reference Manager. 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_wvrw5") +You can also use this name in Dialogic.start()." +texture = SubResource("ImageTexture_lvr8x") hint_text = "This unique identifier is based on the file name. You can change it in the Reference Manager. This is what you should use in a jump event to reference this timeline. @@ -80,7 +80,7 @@ size_flags_horizontal = 10 size_flags_vertical = 4 tooltip_text = "Switch between Text Editor and Visual Editor" text = "Text editor" -icon = SubResource("ImageTexture_wvrw5") +icon = SubResource("ImageTexture_lvr8x") [node name="VisualEditor" parent="VBox" instance=ExtResource("2_qs7vc")] unique_name_in_owner = true @@ -100,6 +100,34 @@ symbol_lookup_on_click = true line_folding = false gutters_draw_fold_gutter = false +[node name="SearchSection" type="HBoxContainer" parent="VBox"] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="Search" type="LineEdit" parent="VBox/SearchSection"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Search" + +[node name="SearchLabel" type="Label" parent="VBox/SearchSection"] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="SearchUp" type="Button" parent="VBox/SearchSection"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="SearchDown" type="Button" parent="VBox/SearchSection"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="SearchClose" type="Button" parent="VBox/SearchSection"] +unique_name_in_owner = true +layout_mode = 2 + [node name="NoTimelineScreen" type="PanelContainer" parent="."] visible = false layout_mode = 1 @@ -108,7 +136,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_3migc") +theme_override_styles/panel = SubResource("StyleBoxFlat_lpeon") [node name="CenterContainer" type="CenterContainer" parent="NoTimelineScreen"] layout_mode = 2 @@ -128,4 +156,8 @@ autowrap_mode = 3 layout_mode = 2 text = "Create New Timeline" +[connection signal="text_changed" from="VBox/SearchSection/Search" to="." method="_on_search_text_changed"] +[connection signal="pressed" from="VBox/SearchSection/SearchUp" to="." method="_on_search_up_pressed"] +[connection signal="pressed" from="VBox/SearchSection/SearchDown" to="." method="_on_search_down_pressed"] +[connection signal="pressed" from="VBox/SearchSection/SearchClose" to="." method="_on_close_search_pressed"] [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 index 3bb813feb..353958ac5 100644 --- a/addons/dialogic/Editor/dialogic_editor.gd +++ b/addons/dialogic/Editor/dialogic_editor.gd @@ -41,7 +41,7 @@ func _get_title() -> String: ## If this editor supports editing resources, load them here (overwrite in subclass) -func _open_resource(resource:Resource) -> void: +func _open_resource(_resource:Resource) -> void: pass @@ -51,7 +51,7 @@ func _save() -> void: ## Overwrite. Called when this editor is shown. (show() doesn't have to be called) -func _open(extra_info:Variant = null) -> void: +func _open(_extra_info:Variant = null) -> void: pass diff --git a/addons/dialogic/Editor/editor_main.gd b/addons/dialogic/Editor/editor_main.gd index 533470c38..599405756 100644 --- a/addons/dialogic/Editor/editor_main.gd +++ b/addons/dialogic/Editor/editor_main.gd @@ -8,159 +8,256 @@ var editors_manager: Control = null var editor_file_dialog: EditorFileDialog -## Styling -@export var editor_tab_bg := StyleBoxFlat.new() - +@onready var sidebar := %Sidebar as DialogicSidebar func _ready() -> void: if get_parent() is SubViewport: return + ## CONNECTIONS + sidebar.show_sidebar.connect(_on_sidebar_toggled) + ## REFERENCES - editors_manager = $Margin/EditorsManager - var button :Button = editors_manager.add_icon_button(get_theme_icon("MakeFloating", "EditorIcons"), 'Make floating') + editors_manager = $EditorsManager + var button: Button = editors_manager.add_icon_button( + get_theme_icon("MakeFloating", "EditorIcons"), "Make floating" + ) button.pressed.connect(toggle_floating_window) - - - ## STYLING - $BG.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")) + 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) + 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.add_button("No Saving Please!", true, "nosave") $SaveConfirmationDialog.hide() update_theme_additions() + EditorInterface.get_base_control().theme_changed.connect(update_theme_additions) -func update_theme_additions(): +func _on_sidebar_toggled(sidebar_shown: bool) -> void: + var h_split := (%HSplit as HSplitContainer) + if sidebar_shown: + h_split.dragger_visibility = SplitContainer.DRAGGER_VISIBLE + h_split.split_offset = 150 + h_split.collapsed = false + else: + h_split.dragger_visibility = SplitContainer.DRAGGER_HIDDEN_COLLAPSED + h_split.split_offset = 0 + h_split.collapsed = true + + +func update_theme_additions() -> void: + add_theme_stylebox_override( + "panel", + ( + DCSS + . inline( + { + "background": get_theme_color("base_color", "Editor"), + "padding": + [5 * DialogicUtil.get_editor_scale(), 5 * DialogicUtil.get_editor_scale()], + } + ) + ) + ) + var holder_panel := ( + DCSS + . inline( + { + "border-radius": 5, + #'border': 2, + #'border-color': get_theme_color("base_color", "Editor"), + "background": get_theme_color("dark_color_2", "Editor"), + "padding": + [5 * DialogicUtil.get_editor_scale(), 5 * DialogicUtil.get_editor_scale()], + } + ) + ) + holder_panel.border_width_top = 0 + holder_panel.corner_radius_top_left = 0 + editors_manager.editors_holder.add_theme_stylebox_override("panel", holder_panel) + 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_color('font_uneditable_color', 'DialogicTitle', get_theme_color('warning_color', 'Editor')) - theme.set_color('font_selected_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) + 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_color( + "font_uneditable_color", "DialogicTitle", get_theme_color("warning_color", "Editor") + ) + theme.set_color( + "font_selected_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, + "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) + theme.set_stylebox("panel", "DialogicPanelDarkA", dark_panel) var cornerless_panel := panel_style.duplicate() cornerless_panel.corner_radius_top_left = 0 - theme.set_stylebox('panel', 'DialogicPanelA_cornerless', cornerless_panel) - + theme.set_stylebox("panel", "DialogicPanelA_cornerless", cornerless_panel) # panel used for example for portrait previews in character editor - theme.set_type_variation('DialogicPanelB', 'PanelContainer') - var side_panel :StyleBoxFlat= panel_style.duplicate() + 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.expand_margin_left = get_theme_constant("separation", "SplitContainer") 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_stylebox("panel", "DialogicPanelB", side_panel) - - theme.set_type_variation('DialogicEventEdit', 'Control') + 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.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) + 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) + 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) + 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_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_type_variation("DialogicTextEventTextEdit", "CodeEdit") + var editor_settings := plugin_reference.get_editor_interface().get_editor_settings() + var text_panel := ( + DCSS + . inline( + { + "border-radius": 8, + "background": + editor_settings.get_setting("text_editor/theme/highlighting/background_color").lerp( + editor_settings.get_setting("text_editor/theme/highlighting/text_color"), 0.05 + ), + "padding": [8, 8], + } + ) + ) + text_panel.content_margin_bottom = 5 + text_panel.content_margin_left = 13 + theme.set_stylebox("normal", "DialogicTextEventTextEdit", text_panel) + + var event_field_group_panel := DCSS.inline({ + 'border-radius': 8, + "border":1, + "padding":2, + "boder-color": get_theme_color("property_color", "Editor"), + "background":"none"}) + theme.set_type_variation("DialogicEventEditGroup", "PanelContainer") + theme.set_stylebox("panel", "DialogicEventEditGroup", event_field_group_panel) theme.set_icon('Plugin', 'Dialogic', load("res://addons/dialogic/Editor/Images/plugin-icon.svg")) ## Switches from floating window mode to embedded mode based on current mode -func toggle_floating_window(): +func toggle_floating_window() -> void: if get_parent() is Window: swap_to_embedded_editor() else: @@ -168,7 +265,7 @@ func toggle_floating_window(): ## Removes the main control from it's parent and adds it to a new Window node -func swap_to_floating_window(): +func swap_to_floating_window() -> void: if get_parent() is Window: return @@ -187,23 +284,31 @@ func swap_to_floating_window(): window.disable_3d = true window.wrap_controls = true window.popup_centered() - plugin_reference.get_editor_interface().set_main_screen_editor('2D') + plugin_reference.get_editor_interface().set_main_screen_editor("2D") ## Removes the main control from the window node and adds it to it's grandparent ## which is the original owner. -func swap_to_embedded_editor(): +func swap_to_embedded_editor() -> void: if not get_parent() is Window: return var window := get_parent() get_parent().remove_child(self) - plugin_reference.get_editor_interface().set_main_screen_editor('Dialogic') + plugin_reference.get_editor_interface().set_main_screen_editor("Dialogic") window.get_parent().add_child(self) window.queue_free() -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: +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(): @@ -216,10 +321,10 @@ func godot_file_dialog(callable:Callable, filter:String, mode := EditorFileDialo 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 + 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() + 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) @@ -229,4 +334,3 @@ func godot_file_dialog(callable:Callable, filter:String, mode := EditorFileDialo 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 index 435b5a9f4..849ad796b 100644 --- a/addons/dialogic/Editor/editor_main.tscn +++ b/addons/dialogic/Editor/editor_main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=19 format=3 uid="uid://de6yhw4r8jqb3"] +[gd_scene load_steps=18 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"] @@ -15,22 +15,7 @@ [ext_resource type="Script" path="res://addons/dialogic/Editor/Common/update_manager.gd" id="14_l6b1p"] [ext_resource type="PackedScene" uid="uid://vv3m5m68fwg7" path="res://addons/dialogic/Editor/Common/update_install_window.tscn" id="15_cu4xj"] -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mdik5"] -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_rog03"] +[sub_resource type="Image" id="Image_uqxml"] 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", @@ -40,19 +25,18 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_drcn6"] -image = SubResource("Image_rog03") +image = SubResource("Image_uqxml") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hwjob"] -content_margin_left = 4.0 -content_margin_top = 4.0 -content_margin_right = 4.0 -content_margin_bottom = 4.0 +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5bs7k"] +content_margin_left = 8.0 +content_margin_top = 8.0 +content_margin_right = 8.0 +content_margin_bottom = 8.0 bg_color = Color(0.1155, 0.132, 0.1595, 1) corner_detail = 1 anti_aliasing = false -[node name="EditorView" type="Control"] -layout_mode = 3 +[node name="EditorView" type="ScrollContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -61,30 +45,15 @@ grow_vertical = 2 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1_x88ov") -editor_tab_bg = SubResource("StyleBoxFlat_mdik5") - -[node name="BG" type="ColorRect" parent="."] -layout_mode = 2 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0.0588235, 0.0980392, 0.141176, 1) - -[node name="Margin" type="MarginContainer" parent="."] -layout_mode = 2 -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"] +[node name="EditorsManager" type="Control" parent="."] layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 script = ExtResource("2_pe2tl") -[node name="HSplit" type="HSplitContainer" parent="Margin/EditorsManager"] +[node name="HSplit" type="HSplitContainer" parent="EditorsManager"] +unique_name_in_owner = true layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -95,29 +64,28 @@ size_flags_vertical = 3 theme_override_constants/separation = 0 split_offset = 150 -[node name="Sidebar" parent="Margin/EditorsManager/HSplit" instance=ExtResource("3_lp6hj")] +[node name="Sidebar" parent="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"] +[node name="VBox" type="VBoxContainer" parent="EditorsManager/HSplit"] layout_mode = 2 theme_override_constants/separation = 0 -[node name="Toolbar" type="HBoxContainer" parent="Margin/EditorsManager/HSplit/VBox"] +[node name="Toolbar" type="HBoxContainer" parent="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"] +[node name="EditorTabBar" type="TabBar" parent="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") @@ -132,15 +100,15 @@ 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"] +[node name="CustomButtons" type="HBoxContainer" parent="EditorsManager/HSplit/VBox/Toolbar"] unique_name_in_owner = true layout_mode = 2 -[node name="Editors" type="PanelContainer" parent="Margin/EditorsManager/HSplit/VBox"] +[node name="Editors" type="PanelContainer" parent="EditorsManager/HSplit/VBox"] layout_mode = 2 size_flags_vertical = 3 -[node name="CodeCompletionHelper" type="Node" parent="Margin/EditorsManager"] +[node name="CodeCompletionHelper" type="Node" parent="EditorsManager"] script = ExtResource("11_fyce4") [node name="SaveConfirmationDialog" type="AcceptDialog" parent="."] @@ -166,7 +134,7 @@ content_scale_aspect = 4 script = ExtResource("10_xbkrt") [node name="Manager" parent="ReferenceManager" instance=ExtResource("10_l1rf8")] -theme_override_styles/panel = SubResource("StyleBoxFlat_hwjob") +theme_override_styles/panel = SubResource("StyleBoxFlat_5bs7k") [node name="UpdateManager" type="Node" parent="."] script = ExtResource("14_l6b1p") diff --git a/addons/dialogic/Editor/editors_manager.gd b/addons/dialogic/Editor/editors_manager.gd index af959a1d7..029a9199f 100644 --- a/addons/dialogic/Editor/editors_manager.gd +++ b/addons/dialogic/Editor/editors_manager.gd @@ -7,21 +7,22 @@ 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 +@onready var hsplit := $HSplit +@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 reference_manager: Node: get: - return get_node("../../ReferenceManager") + return get_node("../ReferenceManager") ## Information on supported resource extensions and registered editors var current_editor: DialogicEditor = null var previous_editor: DialogicEditor = null var editors := {} var supported_file_extensions := [] -var used_resources_cache : Array = [] +var used_resources_cache: Array = [] ################################################################################ @@ -55,7 +56,7 @@ func _ready() -> void: DialogicResourceUtil.update() - await get_parent().get_parent().ready + await get_parent().ready await get_tree().process_frame load_saved_state() @@ -65,6 +66,8 @@ func _ready() -> void: find_parent('EditorView').plugin_reference.get_editor_interface().get_file_system_dock().files_moved.connect(_on_file_moved) find_parent('EditorView').plugin_reference.get_editor_interface().get_file_system_dock().file_removed.connect(_on_file_removed) + hsplit.set("theme_override_constants/separation", get_theme_constant("base_margin", "Editor") * DialogicUtil.get_editor_scale()) + func _add_editor(path:String) -> void: var editor: DialogicEditor = load(path).instantiate() @@ -117,7 +120,7 @@ func _on_editors_tab_changed(tab:int) -> void: func edit_resource(resource:Resource, save_previous:bool = true, silent:= false) -> void: if not resource: # The resource doesn't exists, show an error - print('[Dialogic] The resource you are trying to edit doesn\'t exists any more.') + print("[Dialogic] The resource you are trying to edit doesn't exist any more.") return if current_editor and save_previous: @@ -166,7 +169,7 @@ func open_editor(editor:DialogicEditor, save_previous: bool = true, extra_info:V tabbar.current_tab = editor.get_index() if editor.current_resource: - var text:String = editor.current_resource.resource_path.get_file() + var text: String = editor.current_resource.resource_path.get_file() if editor.current_resource_state == DialogicEditor.ResourceStates.UNSAVED: text += "(*)" @@ -198,7 +201,7 @@ func show_add_resource_dialog(accept_callable:Callable, filter:String = "*", tit func _on_add_resource_dialog_accepted(path:String, callable:Callable) -> void: - var file_name :String= path.get_file().trim_suffix('.'+path.get_extension()) + var file_name: String = path.get_file().trim_suffix('.'+path.get_extension()) for i in ['#','&','+',';','(',')','!','*','*','"',"'",'%', '$', ':','.',',']: file_name = file_name.replace(i, '') callable.call(path.trim_suffix(path.get_file()).path_join(file_name)+'.'+path.get_extension()) @@ -206,7 +209,8 @@ func _on_add_resource_dialog_accepted(path:String, callable:Callable) -> void: ## Called by the plugin.gd script on CTRL+S or Debug Game start func save_current_resource() -> void: - current_editor._save() + if current_editor: + current_editor._save() ## Change the resource state @@ -276,5 +280,5 @@ func get_current_editor() -> DialogicEditor: return current_editor -func _exit_tree(): +func _exit_tree() -> void: DialogicUtil.set_editor_setting('last_resources', used_resources_cache) diff --git a/addons/dialogic/Example Assets/already_read_indicator.gd b/addons/dialogic/Example Assets/already_read_indicator.gd index 8ead30869..4dbf5d392 100644 --- a/addons/dialogic/Example Assets/already_read_indicator.gd +++ b/addons/dialogic/Example Assets/already_read_indicator.gd @@ -1,6 +1,6 @@ extends Control -func _ready(): +func _ready() -> void: if DialogicUtil.autoload().has_subsystem('History'): DialogicUtil.autoload().History.visited_event.connect(_on_visited_event) DialogicUtil.autoload().History.unvisited_event.connect(_on_not_read_event) diff --git a/addons/dialogic/Example Assets/default_event.gd b/addons/dialogic/Example Assets/default_event.gd index a2cfa1ed0..6ffbc7145 100644 --- a/addons/dialogic/Example Assets/default_event.gd +++ b/addons/dialogic/Example Assets/default_event.gd @@ -2,7 +2,7 @@ extends DialogicEvent # DEFINE ALL PROPERTIES OF THE EVENT -# var MySetting :String = "" +# var MySetting: String = "" func _execute() -> void: # I have no idea how this event works ;) diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd index e2378dd3b..5595991f9 100644 --- a/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd @@ -9,7 +9,7 @@ func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String if $Sprite.sprite_frames.has_animation(passed_portrait): $Sprite.play(passed_portrait) -func _on_animated_sprite_2d_animation_finished(): +func _on_animated_sprite_2d_animation_finished() -> void: $Sprite.frame = randi()%$Sprite.sprite_frames.get_frame_count($Sprite.animation) $Sprite.play() diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd index acb600aa6..7b1b74a8b 100644 --- a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd @@ -3,7 +3,7 @@ extends DialogicPortrait enum Faces {BASED_ON_PORTRAIT_NAME, NEUTRAL, HAPPY, SAD, JOY, SHOCK, ANGRY} -@export var emotion : Faces = Faces.BASED_ON_PORTRAIT_NAME +@export var emotion: Faces = Faces.BASED_ON_PORTRAIT_NAME @export var portrait_width: int @export var portrait_height: int @export var alien := true diff --git a/addons/dialogic/Modules/Audio/event_music.gd b/addons/dialogic/Modules/Audio/event_music.gd index dd5c923d0..c54c5bd9d 100644 --- a/addons/dialogic/Modules/Audio/event_music.gd +++ b/addons/dialogic/Modules/Audio/event_music.gd @@ -8,15 +8,15 @@ extends DialogicEvent ### Settings ## The file to play. If empty, the previous music will be faded out. -var file_path: String = "" +var file_path := "" ## 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" +var audio_bus := "" ## If true, the audio will loop, otherwise only play once. -var loop: bool = true +var loop := true ################################################################################ @@ -57,7 +57,7 @@ func get_shortcode_parameters() -> Dictionary: "path" : {"property": "file_path", "default": ""}, "fade" : {"property": "fade_length", "default": 0}, "volume" : {"property": "volume", "default": 0}, - "bus" : {"property": "audio_bus", "default": "Master", + "bus" : {"property": "audio_bus", "default": "", "suggestions": get_bus_suggestions}, "loop" : {"property": "loop", "default": true}, } diff --git a/addons/dialogic/Modules/Audio/event_sound.gd b/addons/dialogic/Modules/Audio/event_sound.gd index 3d8308bc4..844006177 100644 --- a/addons/dialogic/Modules/Audio/event_sound.gd +++ b/addons/dialogic/Modules/Audio/event_sound.gd @@ -8,13 +8,13 @@ extends DialogicEvent ### Settings ## The path to the file to play. -var file_path: String = "" +var file_path := "" ## The volume to play the sound at. var volume: float = 0 ## The bus to play the sound on. -var audio_bus: String = "Master" +var audio_bus := "" ## If true, the sound will loop infinitely. Not recommended (as there is no way to stop it). -var loop: bool = false +var loop := false ################################################################################ @@ -54,7 +54,7 @@ func get_shortcode_parameters() -> Dictionary: #param_name : property_name "path" : {"property": "file_path", "default": "",}, "volume" : {"property": "volume", "default": 0}, - "bus" : {"property": "audio_bus", "default": "Master", + "bus" : {"property": "audio_bus", "default": "", "suggestions": get_bus_suggestions}, "loop" : {"property": "loop", "default": false}, } @@ -64,7 +64,7 @@ func get_shortcode_parameters() -> Dictionary: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_edit('file_path', ValueType.FILE, {'left_text' : 'Play', 'file_filter' : '*.mp3, *.ogg, *.wav; Supported Audio Files', @@ -73,6 +73,7 @@ func build_event_editor(): add_body_edit('volume', ValueType.NUMBER, {'left_text':'Volume:', 'mode':2}, '!file_path.is_empty()') add_body_edit('audio_bus', ValueType.SINGLELINE_TEXT, {'left_text':'Audio Bus:'}, '!file_path.is_empty()') + func get_bus_suggestions() -> Dictionary: var bus_name_list := {} for i in range(AudioServer.bus_count): diff --git a/addons/dialogic/Modules/Audio/subsystem_audio.gd b/addons/dialogic/Modules/Audio/subsystem_audio.gd index 5a22680fd..e27fbeb42 100644 --- a/addons/dialogic/Modules/Audio/subsystem_audio.gd +++ b/addons/dialogic/Modules/Audio/subsystem_audio.gd @@ -34,6 +34,8 @@ signal sound_started(info: Dictionary) ## ## Background music is long audio. var base_music_player := AudioStreamPlayer.new() +## Reference to the last used music player. +var current_music_player: AudioStreamPlayer ## Audio player base, that will be duplicated to play sound effects. ## ## Sound effects are short audio. @@ -46,7 +48,7 @@ var base_sound_player := AudioStreamPlayer.new() ## Clears the state on this subsystem and stops all audio. ## ## If you want to stop sounds only, use [method stop_all_sounds]. -func clear_game_state(clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: +func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: update_music() stop_all_sounds() @@ -73,6 +75,11 @@ func resume() -> void: for child in get_children(): child.stream_paused = false + +func _on_dialogic_timeline_ended() -> void: + if not dialogic.Styles.get_layout_node(): + clear_game_state() + pass #endregion @@ -80,6 +87,8 @@ func resume() -> void: #################################################################################################### func _ready() -> void: + dialogic.timeline_ended.connect(_on_dialogic_timeline_ended) + base_music_player.name = "Music" add_child(base_music_player) @@ -88,36 +97,45 @@ func _ready() -> void: ## Updates the background music. Will fade out previous music. -func update_music(path := "", volume := 0.0, audio_bus := "Master", fade_time := 0.0, loop := true) -> void: +func update_music(path := "", volume := 0.0, audio_bus := "", fade_time := 0.0, loop := 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(): + if is_instance_valid(current_music_player) and current_music_player.playing or !path.is_empty(): fader = create_tween() + var prev_node: Node = null - if base_music_player.playing: - prev_node = base_music_player.duplicate() + if is_instance_valid(current_music_player) and current_music_player.playing: + prev_node = current_music_player.duplicate() add_child(prev_node) - prev_node.play(base_music_player.get_playback_position()) - prev_node.remove_from_group('dialogic_music_player') + prev_node.play(current_music_player.get_playback_position()) 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 not base_music_player.stream is AudioStreamWAV: - if "loop" in base_music_player.stream: - base_music_player.stream.loop = loop - elif "loop_mode" in base_music_player.stream: + current_music_player = base_music_player.duplicate() + add_child(current_music_player) + current_music_player.stream = load(path) + current_music_player.volume_db = volume + if audio_bus: + current_music_player.bus = audio_bus + if not current_music_player.stream is AudioStreamWAV: + if "loop" in current_music_player.stream: + current_music_player.stream.loop = loop + elif "loop_mode" in current_music_player.stream: if loop: - base_music_player.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD + current_music_player.stream.loop_mode = AudioStreamWAV.LOOP_FORWARD else: - base_music_player.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED + current_music_player.stream.loop_mode = AudioStreamWAV.LOOP_DISABLED - base_music_player.play(0) - fader.parallel().tween_method(interpolate_volume_linearly.bind(base_music_player), 0.0, db_to_linear(volume),fade_time) + current_music_player.play(0) + fader.parallel().tween_method(interpolate_volume_linearly.bind(current_music_player), 0.0, db_to_linear(volume),fade_time) else: - base_music_player.stop() + if is_instance_valid(current_music_player): + current_music_player.stop() + current_music_player.queue_free() + if prev_node: fader.tween_callback(prev_node.queue_free) @@ -128,12 +146,14 @@ func has_music() -> bool: ## Plays a given sound file. -func play_sound(path: String, volume := 0.0, audio_bus := "Master", loop := false) -> void: +func play_sound(path: String, volume := 0.0, audio_bus := "", loop := 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: @@ -141,8 +161,11 @@ func play_sound(path: String, volume := 0.0, audio_bus := "Master", loop := fals 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 + if audio_bus: + 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) diff --git a/addons/dialogic/Modules/Background/DefaultBackgroundScene/default_background.gd b/addons/dialogic/Modules/Background/DefaultBackgroundScene/default_background.gd index ce4d217bb..ef3c57058 100644 --- a/addons/dialogic/Modules/Background/DefaultBackgroundScene/default_background.gd +++ b/addons/dialogic/Modules/Background/DefaultBackgroundScene/default_background.gd @@ -3,8 +3,8 @@ extends DialogicBackground ## The default background scene. ## Extend the DialogicBackground class to create your own background scene. -@onready var image_node = $Image -@onready var color_node = $ColorRect +@onready var image_node: TextureRect = $Image +@onready var color_node: ColorRect = $ColorRect func _ready() -> void: @@ -15,7 +15,7 @@ func _ready() -> void: image_node.anchor_bottom = 1 -func _update_background(argument:String, time:float) -> void: +func _update_background(argument:String, _time:float) -> void: if argument.begins_with('res://'): image_node.texture = load(argument) color_node.color = Color.TRANSPARENT diff --git a/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_diagonal_up_left.gd b/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_diagonal_up_left.gd index 787ed404f..061f943c7 100644 --- a/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_diagonal_up_left.gd +++ b/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_diagonal_up_left.gd @@ -2,7 +2,7 @@ extends "res://addons/dialogic/Modules/Background/Transitions/simple_swipe_trans func _fade() -> void: var shader := setup_swipe_shader() - var texture :GradientTexture2D = shader.get_shader_parameter('wipe_texture') + var texture: GradientTexture2D = shader.get_shader_parameter('wipe_texture') texture.fill_from = Vector2.DOWN texture.fill_to = Vector2.RIGHT tween_shader_progress() diff --git a/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_left_to_right.gd b/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_left_to_right.gd index 32084e92e..8b55d4147 100644 --- a/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_left_to_right.gd +++ b/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_left_to_right.gd @@ -2,7 +2,7 @@ extends "res://addons/dialogic/Modules/Background/Transitions/simple_swipe_trans func _fade() -> void: var shader := setup_swipe_shader() - var texture :GradientTexture2D = shader.get_shader_parameter('wipe_texture') + var texture: GradientTexture2D = shader.get_shader_parameter('wipe_texture') texture.fill_from = Vector2.ZERO texture.fill_to = Vector2.RIGHT diff --git a/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_right_to_left.gd b/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_right_to_left.gd index 14005d9aa..5433d595b 100644 --- a/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_right_to_left.gd +++ b/addons/dialogic/Modules/Background/Transitions/Defaults/swipe_right_to_left.gd @@ -2,7 +2,7 @@ extends "res://addons/dialogic/Modules/Background/Transitions/simple_swipe_trans func _fade() -> void: var shader := setup_swipe_shader() - var texture :GradientTexture2D = shader.get_shader_parameter('wipe_texture') + var texture: GradientTexture2D = shader.get_shader_parameter('wipe_texture') texture.fill_from = Vector2.RIGHT texture.fill_to = Vector2.ZERO tween_shader_progress() diff --git a/addons/dialogic/Modules/Background/Transitions/class_dialogic_background_transition.gd b/addons/dialogic/Modules/Background/Transitions/class_dialogic_background_transition.gd index f5465d5a1..3f4f1f015 100644 --- a/addons/dialogic/Modules/Background/Transitions/class_dialogic_background_transition.gd +++ b/addons/dialogic/Modules/Background/Transitions/class_dialogic_background_transition.gd @@ -2,7 +2,7 @@ class_name DialogicBackgroundTransition extends Node ## Helper -var this_folder : String = get_script().resource_path.get_base_dir() +var this_folder: String = get_script().resource_path.get_base_dir() ## Set before _fade() is called, will be the root node of the previous bg scene. @@ -42,7 +42,7 @@ func set_shader(path_to_shader:String=DialogicUtil.get_module_path('Background') return null -func tween_shader_progress(progress_parameter:="progress") -> PropertyTweener: +func tween_shader_progress(_progress_parameter:="progress") -> PropertyTweener: if !bg_holder: return diff --git a/addons/dialogic/Modules/Background/dialogic_background.gd b/addons/dialogic/Modules/Background/dialogic_background.gd index a7794c31b..9e84a91af 100644 --- a/addons/dialogic/Modules/Background/dialogic_background.gd +++ b/addons/dialogic/Modules/Background/dialogic_background.gd @@ -15,24 +15,24 @@ var viewport: SubViewport ## 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: +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: +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 _custom_fade_in(time:float) -> bool: +func _custom_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 _custom_fade_out(time:float) -> bool: +func _custom_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 index 3fac581c9..0380e6981 100644 --- a/addons/dialogic/Modules/Background/event_background.gd +++ b/addons/dialogic/Modules/Background/event_background.gd @@ -10,14 +10,14 @@ extends DialogicEvent ## 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 = "" +var scene := "" ## 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 = "" +var argument := "" ## The time the fade animation will take. Leave at 0 for instant change. var fade: float = 0.0 ## Name of the transition to use. -var transition: String = "" +var transition := "" ## Helpers for visual editor enum ArgumentTypes {IMAGE, CUSTOM} @@ -95,7 +95,7 @@ func get_shortcode_parameters() -> Dictionary: #region EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_edit('_scene_type', ValueType.FIXED_OPTIONS, { 'left_text' :'Show', 'options': [ @@ -146,8 +146,8 @@ func build_event_editor(): add_body_edit("fade", ValueType.NUMBER, {'left_text':'Fade time:'}) -func get_transition_suggestions(filter:String="") -> Dictionary: - var transitions := DialogicResourceUtil.list_special_resources_of_type("BackgroundTransition") +func get_transition_suggestions(_filter:String="") -> Dictionary: + var transitions := DialogicResourceUtil.list_special_resources("BackgroundTransition") var suggestions := {} for i in transitions: suggestions[DialogicUtil.pretty_name(i)] = {'value': DialogicUtil.pretty_name(i), 'editor_icon': ["PopupMenu", "EditorIcons"]} diff --git a/addons/dialogic/Modules/Background/index.gd b/addons/dialogic/Modules/Background/index.gd index e25806421..d40f03020 100644 --- a/addons/dialogic/Modules/Background/index.gd +++ b/addons/dialogic/Modules/Background/index.gd @@ -9,5 +9,5 @@ func _get_subsystems() -> Array: return [{'name':'Backgrounds', 'script':this_folder.path_join('subsystem_backgrounds.gd')}] -func _get_special_resources() -> Array[Dictionary]: - return list_special_resources("Transitions/Defaults", "BackgroundTransition", ".gd") +func _get_special_resources() -> Dictionary: + return {&"BackgroundTransition":list_special_resources("Transitions/Defaults", ".gd")} diff --git a/addons/dialogic/Modules/Background/node_background_holder.gd b/addons/dialogic/Modules/Background/node_background_holder.gd index 6b2c3b520..11a262872 100644 --- a/addons/dialogic/Modules/Background/node_background_holder.gd +++ b/addons/dialogic/Modules/Background/node_background_holder.gd @@ -2,5 +2,5 @@ class_name DialogicNode_BackgroundHolder extends ColorRect -func _ready(): +func _ready() -> void: add_to_group('dialogic_background_holders') diff --git a/addons/dialogic/Modules/Background/subsystem_backgrounds.gd b/addons/dialogic/Modules/Background/subsystem_backgrounds.gd index 6b9fcac45..f98d83031 100644 --- a/addons/dialogic/Modules/Background/subsystem_backgrounds.gd +++ b/addons/dialogic/Modules/Background/subsystem_backgrounds.gd @@ -99,7 +99,7 @@ func update_background(scene := "", argument := "", fade_time := 0.0, transition else: new_viewport = null - var trans_script: Script = load(DialogicResourceUtil.guess_special_resource("BackgroundTransition", transition_path, default_transition)) + var trans_script: Script = load(DialogicResourceUtil.guess_special_resource("BackgroundTransition", transition_path, {"path":default_transition}).path) var trans_node := Node.new() trans_node.set_script(trans_script) trans_node = (trans_node as DialogicBackgroundTransition) @@ -143,6 +143,7 @@ func _on_transition_finished(background_node:DialogicNode_BackgroundHolder, tran background_node.color = Color.TRANSPARENT transition_node.queue_free() + ## Adds sub-viewport with the given background scene as child to ## Dialogic scene. func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder) -> SubViewportContainer: @@ -166,6 +167,7 @@ func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder viewport.transparent_bg = true viewport.disable_3d = true viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS + viewport.canvas_item_default_texture_filter = ProjectSettings.get_setting("rendering/textures/canvas_textures/default_texture_filter") viewport.add_child(b_scene) b_scene.viewport = viewport @@ -176,6 +178,7 @@ func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder return v_con + ## Returns the current background node. func get_current_background_node() -> Node: var background_holder: DialogicNode_BackgroundHolder = null @@ -191,7 +194,6 @@ func get_current_background_node() -> Node: return current_background_node - ## Whether a background is set. func has_background() -> bool: return !dialogic.current_state_info.get('background_scene', '').is_empty() or !dialogic.current_state_info.get('background_argument','').is_empty() diff --git a/addons/dialogic/Modules/Call/event_call.gd b/addons/dialogic/Modules/Call/event_call.gd index 7b4b10e3a..0f2c05ddc 100644 --- a/addons/dialogic/Modules/Call/event_call.gd +++ b/addons/dialogic/Modules/Call/event_call.gd @@ -7,16 +7,16 @@ extends DialogicEvent ### Settings ## The name of the autoload to call the method on. -var autoload_name: String = "" +var autoload_name := "" ## The name of the method to call on the given autoload. -var method: String = "": +var method := "": set(value): method = value if Engine.is_editor_hint(): update_argument_info() check_arguments_and_update_warning() ## A list of arguments to give to the call. -var arguments: Array = []: +var arguments := []: set(value): arguments = value if Engine.is_editor_hint(): @@ -88,7 +88,6 @@ func to_text() -> String: result += '()' else: result += '(' - var arr := [] for i in arguments: if i is String and i.begins_with('@'): result += i.trim_prefix('@') @@ -137,7 +136,7 @@ func get_shortcode_parameters() -> Dictionary: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_edit('autoload_name', ValueType.DYNAMIC_OPTIONS, {'left_text':'On autoload', 'empty_text':'Autoload', 'suggestions_func':get_autoload_suggestions, @@ -166,15 +165,24 @@ func get_method_suggestions(filter:String="", temp_autoload:String = "") -> Dict var suggestions := {} var script: Script - if temp_autoload: + if temp_autoload and ProjectSettings.has_setting('autoload/'+temp_autoload): script = load(ProjectSettings.get_setting('autoload/'+temp_autoload).trim_prefix('*')) + elif autoload_name and ProjectSettings.has_setting('autoload/'+autoload_name): - script = load(ProjectSettings.get_setting('autoload/'+autoload_name).trim_prefix('*')) + var loaded_autoload := load(ProjectSettings.get_setting('autoload/'+autoload_name).trim_prefix('*')) + + if loaded_autoload is PackedScene: + var packed_scene: PackedScene = loaded_autoload + script = packed_scene.instantiate().get_script() + + else: + script = loaded_autoload + if script: - for method in script.get_script_method_list(): - if method.name.begins_with('@') or method.name.begins_with('_'): + for script_method in script.get_script_method_list(): + if script_method.name.begins_with('@') or script_method.name.begins_with('_'): continue - suggestions[method.name] = {'value': method.name, 'tooltip':method.name, 'editor_icon': ["Callable", "EditorIcons"]} + suggestions[script_method.name] = {'value': script_method.name, 'tooltip':script_method.name, 'editor_icon': ["Callable", "EditorIcons"]} if !filter.is_empty(): suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]} return suggestions @@ -185,14 +193,14 @@ func update_argument_info() -> void: if !ResourceLoader.exists(ProjectSettings.get_setting('autoload/'+autoload_name, '').trim_prefix('*')): _current_method_arg_hints = {} return - var script :Script = load(ProjectSettings.get_setting('autoload/'+autoload_name, '').trim_prefix('*')) + var script: Script = load(ProjectSettings.get_setting('autoload/'+autoload_name, '').trim_prefix('*')) for m in script.get_script_method_list(): if m.name == method: _current_method_arg_hints = {'a':autoload_name, 'm':method, 'info':m} break -func check_arguments_and_update_warning(): +func check_arguments_and_update_warning() -> void: if not _current_method_arg_hints.has("info") or _current_method_arg_hints.info.is_empty(): ui_update_warning.emit() return @@ -206,7 +214,7 @@ func check_arguments_and_update_warning(): if _current_method_arg_hints.info.args[idx].type != typeof(arg): if arg is String and arg.begins_with('@'): continue - var expected_type :String = "" + var expected_type: String = "" match _current_method_arg_hints.info.args[idx].type: TYPE_BOOL: expected_type = "bool" TYPE_STRING: expected_type = "string" @@ -228,7 +236,7 @@ func check_arguments_and_update_warning(): ####################### CODE COMPLETION ######################################## ################################################################################ -func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: +func _get_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void: if line.count(' ') == 1 and not '.' in line: for i in get_autoload_suggestions(): TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'.', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("Node", "EditorIcons")) @@ -237,7 +245,7 @@ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:Str TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+'(', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), TextNode.get_theme_icon("Callable", "EditorIcons")) -func _get_start_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit) -> void: +func _get_start_code_completion(_CodeCompletionHelper:Node, TextNode:TextEdit) -> void: TextNode.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, 'do', 'do ', event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.3), _get_icon()) diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd index 2d006a540..b99e409ab 100644 --- a/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd +++ b/addons/dialogic/Modules/Character/DefaultAnimations/bounce.gd @@ -1,11 +1,16 @@ extends DialogicAnimation -func animate(): +func animate() -> void: 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.tween_property(node, 'position:y', base_position.y-node.get_viewport().size.y/10, time*0.4).set_trans(Tween.TRANS_EXPO) + tween.parallel().tween_property(node, 'scale:y', base_scale.y*1.05, time*0.4).set_trans(Tween.TRANS_EXPO) + tween.tween_property(node, 'position:y', base_position.y, time*0.6).set_trans(Tween.TRANS_BOUNCE) + tween.parallel().tween_property(node, 'scale:y', base_scale.y, time*0.6).set_trans(Tween.TRANS_BOUNCE) tween.finished.connect(emit_signal.bind('finished_once')) + + +func _get_named_variations() -> Dictionary: + return { + "bounce": {"type": AnimationType.ACTION}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in.gd deleted file mode 100644 index 941d1e4e9..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in.gd +++ /dev/null @@ -1,13 +0,0 @@ -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_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in_out.gd new file mode 100644 index 000000000..d5484d901 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_in_out.gd @@ -0,0 +1,39 @@ +extends DialogicAnimation + + +func animate() -> void: + var tween := (node.create_tween() as Tween) + + var end_scale: Vector2 = node.scale + var end_modulate_alpha := 1.0 + var modulation_property := get_modulation_property() + + if is_reversed: + end_scale = Vector2(0, 0) + end_modulate_alpha = 0.0 + + else: + node.scale = Vector2(0, 0) + var original_modulation: Color = node.get(modulation_property) + original_modulation.a = 0.0 + node.set(modulation_property, original_modulation) + + + tween.set_ease(Tween.EASE_IN_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.set_parallel() + + (tween.tween_property(node, "scale", end_scale, time) + .set_trans(Tween.TRANS_SPRING) + .set_ease(Tween.EASE_OUT)) + tween.tween_property(node, modulation_property + ":a", end_modulate_alpha, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "bounce in": {"reversed": false, "type": AnimationType.IN}, + "bounce out": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/bounce_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/bounce_out.gd deleted file mode 100644 index bc9764282..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/bounce_out.gd +++ /dev/null @@ -1,15 +0,0 @@ -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_down_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/fade_down_in_out.gd new file mode 100644 index 000000000..847bda793 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/fade_down_in_out.gd @@ -0,0 +1,44 @@ +extends DialogicAnimation + +func animate() -> void: + var tween := (node.create_tween() as Tween) + + var start_height: float = base_position.y - node.get_viewport().size.y / 5 + var end_height := base_position.y + + var start_modulation := 0.0 + var end_modulation := 1.0 + + if is_reversed: + end_height = start_height + start_height = base_position.y + end_modulation = 0.0 + start_modulation = 1.0 + + node.position.y = start_height + + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.set_parallel() + + var end_postion := Vector2(base_position.x, end_height) + tween.tween_property(node, "position", end_postion, time) + + var property := get_modulation_property() + + var original_modulation: Color = node.get(property) + original_modulation.a = start_modulation + node.set(property, original_modulation) + var modulation_alpha := property + ":a" + + tween.tween_property(node, modulation_alpha, end_modulation, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "fade in down": {"reversed": false, "type": AnimationType.IN}, + "fade out up": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_out.gd new file mode 100644 index 000000000..e432ecd43 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_out.gd @@ -0,0 +1,34 @@ +extends DialogicAnimation + +func animate() -> void: + + var modulation_property := get_modulation_property() + var end_modulation_alpha := 1.0 + + if is_reversed: + end_modulation_alpha = 0.0 + + else: + var original_modulation: Color = node.get(modulation_property) + original_modulation.a = 0.0 + node.set(modulation_property, original_modulation) + + var tween := (node.create_tween() as Tween) + if is_reversed: + tween.set_ease(Tween.EASE_IN) + else: + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.tween_property(node, modulation_property + ":a", end_modulation_alpha, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "fade in": {"reversed": false, "type": AnimationType.IN}, + "fade out": {"reversed": true, "type": AnimationType.OUT}, + "fade cross": {"type": AnimationType.CROSSFADE}, + } + diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_up.gd b/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_up.gd deleted file mode 100644 index 4ee09eca2..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/fade_in_up.gd +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index 45adeef1f..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/fade_out_down.gd +++ /dev/null @@ -1,12 +0,0 @@ -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/fade_up_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/fade_up_in_out.gd new file mode 100644 index 000000000..83a8ba822 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/fade_up_in_out.gd @@ -0,0 +1,44 @@ +extends DialogicAnimation + +func animate() -> void: + var tween := (node.create_tween() as Tween) + + var start_height: float = base_position.y + node.get_viewport().size.y / 5 + var end_height := base_position.y + + var start_modulation := 0.0 + var end_modulation := 1.0 + + if is_reversed: + end_height = start_height + start_height = base_position.y + end_modulation = 0.0 + start_modulation = 1.0 + + node.position.y = start_height + + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_SINE) + tween.set_parallel() + + var end_postion := Vector2(base_position.x, end_height) + tween.tween_property(node, "position", end_postion, time) + + var property := get_modulation_property() + + var original_modulation: Color = node.get(property) + original_modulation.a = start_modulation + node.set(property, original_modulation) + var modulation_alpha := property + ":a" + + tween.tween_property(node, modulation_alpha, end_modulation, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "fade in up": {"reversed": false, "type": AnimationType.IN}, + "fade out down": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd b/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd index f83a8ee80..36e8eab83 100644 --- a/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd +++ b/addons/dialogic/Modules/Character/DefaultAnimations/heartbeat.gd @@ -1,7 +1,13 @@ extends DialogicAnimation -func animate(): +func animate() -> void: 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')) + + +func _get_named_variations() -> Dictionary: + return { + "heartbeat": {"type": AnimationType.ACTION}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_or_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_or_out.gd deleted file mode 100644 index 873225bb5..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_or_out.gd +++ /dev/null @@ -1,5 +0,0 @@ -extends DialogicAnimation - -func animate(): - await node.get_tree().process_frame - emit_signal('finished') diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_out.gd new file mode 100644 index 000000000..3044c69fd --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/instant_in_out.gd @@ -0,0 +1,12 @@ +extends DialogicAnimation + +func animate() -> void: + await node.get_tree().process_frame + finished.emit() + + +func _get_named_variations() -> Dictionary: + return { + "instant in": {"reversed": false, "type": AnimationType.IN}, + "instant out": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd b/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd index 6b22c9974..de445fc95 100644 --- a/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd +++ b/addons/dialogic/Modules/Character/DefaultAnimations/shake_x.gd @@ -1,17 +1,20 @@ extends DialogicAnimation -func animate(): +func animate() -> void: 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) - + var strength: float = node.get_viewport().size.x/60 + var bound_multitween := DialogicUtil.multitween.bind(node, "position", "animation_shake_x") + tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.2) + tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1) + tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.1) + tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1) + tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.1) + tween.tween_method(bound_multitween, Vector2(), Vector2(-1,0)*strength, time*0.1) + tween.tween_method(bound_multitween, Vector2(), Vector2(1, 0)*strength, time*0.2) tween.finished.connect(emit_signal.bind('finished_once')) + +func _get_named_variations() -> Dictionary: + return { + "shake x": {"type": AnimationType.ACTION}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd b/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd index 63f78ba5c..6a0224c46 100644 --- a/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd +++ b/addons/dialogic/Modules/Character/DefaultAnimations/shake_y.gd @@ -1,17 +1,23 @@ extends DialogicAnimation -func animate(): +func animate() -> void: 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) - + + var strength: float = node.get_viewport().size.y/40 + tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.2) + tween.tween_property(node, 'position:y', base_position.y - strength, time * 0.1) + tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.1) + tween.tween_property(node, 'position:y', base_position.y - strength, time * 0.1) + tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.1) + tween.tween_property(node, 'position:y', base_position.y - strength, time * 0.1) + tween.tween_property(node, 'position:y', base_position.y + strength, time * 0.1) + tween.tween_property(node, 'position:y', base_position.y, time * 0.2) + tween.finished.connect(emit_signal.bind('finished_once')) + + +func _get_named_variations() -> Dictionary: + return { + "shake y": {"type": AnimationType.ACTION}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_down_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_down_in_out.gd new file mode 100644 index 000000000..af0124812 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_down_in_out.gd @@ -0,0 +1,26 @@ +extends DialogicAnimation + +func animate() -> void: + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + var target_position := base_position.y + var start_position: float = -node.get_viewport().size.y + + if is_reversed: + target_position = -node.get_viewport().size.y + start_position = base_position.y + + node.position.y = start_position + + tween.tween_property(node, 'position:y', target_position, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "slide in down": {"reversed": false, "type": AnimationType.IN}, + "slide out up": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_down.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_down.gd deleted file mode 100644 index d448e1284..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_down.gd +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 77bf984f3..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_left.gd +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 7df50cdc2..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_right.gd +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 9c48695a4..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_in_up.gd +++ /dev/null @@ -1,10 +0,0 @@ -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_left_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_left_in_out.gd new file mode 100644 index 000000000..f0d7df38a --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_left_in_out.gd @@ -0,0 +1,26 @@ +extends DialogicAnimation + + +func animate() -> void: + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + var end_position_x: float = base_position.x + + if is_reversed: + end_position_x = -node.get_viewport().size.x / 2 + + else: + node.position.x = -node.get_viewport().size.x / 5 + + tween.tween_property(node, 'position:x', end_position_x, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "slide in left": {"reversed": false, "type": AnimationType.IN}, + "slide out right": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_down.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_down.gd deleted file mode 100644 index 66de11015..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_down.gd +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 123b84412..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_left.gd +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 102412470..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_right.gd +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 1eeeeeffd..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/slide_out_up.gd +++ /dev/null @@ -1,9 +0,0 @@ -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/slide_right_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_right_in_out.gd new file mode 100644 index 000000000..3e4a5a4bb --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_right_in_out.gd @@ -0,0 +1,27 @@ +extends DialogicAnimation + +func animate() -> void: + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + var viewport_x: float = node.get_viewport().size.x + + var start_position_x: float = viewport_x + viewport_x / 5 + var end_position_x := base_position.x + + if is_reversed: + start_position_x = base_position.x + end_position_x = viewport_x + node.get_viewport().size.x / 5 + + + node.position.x = start_position_x + tween.tween_property(node, 'position:x', end_position_x, time) + + tween.finished.connect(emit_signal.bind('finished_once')) + + +func _get_named_variations() -> Dictionary: + return { + "slide in right": {"reversed": false, "type": AnimationType.IN}, + "slide out left": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/slide_up_in.gd b/addons/dialogic/Modules/Character/DefaultAnimations/slide_up_in.gd new file mode 100644 index 000000000..fcb1e3376 --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/slide_up_in.gd @@ -0,0 +1,25 @@ +extends DialogicAnimation + +func animate() -> void: + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) + + var start_position_y: float = node.get_viewport().size.y * 2 + var end_position_y := base_position.y + + if is_reversed: + start_position_y = base_position.y + end_position_y = node.get_viewport().size.y * 2 + + node.position.y = start_position_y + tween.tween_property(node, 'position:y', end_position_y, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "slide in up": {"reversed": false, "type": AnimationType.IN}, + "slide out down": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd b/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd index ee9f001ec..0d578b186 100644 --- a/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd +++ b/addons/dialogic/Modules/Character/DefaultAnimations/tada.gd @@ -1,11 +1,11 @@ extends DialogicAnimation -func animate(): +func animate() -> void: var tween := (node.create_tween() as Tween) tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) - - var strength :float = 0.01 - + + 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) @@ -15,5 +15,11 @@ func animate(): 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')) + + +func _get_named_variations() -> Dictionary: + return { + "tada": {"type": AnimationType.ACTION}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_center_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_center_in_out.gd new file mode 100644 index 000000000..fad8b614e --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_center_in_out.gd @@ -0,0 +1,36 @@ +extends DialogicAnimation + +func animate() -> void: + var modulate_property := get_modulation_property() + var modulate_alpha_property := modulate_property + ":a" + + var end_scale: Vector2 = node.scale + var end_modulation_alpha := 1.0 + + if is_reversed: + end_modulation_alpha = 0.0 + + else: + node.scale = Vector2(0, 0) + node.position.y = base_position.y - node.get_viewport().size.y * 0.5 + + var original_modulation: Color = node.get(modulate_property) + original_modulation.a = 0.0 + node.set(modulate_property, original_modulation) + + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_IN_OUT).set_trans(Tween.TRANS_EXPO) + tween.set_parallel(true) + tween.tween_property(node, "scale", end_scale, time) + tween.tween_property(node, "position", base_position, time) + tween.tween_property(node, modulate_alpha_property, end_modulation_alpha, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "zoom center in": {"reversed": false, "type": AnimationType.IN}, + "zoom center out": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in.gd deleted file mode 100644 index 749c2db7a..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in.gd +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 7472a196c..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in_center.gd +++ /dev/null @@ -1,15 +0,0 @@ -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_in_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in_out.gd new file mode 100644 index 000000000..21cc3382d --- /dev/null +++ b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_in_out.gd @@ -0,0 +1,35 @@ +extends DialogicAnimation + +func animate() -> void: + var modulate_property := get_modulation_property() + var modulate_alpha_property := modulate_property + ":a" + + var end_scale: Vector2 = node.scale + var end_modulation_alpha := 1.0 + + if is_reversed: + end_scale = Vector2(0, 0) + end_modulation_alpha = 0.0 + + else: + node.scale = Vector2(0,0) + + var original_modulation: Color = node.get(modulate_property) + original_modulation.a = 0.0 + node.set(modulate_property, original_modulation) + + var tween := (node.create_tween() as Tween) + tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_EXPO) + tween.set_parallel(true) + tween.tween_property(node, "scale", end_scale, time) + tween.tween_property(node, modulate_alpha_property, end_modulation_alpha, time) + + await tween.finished + finished_once.emit() + + +func _get_named_variations() -> Dictionary: + return { + "zoom in": {"reversed": false, "type": AnimationType.IN}, + "zoom out": {"reversed": true, "type": AnimationType.OUT}, + } diff --git a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out.gd b/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out.gd deleted file mode 100644 index dedf61900..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out.gd +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 1860259e3..000000000 --- a/addons/dialogic/Modules/Character/DefaultAnimations/zoom_out_center.gd +++ /dev/null @@ -1,12 +0,0 @@ -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/DialogicPortraitAnimationsUtil.gd b/addons/dialogic/Modules/Character/DialogicPortraitAnimationsUtil.gd new file mode 100644 index 000000000..66908e330 --- /dev/null +++ b/addons/dialogic/Modules/Character/DialogicPortraitAnimationsUtil.gd @@ -0,0 +1,47 @@ +@tool +class_name DialogicPortraitAnimationUtil + +enum AnimationType {ALL=-1, IN=1, OUT=2, ACTION=3, CROSSFADE=4} + + +static func guess_animation(string:String, type := AnimationType.ALL) -> String: + var default := {} + var filter := {} + var ignores := [] + match type: + AnimationType.ALL: + pass + AnimationType.IN: + filter = {"type":AnimationType.IN} + ignores = ["in"] + AnimationType.OUT: + filter = {"type":AnimationType.OUT} + ignores = ["out"] + AnimationType.ACTION: + filter = {"type":AnimationType.ACTION} + AnimationType.CROSSFADE: + filter = {"type":AnimationType.CROSSFADE} + ignores = ["cross"] + return DialogicResourceUtil.guess_special_resource(&"PortraitAnimation", string, default, filter, ignores).get("path", "") + + +static func get_portrait_animations_filtered(type := AnimationType.ALL) -> Dictionary: + var filter := {"type":type} + if type == AnimationType.ALL: + filter["type"] = [AnimationType.IN, AnimationType.OUT, AnimationType.ACTION] + return DialogicResourceUtil.list_special_resources("PortraitAnimation", filter) + + +static func get_suggestions(_search_text := "", current_value:= "", empty_text := "Default", action := AnimationType.ALL) -> Dictionary: + var suggestions := {} + + if empty_text and current_value: + suggestions[empty_text] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + + for anim_name in get_portrait_animations_filtered(action): + suggestions[DialogicUtil.pretty_name(anim_name)] = { + 'value' : DialogicUtil.pretty_name(anim_name), + 'editor_icon' : ["Animation", "EditorIcons"] + } + + return suggestions diff --git a/addons/dialogic/Modules/Character/class_dialogic_animation.gd b/addons/dialogic/Modules/Character/class_dialogic_animation.gd index b050c0286..9792ab237 100644 --- a/addons/dialogic/Modules/Character/class_dialogic_animation.gd +++ b/addons/dialogic/Modules/Character/class_dialogic_animation.gd @@ -3,45 +3,75 @@ extends Node ## Class that can be used to animate portraits. Can be extended to create animations. +enum AnimationType {IN=1, OUT=2, ACTION=3, CROSSFADE=4} + signal finished_once signal finished ## Set at runtime, will be the node to animate. -var node :Node +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 +var time: float + +## Set at runtime, will be the base position of the node. +## Depending on the animation, this might be the start, end or both. +var base_position: Vector2 +## Set at runtime, will be the base scale of the node. +var base_scale: Vector2 ## Used to repeate the animation for a number of times. -var repeats : int +var repeats: int +## If `true`, the animation will be reversed. +## This must be implemented by each animation or it will have no effect. +var is_reversed: bool = false -func _ready(): - connect('finished_once', finished_one_loop) + +func _ready() -> void: + finished_once.connect(finished_one_loop) ## To be overridden. Do the actual animating/tweening in here. -## Use the properties [node], [time], [end_position], [orig_pos]. -func animate(): +## Use the properties [member node], [member time], [member base_position], etc. +func animate() -> void: pass -func finished_one_loop(): +## This method controls whether to repeat the animation or not. +## Animations must call this once they finished an animation. +func finished_one_loop() -> void: repeats -= 1 + if repeats > 0: animate() - elif repeats == 0: - emit_signal("finished") + else: + finished.emit() -func pause(): + +func pause() -> void: if node: node.process_mode = Node.PROCESS_MODE_DISABLED -func resume(): +func resume() -> void: if node: node.process_mode = Node.PROCESS_MODE_INHERIT + + +func _get_named_variations() -> Dictionary: + return {} + + +## If the animation wants to change the modulation, this method +## will return the property to change. +## +## The [class CanvasGroup] can use `self_modulate` instead of `modulate` +## to uniformly change the modulation of all children without additively +## overlaying the modulations. +func get_modulation_property() -> String: + if node is CanvasGroup: + return "self_modulate" + else: + return "modulate" diff --git a/addons/dialogic/Modules/Character/custom_portrait_thumbnail.png b/addons/dialogic/Modules/Character/custom_portrait_thumbnail.png new file mode 100644 index 000000000..77d3cf323 Binary files /dev/null and b/addons/dialogic/Modules/Character/custom_portrait_thumbnail.png differ diff --git a/addons/dialogic/Modules/Text/icon.png.import b/addons/dialogic/Modules/Character/custom_portrait_thumbnail.png.import similarity index 62% rename from addons/dialogic/Modules/Text/icon.png.import rename to addons/dialogic/Modules/Character/custom_portrait_thumbnail.png.import index 7a22d4c56..c570321de 100644 --- a/addons/dialogic/Modules/Text/icon.png.import +++ b/addons/dialogic/Modules/Character/custom_portrait_thumbnail.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://qjpkitm0gily" -path="res://.godot/imported/icon.png-24a13267459b061f88f2248f23206b6d.ctex" +uid="uid://c5tu88x32sjkf" +path="res://.godot/imported/custom_portrait_thumbnail.png-0513583853d87342a634d56bc0bec965.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://addons/dialogic/Modules/Text/icon.png" -dest_files=["res://.godot/imported/icon.png-24a13267459b061f88f2248f23206b6d.ctex"] +source_file="res://addons/dialogic/Modules/Character/custom_portrait_thumbnail.png" +dest_files=["res://.godot/imported/custom_portrait_thumbnail.png-0513583853d87342a634d56bc0bec965.ctex"] [params] diff --git a/addons/dialogic/Modules/Character/default_portrait.gd b/addons/dialogic/Modules/Character/default_portrait.gd index 25edb32e5..dcd89b01e 100644 --- a/addons/dialogic/Modules/Character/default_portrait.gd +++ b/addons/dialogic/Modules/Character/default_portrait.gd @@ -5,7 +5,7 @@ extends DialogicPortrait ## The parent class has a character and portrait variable. @export_group('Main') -@export_file var image : String = "" +@export_file var image := "" ## Load anything related to the given character and portrait diff --git a/addons/dialogic/Modules/Character/default_portrait_thumbnail.png b/addons/dialogic/Modules/Character/default_portrait_thumbnail.png new file mode 100644 index 000000000..369a1118b Binary files /dev/null and b/addons/dialogic/Modules/Character/default_portrait_thumbnail.png differ diff --git a/addons/dialogic/Modules/Character/icon_character.png.import b/addons/dialogic/Modules/Character/default_portrait_thumbnail.png.import similarity index 62% rename from addons/dialogic/Modules/Character/icon_character.png.import rename to addons/dialogic/Modules/Character/default_portrait_thumbnail.png.import index 05efb2416..5ecc041b4 100644 --- a/addons/dialogic/Modules/Character/icon_character.png.import +++ b/addons/dialogic/Modules/Character/default_portrait_thumbnail.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b1i3ttk36kghm" -path="res://.godot/imported/icon_character.png-df3b6adf2508bd6ab91af526266b4aaa.ctex" +uid="uid://b0eisk30btlx2" +path="res://.godot/imported/default_portrait_thumbnail.png-f9f92adb946f409def18655d6ab5c5df.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"] +source_file="res://addons/dialogic/Modules/Character/default_portrait_thumbnail.png" +dest_files=["res://.godot/imported/default_portrait_thumbnail.png-f9f92adb946f409def18655d6ab5c5df.ctex"] [params] diff --git a/addons/dialogic/Modules/Character/dialogic_portrait.gd b/addons/dialogic/Modules/Character/dialogic_portrait.gd index 438506d60..b2bf47ad4 100644 --- a/addons/dialogic/Modules/Character/dialogic_portrait.gd +++ b/addons/dialogic/Modules/Character/dialogic_portrait.gd @@ -12,11 +12,12 @@ 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. +## If this returns true, it won't instance a new scene, but call +## [method _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 +## This allows implementing transitions between portraits that use the same scene. +func _should_do_portrait_update(_character: DialogicCharacter, _portrait: String) -> bool: + return false ## If the custom portrait accepts a change, then accept it here @@ -25,17 +26,18 @@ func _should_do_portrait_update(character:DialogicCharacter, portrait:String) -> ## >>> $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: +func _update_portrait(_passed_character: DialogicCharacter, _passed_portrait: String) -> void: pass ## 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: +## character editor preview and in portrait containers. +## Scale and offset will be applied by Dialogic. +## For example, a simple sprite: ## >>> 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. +## This will only work as expected if the portrait is positioned so that the +## root is at the pivot point. ## ## If you've used apply_texture this should work automatically. func _get_covered_rect() -> Rect2: @@ -55,7 +57,7 @@ func _set_mirror(mirror:bool) -> void: ## Function to accept and use the extra data, if the custom portrait wants to accept it -func _set_extra_data(data: String) -> void: +func _set_extra_data(_data: String) -> void: pass #endregion diff --git a/addons/dialogic/Modules/Character/event_character.gd b/addons/dialogic/Modules/Character/event_character.gd index 154287059..1294d51e0 100644 --- a/addons/dialogic/Modules/Character/event_character.gd +++ b/addons/dialogic/Modules/Character/event_character.gd @@ -5,45 +5,75 @@ extends DialogicEvent enum Actions {JOIN, LEAVE, UPDATE} - ### Settings ## The type of action of this event (JOIN/LEAVE/UPDATE). See [Actions]. -var action : int = Actions.JOIN +var action := Actions.JOIN ## The character that will join/leave/update. -var character : DialogicCharacter = null +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 = "" +var portrait := "" ## The index of the position this character should move to -var position: int = 1 +var transform := "center" + ## Name of the 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 = "" +var animation_name := "" ## 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 +var animation_wait := false + +## The fade animation to use. If left empty, the default cross-fade animation AND time will be used. +var fade_animation := "" +var fade_length := 0.5 + ## 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 +var transform_time: float = 0.0 +var transform_ease := Tween.EaseType.EASE_IN_OUT +var transform_trans := Tween.TransitionType.TRANS_SINE + +var ease_options := [ + {'label': 'In', 'value': Tween.EASE_IN}, + {'label': 'Out', 'value': Tween.EASE_OUT}, + {'label': 'In_Out', 'value': Tween.EASE_IN_OUT}, + {'label': 'Out_In', 'value': Tween.EASE_OUT_IN}, + ] + +var trans_options := [ + {'label': 'Linear', 'value': Tween.TRANS_LINEAR}, + {'label': 'Sine', 'value': Tween.TRANS_SINE}, + {'label': 'Quint', 'value': Tween.TRANS_QUINT}, + {'label': 'Quart', 'value': Tween.TRANS_QUART}, + {'label': 'Quad', 'value': Tween.TRANS_QUAD}, + {'label': 'Expo', 'value': Tween.TRANS_EXPO}, + {'label': 'Elastic', 'value': Tween.TRANS_ELASTIC}, + {'label': 'Cubic', 'value': Tween.TRANS_CUBIC}, + {'label': 'Circ', 'value': Tween.TRANS_CIRC}, + {'label': 'Bounce', 'value': Tween.TRANS_BOUNCE}, + {'label': 'Back', 'value': Tween.TRANS_BACK}, + {'label': 'Spring', 'value': Tween.TRANS_SPRING} + ] + ## 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 +var mirrored := false ## If set, will be passed to the portrait scene. -var extra_data: String = "" +var extra_data := "" ### Helpers ## Indicators for whether something should be updated (UPDATE mode only) var set_portrait := false -var set_position := false +var set_transform := false var set_z_index := false var set_mirrored := false ## Used to set the character resource from the unique name identifier and vice versa @@ -60,112 +90,107 @@ var character_identifier: String: character_identifier = value character = DialogicResourceUtil.get_character_resource(value) -# 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*\\[(?.*)\\])?") +var regex := RegEx.create_from_string(r'(?join|update|leave)\s*(")?(?(?(2)[^"\n]*|[^(: \n]*))(?(2)"|)(\W*\((?.*)\))?(\s*(?[^\[]*))?(\s*\[(?.*)\])?') ################################################################################ ## EXECUTION ################################################################################ func _execute() -> void: - match action: - Actions.JOIN: - if character: - if dialogic.has_subsystem('History') and !dialogic.Portraits.is_character_joined(character): - var character_name_text := dialogic.Text.get_character_name_parsed(character) - dialogic.History.store_simple_history_entry(character_name_text + " joined", event_name, {'character': character_name_text, 'mode':'Join'}) - - var final_animation_length: float = animation_length + if not character and not character_identifier == "--All--": + finish() + return - if dialogic.Inputs.auto_skip.enabled: - var max_time: float = dialogic.Inputs.auto_skip.time_per_event - final_animation_length = min(max_time, animation_length) + # Calculate animation time (can be shortened during skipping) + var final_animation_length: float = animation_length + var final_position_move_time: float = transform_time + if dialogic.Inputs.auto_skip.enabled: + var max_time: float = dialogic.Inputs.auto_skip.time_per_event + final_animation_length = min(max_time, animation_length) + final_position_move_time = min(max_time, transform_time) - await dialogic.Portraits.join_character( - character, portrait, position, - mirrored, z_index, extra_data, - animation_name, final_animation_length, animation_wait) - Actions.LEAVE: - var final_animation_length: float = animation_length + # JOIN ------------------------------------- + if action == Actions.JOIN: + if dialogic.has_subsystem('History') and !dialogic.Portraits.is_character_joined(character): + var character_name_text := dialogic.Text.get_character_name_parsed(character) + dialogic.History.store_simple_history_entry(character_name_text + " joined", event_name, {'character': character_name_text, 'mode':'Join'}) - if dialogic.Inputs.auto_skip.enabled: - var max_time: float = dialogic.Inputs.auto_skip.time_per_event - final_animation_length = min(max_time, animation_length) + await dialogic.Portraits.join_character( + character, portrait, transform, + mirrored, z_index, extra_data, + animation_name, final_animation_length, animation_wait) - if character_identifier == '--All--': + # LEAVE ------------------------------------- + elif action == Actions.LEAVE: + if character_identifier == '--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'}) - 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, + final_animation_length, + animation_wait + ) - await dialogic.Portraits.leave_all_characters( - animation_name, - final_animation_length, - animation_wait - ) + elif character: + if dialogic.has_subsystem('History') and dialogic.Portraits.is_character_joined(character): + var character_name_text := dialogic.Text.get_character_name_parsed(character) + dialogic.History.store_simple_history_entry(character_name_text+" left", event_name, {'character': character_name_text, 'mode':'Leave'}) - elif character: - if dialogic.has_subsystem('History') and dialogic.Portraits.is_character_joined(character): - var character_name_text := dialogic.Text.get_character_name_parsed(character) - dialogic.History.store_simple_history_entry(character_name_text+" left", event_name, {'character': character_name_text, 'mode':'Leave'}) + await dialogic.Portraits.leave_character( + character, + animation_name, + final_animation_length, + animation_wait + ) - await dialogic.Portraits.leave_character( - character, - animation_name, - final_animation_length, - animation_wait - ) + # UPDATE ------------------------------------- + elif action == Actions.UPDATE: + if not character or not dialogic.Portraits.is_character_joined(character): + finish() + return - Actions.UPDATE: - if !character or !dialogic.Portraits.is_character_joined(character): - finish() - return + dialogic.Portraits.change_character_extradata(character, extra_data) - if set_portrait: - dialogic.Portraits.change_character_portrait(character, portrait, false) + if set_portrait: + dialogic.Portraits.change_character_portrait(character, portrait, fade_animation, fade_length) - if set_mirrored: - dialogic.Portraits.change_character_mirror(character, mirrored) + if set_mirrored: + dialogic.Portraits.change_character_mirror(character, mirrored) - if set_z_index: - dialogic.Portraits.change_character_z_index(character, z_index) + if set_z_index: + dialogic.Portraits.change_character_z_index(character, z_index) - if set_position: - var final_position_move_time: float = position_move_time + if set_transform: + dialogic.Portraits.move_character(character, transform, final_position_move_time, transform_ease, transform_trans) - if dialogic.Inputs.auto_skip.enabled: - var max_time: float = dialogic.Inputs.auto_skip.time_per_event - final_position_move_time = min(max_time, position_move_time) + if animation_name: + var final_animation_repetitions: int = animation_repeats - dialogic.Portraits.move_character(character, position, final_position_move_time) - - if animation_name: - var final_animation_length: float = animation_length - var final_animation_repitions: int = animation_repeats + if dialogic.Inputs.auto_skip.enabled: + var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event + var time_for_repetitions: float = time_per_event / animation_repeats + final_animation_length = time_for_repetitions - if dialogic.Inputs.auto_skip.enabled: - var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event - var time_for_repitions: float = time_per_event / animation_repeats - final_animation_length = time_for_repitions + var animation := dialogic.Portraits.animate_character( + character, + animation_name, + final_animation_length, + final_animation_repetitions, + ) - var anim: DialogicAnimation = dialogic.Portraits.animate_character( - character, - animation_name, - final_animation_length, - final_animation_repitions - ) + if animation_wait: + dialogic.current_state = DialogicGameHandler.States.ANIMATING + await animation.finished + dialogic.current_state = DialogicGameHandler.States.IDLE - if animation_wait: - dialogic.current_state = DialogicGameHandler.States.ANIMATING - await anim.finished - dialogic.current_state = DialogicGameHandler.States.IDLE finish() -################################################################################ -## INITIALIZE -################################################################################ +#region INITIALIZE +############################################################################### func _init() -> void: event_name = "Character" @@ -175,15 +200,17 @@ func _init() -> void: func _get_icon() -> Resource: - return load(self.get_script().get_path().get_base_dir().path_join('icon_character.png')) + return load(self.get_script().get_path().get_base_dir().path_join('icon.svg')) -################################################################################ -## SAVING/LOADING +#endregion + +#region SAVING, LOADING, DEFAULTS ################################################################################ func to_text() -> String: var result_string := "" + # ACTIONS match action: Actions.JOIN: result_string += "join " Actions.LEAVE: result_string += "leave " @@ -191,137 +218,105 @@ func to_text() -> String: var default_values := DialogicUtil.get_custom_event_defaults(event_name) - if character or character_identifier == '--All--': - if action == Actions.LEAVE and character_identifier == '--All--': - result_string += "--All--" - else: - var name := DialogicResourceUtil.get_unique_identifier(character.resource_path) - if name.count(" ") > 0: - name = '"' + name + '"' - result_string += name - if portrait.strip_edges() != default_values.get('portrait', '') and action != Actions.LEAVE and (action != Actions.UPDATE or set_portrait): - result_string+= " ("+portrait+")" - - if action != Actions.LEAVE and (action != Actions.UPDATE or set_position): - result_string += " "+str(position) + # CHARACTER IDENTIFIER + if action == Actions.LEAVE and character_identifier == '--All--': + result_string += "--All--" + elif character: + var name := DialogicResourceUtil.get_unique_identifier(character.resource_path) - var shortcode := "[" - if animation_name: - shortcode += 'animation="'+animation_name+'"' + if name.count(" ") > 0: + name = '"' + name + '"' - if animation_length != default_values.get('animation_length', 0.5): - shortcode += ' length="'+str(animation_length)+'"' + result_string += name - if animation_wait != default_values.get('animation_wait', false): - shortcode += ' wait="'+str(animation_wait)+'"' + # PORTRAIT + if portrait.strip_edges() != default_values.get('portrait', ''): + if action != Actions.LEAVE and (action != Actions.UPDATE or set_portrait): + result_string += " (" + portrait + ")" - if animation_repeats != default_values.get('animation_repeats', 1) and action == Actions.UPDATE: - shortcode += ' repeat="'+str(animation_repeats)+'"' + # TRANSFORM + if action == Actions.JOIN or (action == Actions.UPDATE and set_transform): + result_string += " " + str(transform) - if z_index != default_values.get('z_index', 0) or (action == Actions.UPDATE and set_z_index): - shortcode += ' z_index="' + str(z_index) + '"' + # SETS: + if action == Actions.JOIN or action == Actions.LEAVE: + set_mirrored = mirrored != default_values.get("mirrored", false) + set_z_index = z_index != default_values.get("z_index", 0) - if mirrored != default_values.get('mirrored', false) or (action == Actions.UPDATE and set_mirrored): - shortcode += ' mirrored="' + str(mirrored) + '"' + var shortcode := store_to_shortcode_parameters() - if position_move_time != default_values.get('position_move_time', 0) and action == Actions.UPDATE and set_position: - shortcode += ' move_time="' + str(position_move_time) + '"' + if shortcode != "": + result_string += " [" + shortcode + "]" - if extra_data != "": - shortcode += ' extra_data="' + extra_data + '"' - - shortcode += "]" - - if shortcode != "[]": - result_string += " "+shortcode return result_string func from_text(string:String) -> void: - var character_directory := DialogicResourceUtil.get_character_directory() - - # load default character + # Load default character character = DialogicResourceUtil.get_character_resource(character_identifier) var result := regex.search(string) + # ACTION 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_identifier = '--All--' - else: - var name := result.get_string('name').strip_edges() - character = DialogicResourceUtil.get_character_resource(name) - - - if !result.get_string('portrait').is_empty(): - portrait = result.get_string('portrait').strip_edges().trim_prefix('(').trim_suffix(')') - set_portrait = true + "join": action = Actions.JOIN + "leave": action = Actions.LEAVE + "update": action = Actions.UPDATE - if result.get_string('position'): - position = int(result.get_string('position')) - set_position = true + # CHARACTER + var given_name := result.get_string('name').strip_edges() + var given_portrait := result.get_string('portrait').strip_edges() + var given_transform := result.get_string('transform').strip_edges() - if result.get_string('shortcode'): - var shortcode_params = parse_shortcode_parameters(result.get_string('shortcode')) - animation_name = shortcode_params.get('animation', '') - - var animLength = shortcode_params.get('length', '0.5').to_float() - if typeof(animLength) == TYPE_FLOAT: - animation_length = animLength + if given_name: + if action == Actions.LEAVE and given_name == "--All--": + character_identifier = '--All--' else: - animation_length = animLength.to_float() + character = DialogicResourceUtil.get_character_resource(given_name) - 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', animation_repeats)) - position_move_time = float(shortcode_params.get('move_time', position_move_time)) + # PORTRAIT + if given_portrait: + portrait = given_portrait.trim_prefix('(').trim_suffix(')') + set_portrait = true - #move time is only supported on Update, but it isnt part of the animations so its separate - if action == Actions.UPDATE: - position_move_time = float(shortcode_params.get('move_time', position_move_time)) + # TRANSFORM + if given_transform: + transform = given_transform + set_transform = true - z_index = int(shortcode_params.get('z_index', z_index)) - set_z_index = shortcode_params.has('z_index') + # SHORTCODE + if not result.get_string('shortcode'): + return - mirrored = DialogicUtil.str_to_bool(shortcode_params.get('mirrored', str(mirrored))) - set_mirrored = shortcode_params.has('mirrored') - extra_data = shortcode_params.get('extra_data', "") + load_from_shortcode_parameters(result.get_string('shortcode')) -## 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, + "action" : {"property": "action", "default": 0, "custom_stored":true, "suggestions": func(): return {'Join': {'value':Actions.JOIN}, 'Leave':{'value':Actions.LEAVE}, 'Update':{'value':Actions.UPDATE}}}, - "character" : {"property": "character_identifier", "default": ""}, - "portrait" : {"property": "portrait", "default": ""}, - "position" : {"property": "position", "default": 1}, + "character" : {"property": "character_identifier", "default": "", "custom_stored":true,}, + "portrait" : {"property": "portrait", "default": "", "custom_stored":true,}, + "transform" : {"property": "transform", "default": "center", "custom_stored":true,}, -# "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}, + "animation" : {"property": "animation_name", "default": ""}, + "length" : {"property": "animation_length", "default": 0.5}, + "wait" : {"property": "animation_wait", "default": false}, + "repeat" : {"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}, + "fade" : {"property": "fade_animation", "default":""}, + "fade_length" : {"property": "fade_length", "default":0.5}, + "move_time" : {"property": "transform_time", "default": 0.0}, + "move_ease" : {"property": "transform_ease", "default": Tween.EaseType.EASE_IN_OUT, + "suggestions": func(): return list_to_suggestions(ease_options)}, + "move_trans" : {"property": "transform_trans", "default": Tween.TransitionType.TRANS_SINE, + "suggestions": func(): return list_to_suggestions(trans_options)}, "extra_data" : {"property": "extra_data", "default": ""}, } @@ -331,6 +326,7 @@ func is_valid_event(string:String) -> bool: return true return false +#endregion ################################################################################ ## EDITOR REPRESENTATION @@ -363,7 +359,6 @@ func build_event_editor() -> void: '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_identifier != "--All--"') add_header_edit('set_portrait', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Character/update_portrait.svg"), @@ -374,14 +369,28 @@ func build_event_editor() -> void: 'suggestions_func' : get_portrait_suggestions, 'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")}, 'should_show_portrait_selector() and (action != Actions.UPDATE or set_portrait)') - add_header_edit('set_position', ValueType.BOOL_BUTTON, + add_header_edit('set_transform', ValueType.BOOL_BUTTON, {'icon': load("res://addons/dialogic/Modules/Character/update_position.svg"), 'tooltip':'Change Position'}, "character != null and !has_no_portraits() and action == Actions.UPDATE") add_header_label('at position', 'character != null and !has_no_portraits() and action == Actions.JOIN') - add_header_label('to position', 'character != null and !has_no_portraits() and action == Actions.UPDATE and set_position') - add_header_edit('position', ValueType.NUMBER, {'mode':1}, - 'character != null and !has_no_portraits() and action != %s and (action != Actions.UPDATE or set_position)' %Actions.LEAVE) + add_header_label('to position', 'character != null and !has_no_portraits() and action == Actions.UPDATE and set_transform') + add_header_edit('transform', ValueType.DYNAMIC_OPTIONS, + {'placeholder' : 'center', + 'mode' : 0, + 'suggestions_func' : get_position_suggestions, + 'tooltip' : "You can use a predefined position or a custom transform like 'pos=x0.5y1 size=x0.5y1 rot=10'.\nLearn more about this in the documentation."}, + 'character != null and !has_no_portraits() and action != %s and (action != Actions.UPDATE or set_transform)' %Actions.LEAVE) # Body + add_body_edit('fade_animation', ValueType.DYNAMIC_OPTIONS, + {'left_text' : 'Fade:', + 'suggestions_func' : get_fade_suggestions, + 'editor_icon' : ["Animation", "EditorIcons"], + 'placeholder' : 'Default', + 'enable_pretty_name' : true}, + 'should_show_fade_options()') + add_body_edit('fade_length', ValueType.NUMBER, {'left_text':'Length:', 'suffix':'s', "min":0}, + 'should_show_fade_options() and !fade_animation.is_empty()') + add_body_line_break("should_show_fade_options()") add_body_edit('animation_name', ValueType.DYNAMIC_OPTIONS, {'left_text' : 'Animation:', 'suggestions_func' : get_animation_suggestions, @@ -389,130 +398,148 @@ func build_event_editor() -> void: 'placeholder' : 'Default', 'enable_pretty_name' : true}, 'should_show_animation_options()') - add_body_edit('animation_length', ValueType.NUMBER, {'left_text':'Length:', 'suffix':'s'}, + add_body_edit('animation_length', ValueType.NUMBER, {'left_text':'Length:', 'suffix':'s', "min":0}, 'should_show_animation_options() and !animation_name.is_empty()') add_body_edit('animation_wait', ValueType.BOOL, {'left_text':'Await end:'}, 'should_show_animation_options() and !animation_name.is_empty()') - add_body_edit('animation_repeats', ValueType.NUMBER, {'left_text':'Repeat:', 'mode':1}, + add_body_edit('animation_repeats', ValueType.NUMBER, {'left_text':'Repeat:', 'mode':1, "min":1}, 'should_show_animation_options() and !animation_name.is_empty() and action == %s)' %Actions.UPDATE) add_body_line_break() - add_body_edit('position_move_time', ValueType.NUMBER, {'left_text':'Movement duration:'}, - 'action == %s and set_position' %Actions.UPDATE) + add_body_edit('transform_time', ValueType.NUMBER, {'left_text':'Movement duration:', "min":0}, + "should_show_transform_options()") + add_body_edit("transform_trans", ValueType.FIXED_OPTIONS, {'options':trans_options, 'left_text':"Trans:"}, 'should_show_transform_options() and transform_time > 0') + add_body_edit("transform_ease", ValueType.FIXED_OPTIONS, {'options':ease_options, 'left_text':"Ease:"}, 'should_show_transform_options() and transform_time > 0') + add_body_edit('set_z_index', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Character/update_z_index.svg"), 'tooltip':'Change Z-Index'}, "character != null and action == Actions.UPDATE") add_body_edit('z_index', ValueType.NUMBER, {'left_text':'Z-index:', 'mode':1}, 'action != %s and (action != Actions.UPDATE or set_z_index)' %Actions.LEAVE) add_body_edit('set_mirrored', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Character/update_mirror.svg"), 'tooltip':'Change Mirroring'}, "character != null and action == Actions.UPDATE") add_body_edit('mirrored', ValueType.BOOL, {'left_text':'Mirrored:'}, 'action != %s and (action != Actions.UPDATE or set_mirrored)' %Actions.LEAVE) + add_body_edit('extra_data', ValueType.SINGLELINE_TEXT, {'left_text':'Extra Data:'}, 'action != Actions.LEAVE') + + +func should_show_transform_options() -> bool: + return action == Actions.UPDATE and set_transform func should_show_animation_options() -> bool: - return (character != null and !character.portraits.is_empty()) or character_identifier == '--All--' + return (character and !character.portraits.is_empty()) or character_identifier == '--All--' + + +func should_show_fade_options() -> bool: + return action == Actions.UPDATE and set_portrait and character and not character.portraits.is_empty() + func should_show_portrait_selector() -> bool: - return character != null and len(character.portraits) > 1 and action != Actions.LEAVE + return character 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 - - var icon = load("res://addons/dialogic/Editor/Images/Resources/character.svg") - - suggestions['(No one)'] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} - var character_directory = DialogicResourceUtil.get_character_directory() - 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], 'icon': icon.duplicate()} - return suggestions + return DialogicUtil.get_character_suggestions(search_text, character, false, action == Actions.LEAVE, editor_node) 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"]} + var empty_text := "Don't Change" 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 + empty_text = "Default portrait" + return DialogicUtil.get_portrait_suggestions(search_text, character, true, empty_text) -func get_animation_suggestions(search_text:String) -> Dictionary: - var suggestions := {} +func get_position_suggestions(search_text:String='') -> Dictionary: + return DialogicUtil.get_portrait_position_suggestions(search_text) + +func get_animation_suggestions(search_text:String='') -> Dictionary: + var DPAU := DialogicPortraitAnimationUtil match action: - Actions.JOIN, Actions.LEAVE: - suggestions['Default'] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} + Actions.JOIN: + return DPAU.get_suggestions(search_text, animation_name, "Default", DPAU.AnimationType.IN) + Actions.LEAVE: + return DPAU.get_suggestions(search_text, animation_name, "Default", DPAU.AnimationType.OUT) Actions.UPDATE: - suggestions['None'] = {'value':"", 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} - - for anim in DialogicUtil.get_portrait_animation_scripts(action+1): - suggestions[DialogicUtil.pretty_name(anim)] = {'value':DialogicUtil.pretty_name(anim), 'editor_icon':["Animation", "EditorIcons"]} + return DPAU.get_suggestions(search_text, animation_name, "None", DPAU.AnimationType.ACTION) + return {} - return suggestions - -func _on_character_edit_pressed() -> void: - var editor_manager := _editor_node.find_parent('EditorsManager') - if editor_manager: - editor_manager.edit_resource(character) +func get_fade_suggestions(search_text:String='') -> Dictionary: + return DialogicPortraitAnimationUtil.get_suggestions(search_text, fade_animation, "Default", DialogicPortraitAnimationUtil.AnimationType.CROSSFADE) ####################### CODE COMPLETION ######################################## ################################################################################ -func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: - if symbol == ' ' and line.count(' ') == 1: +func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void: + var line_until_caret: String = CodeCompletionHelper.get_line_untill_caret(line) + if symbol == ' ' and line_until_caret.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 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.OUT) - 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: + var completion_character := regex.search(line).get_string('name') + CodeCompletionHelper.suggest_portraits(TextNode, completion_character) + + elif not '[' in line_until_caret and symbol == ' ': + if not line.begins_with("leave"): + for position in get_position_suggestions(): + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, position, position+' ', TextNode.syntax_highlighter.normal_color) + + # Shortcode Part + if '[' in line_until_caret: + # Suggest Parameters + if symbol == '[' or symbol == ' ' and line_until_caret.count('"')%2 == 0:# and (symbol == "[" or (symbol == " " and line_until_caret.rfind('="') < line_until_caret.rfind('"')-1)): + suggest_parameter("animation", line, TextNode) + + if "animation=" in line: + for param in ["length", "wait"]: + suggest_parameter(param, line, TextNode) + if line.begins_with('update'): + suggest_parameter("repeat", line, TextNode) + if line.begins_with("update"): + for param in ["move_time", "move_trans", "move_ease"]: + suggest_parameter(param, line, TextNode) + if not line.begins_with('leave'): + for param in ["mirrored", "z_index", "extra_data"]: + suggest_parameter(param, line, TextNode) + + # Suggest Values + else: + var current_param: RegExMatch = CodeCompletionHelper.completion_shortcode_param_getter_regex.search(line) + if not current_param: + return + + match current_param.get_string("param"): + "animation": + var animations := {} + if line.begins_with('join'): + animations = DialogicPortraitAnimationUtil.get_portrait_animations_filtered(DialogicPortraitAnimationUtil.AnimationType.IN) + elif line.begins_with('update'): + animations = DialogicPortraitAnimationUtil.get_portrait_animations_filtered(DialogicPortraitAnimationUtil.AnimationType.ACTION) + elif line.begins_with('leave'): + animations = DialogicPortraitAnimationUtil.get_portrait_animations_filtered(DialogicPortraitAnimationUtil.AnimationType.OUT) + + for script: String in animations: + TextNode.add_code_completion_option(CodeEdit.KIND_VARIABLE, DialogicUtil.pretty_name(script), DialogicUtil.pretty_name(script), TextNode.syntax_highlighter.normal_color, null, '" ') + + "wait", "mirrored": + CodeCompletionHelper.suggest_bool(TextNode, TextNode.syntax_highlighter.normal_color) + "move_trans": + CodeCompletionHelper.suggest_custom_suggestions(list_to_suggestions(trans_options), TextNode, TextNode.syntax_highlighter.normal_color) + "move_ease": + CodeCompletionHelper.suggest_custom_suggestions(list_to_suggestions(ease_options), TextNode, TextNode.syntax_highlighter.normal_color) + + +func suggest_parameter(parameter:String, line:String, TextNode:TextEdit) -> void: + if not parameter + "=" in line: + TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, parameter, parameter + '="', 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')) @@ -536,3 +563,13 @@ func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, li if result.get_string('shortcode'): dict = Highlighter.color_shortcode_content(dict, line, result.get_start('shortcode'), result.get_end('shortcode'), event_color) return dict + + +## HELPER +func list_to_suggestions(list:Array) -> Dictionary: + return list.reduce( + func(accum, value): + accum[value.label] = value + accum[value.label]["text_alt"] = [value.label.to_lower()] + return accum, + {}) diff --git a/addons/dialogic/Modules/Character/event_position.gd b/addons/dialogic/Modules/Character/event_position.gd deleted file mode 100644 index f46ae9af4..000000000 --- a/addons/dialogic/Modules/Character/event_position.gd +++ /dev/null @@ -1,110 +0,0 @@ -@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: - var final_movement_time: float = movement_time - - if dialogic.Inputs.auto_skip.enabled: - var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event - final_movement_time = max(movement_time, time_per_event) - - match action: - Actions.SET_RELATIVE: - dialogic.Portraits.move_portrait_position(position, vector, true, final_movement_time) - Actions.SET_ABSOLUTE: - dialogic.Portraits.move_portrait_position(position, vector, false, final_movement_time) - Actions.RESET_ALL: - dialogic.Portraits.reset_all_portrait_positions(final_movement_time) - Actions.RESET: - dialogic.Portraits.reset_portrait_position(position, final_movement_time) - - finish() - - -################################################################################ -## INITIALIZE -################################################################################ - -func _init() -> void: - event_name = "Position" - set_default_color('Color2') - event_category = "Other" - event_sorting_index = 2 - - -func _get_icon() -> Resource: - return load(self.get_script().get_path().get_base_dir().path_join('event_portrait_position.svg')) - -################################################################################ -## 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, 'text_alt':['set_relative', 'relative']}, "Set Absolute":{'value':1, 'text_alt':['set_absolute', 'absolute']}, "Reset":{'value':2,'text_alt':['reset'] }, "Reset All":{'value':3,'text_alt':['reset_all']}}}, - "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_OPTIONS, { - '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.NUMBER, {'left_text':"position", 'mode':1}, - '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.NUMBER, {'left_text':"AnimationTime:", "right_text":"(0 for instant)"}) diff --git a/addons/dialogic/Modules/Character/icon.svg b/addons/dialogic/Modules/Character/icon.svg new file mode 100644 index 000000000..66382274d --- /dev/null +++ b/addons/dialogic/Modules/Character/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/addons/dialogic/Modules/Character/icon.svg.import b/addons/dialogic/Modules/Character/icon.svg.import new file mode 100644 index 000000000..1c9857538 --- /dev/null +++ b/addons/dialogic/Modules/Character/icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://q6lanmf18ii6" +path="res://.godot/imported/icon.svg-4f340f6efbb83004dbd5c761dd1dc448.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Character/icon.svg" +dest_files=["res://.godot/imported/icon.svg-4f340f6efbb83004dbd5c761dd1dc448.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/Character/icon_character.png b/addons/dialogic/Modules/Character/icon_character.png deleted file mode 100644 index 585885474..000000000 Binary files a/addons/dialogic/Modules/Character/icon_character.png and /dev/null differ diff --git a/addons/dialogic/Modules/Character/index.gd b/addons/dialogic/Modules/Character/index.gd index 02fb5a0aa..805d62af8 100644 --- a/addons/dialogic/Modules/Character/index.gd +++ b/addons/dialogic/Modules/Character/index.gd @@ -3,12 +3,11 @@ extends DialogicIndexer func _get_events() -> Array: - return [this_folder.path_join('event_character.gd'), this_folder.path_join('event_position.gd')] + return [this_folder.path_join('event_character.gd')] func _get_subsystems() -> Array: - return [{'name':'Portraits', 'script':this_folder.path_join('subsystem_portraits.gd')}] - + return [{'name':'Portraits', 'script':this_folder.path_join('subsystem_portraits.gd')}, {'name':'PortraitContainers', 'script':this_folder.path_join('subsystem_containers.gd')}] func _get_settings_pages() -> Array: return [this_folder.path_join('settings_portraits.tscn')] @@ -17,5 +16,40 @@ func _get_text_effects() -> Array[Dictionary]: return [{'command':'portrait', 'subsystem':'Portraits', 'method':'text_effect_portrait', 'arg':true}] -func _get_special_resources() -> Array[Dictionary]: - return list_special_resources('DefaultAnimations', &'PortraitAnimation') +func _get_special_resources() -> Dictionary: + return {&'PortraitAnimation': list_animations("DefaultAnimations")} + + +func _get_portrait_scene_presets() -> Array[Dictionary]: + return [ + { + "path": "", + "name": "Default Scene", + "description": "The default scene defined in Settings>Portraits.", + "author":"Dialogic", + "type": "Default", + "icon":"", + "preview_image":[this_folder.path_join("default_portrait_thumbnail.png")], + "documentation":"", + }, + { + "path": "CUSTOM", + "name": "Custom Scene", + "description": "A custom scene. Should extend DialogicPortrait and be in @tool mode.", + "author":"Dialogic", + "type": "Custom", + "icon":"", + "preview_image":[this_folder.path_join("custom_portrait_thumbnail.png")], + "documentation":"https://docs.dialogic.pro/custom-portraits.html", + }, + { + "path": this_folder.path_join("default_portrait.tscn"), + "name": "Simple Image Portrait", + "description": "Can display images as portraits. Does nothing else.", + "author":"Dialogic", + "type": "General", + "icon":"", + "preview_image":[this_folder.path_join("simple_image_portrait_thumbnail.png")], + "documentation":"", + } + ] diff --git a/addons/dialogic/Modules/Character/node_portrait_container.gd b/addons/dialogic/Modules/Character/node_portrait_container.gd index 9a2307f36..5b483983b 100644 --- a/addons/dialogic/Modules/Character/node_portrait_container.gd +++ b/addons/dialogic/Modules/Character/node_portrait_container.gd @@ -2,18 +2,18 @@ class_name DialogicNode_PortraitContainer extends Control -## Node that defines a position for dialogic portraits and how to display portrait at that position. +## Node that defines a position for dialogic portraits and how to display portraits 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. + POSITION, ## This container can be joined/moved to with the Character Event + SPEAKER, ## This container 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 var container_ids: PackedStringArray = ["1"] @export_subgroup('Mode: Speaker') @@ -22,12 +22,17 @@ enum PositionModes { @export var portrait_prefix := '' @export_subgroup('Portrait Placement') -enum SizeModes {KEEP, FIT_STRETCH, FIT_IGNORE_SCALE, FIT_SCALE_HEIGHT} +enum SizeModes { + KEEP, ## The height and width of the container have no effect, only the origin. + FIT_STRETCH, ## The portrait will be fitted into the container, ignoring it's aspect ratio and the character/portrait scale. + FIT_IGNORE_SCALE, ## The portrait will be fitted into the container, ignoring the character/portrait scale, but preserving the aspect ratio. + FIT_SCALE_HEIGHT ## Recommended. The portrait will be scaled to fit the container height. A character/portrait scale of 100% means 100% container height. Aspect ratio will be preserved. + } ## Defines how to affect the scale of the portrait -@export var size_mode : SizeModes = SizeModes.FIT_SCALE_HEIGHT : +@export var size_mode: SizeModes = SizeModes.FIT_SCALE_HEIGHT : set(mode): size_mode = mode - _update_debug_portrait_size_position() + _update_debug_portrait_transform() ## If true, portraits will be mirrored in this position. @export var mirrored := false : @@ -39,7 +44,7 @@ enum SizeModes {KEEP, FIT_STRETCH, FIT_IGNORE_SCALE, FIT_SCALE_HEIGHT} @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 : +@export var origin_anchor: OriginAnchors = OriginAnchors.BOTTOM_CENTER : set(anchor): origin_anchor = anchor _update_debug_origin() @@ -50,27 +55,35 @@ enum OriginAnchors {TOP_LEFT, TOP_CENTER, TOP_RIGHT, LEFT_MIDDLE, CENTER, RIGHT_ origin_offset = offset _update_debug_origin() +enum PivotModes {AT_ORIGIN, PERCENTAGE, PIXELS} +## Usually you want to rotate or scale around the portrait origin. +## For the moments where that is not the case, set the mode to PERCENTAGE or PIXELS and use [member pivot_value]. +@export var pivot_mode: PivotModes = PivotModes.AT_ORIGIN +## Only has an effect when [member pivot_mode] is not AT_ORIGIN. Meaning depends on whether [member pivot_mode] is PERCENTAGE or PIXELS. +@export var pivot_value := Vector2() @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: +@export var debug_character: DialogicCharacter = null: set(character): debug_character = character _update_debug_portrait_scene() -@export var debug_character_portrait :String = "": +@export var debug_character_portrait := "": 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") +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")) +var ignore_resize := false + -func _ready(): +func _ready() -> void: match mode: PositionModes.POSITION: add_to_group('dialogic_portrait_con_position') @@ -85,7 +98,7 @@ func _ready(): debug_origin = Sprite2D.new() add_child(debug_origin) - debug_origin.texture = get_theme_icon("EditorPosition", "EditorIcons") + debug_origin.texture = load("res://addons/dialogic/Editor/Images/Dropdown/default.svg") _update_debug_origin() _update_debug_portrait_scene() @@ -97,18 +110,30 @@ func _ready(): ## MAIN METHODS ################################################################################ -func update_portrait_transforms(): +func update_portrait_transforms() -> void: + if ignore_resize: + return + + match pivot_mode: + PivotModes.AT_ORIGIN: + pivot_offset = _get_origin_position() + PivotModes.PERCENTAGE: + pivot_offset = size*pivot_value + PivotModes.PIXELS: + pivot_offset = pivot_value + for child in get_children(): - DialogicUtil.autoload().Portraits._update_portrait_transform(child) + DialogicUtil.autoload().Portraits._update_character_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 + 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: @@ -123,18 +148,31 @@ func get_local_portrait_transform(portrait_rect:Rect2, character_scale:=1.0) -> # 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 + 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 +func _get_origin_position(rect_size = null) -> Vector2: + if rect_size == null: + rect_size = size + return rect_size * Vector2(origin_anchor%3 / 2.0, floor(origin_anchor/3.0) / 2.0) + origin_offset + +func is_container(id:Variant) -> bool: + return str(id) in container_ids + +#region DEBUG METHODS ################################################################################ -## DEBUG METHODS -################################################################################ +### USE THIS TO DEBUG THE POSITIONS +#func _draw(): + #draw_rect(Rect2(Vector2(), size), Color(1, 0.3098039329052, 1), false, 2) + #draw_string(get_theme_default_font(),get_theme_default_font().get_string_size(container_ids[0], HORIZONTAL_ALIGNMENT_LEFT, 1, get_theme_default_font_size()) , container_ids[0], HORIZONTAL_ALIGNMENT_CENTER) +# +#func _process(delta:float) -> void: + #queue_redraw() + ## Loads the debug_character with the debug_character_portrait ## Creates a holder node and applies mirror @@ -145,35 +183,55 @@ func _update_debug_portrait_scene() -> void: for child in get_children(): if child != debug_origin: child.free() - + + # Get character var character := _get_debug_character() if not character is DialogicCharacter or character.portraits.is_empty(): return - + + # Determine portrait var debug_portrait := debug_character_portrait - if debug_portrait.is_empty(): debug_portrait = character.default_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 + if not debug_portrait in character.portraits: + debug_portrait = character.default_portrait + + var portrait_info: Dictionary = character.get_portrait_info(debug_portrait) + + # Determine scene + 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 + + # Load portrait + DialogicUtil.apply_scene_export_overrides(debug_character_scene_node, character.portraits[debug_portrait].get('export_overrides', {})) debug_character_scene_node._update_portrait(character, debug_portrait) + + # Add character node if !is_instance_valid(debug_character_holder_node): debug_character_holder_node = Node2D.new() add_child(debug_character_holder_node) + print(debug_character_holder_node) + + # Add portrait 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() + + _update_debug_portrait_transform() ## Set's the size and position of the holder and scene node ## according to the size_mode -func _update_debug_portrait_size_position() -> void: +func _update_debug_portrait_transform() -> 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() @@ -184,15 +242,18 @@ func _update_debug_portrait_size_position() -> void: debug_character_holder_node.scale = transform.size -## Updates the debug origins position. Also calls _update_debug_portrait_size_position() + +## Updates the debug origins position. Also calls _update_debug_portrait_transform() 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() + _update_debug_portrait_transform() ## 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 + +#endregion diff --git a/addons/dialogic/Modules/Character/event_portrait_position.svg b/addons/dialogic/Modules/Character/portrait_position.svg similarity index 100% rename from addons/dialogic/Modules/Character/event_portrait_position.svg rename to addons/dialogic/Modules/Character/portrait_position.svg diff --git a/addons/dialogic/Modules/Character/event_portrait_position.svg.import b/addons/dialogic/Modules/Character/portrait_position.svg.import similarity index 70% rename from addons/dialogic/Modules/Character/event_portrait_position.svg.import rename to addons/dialogic/Modules/Character/portrait_position.svg.import index 3755883b9..763036af5 100644 --- a/addons/dialogic/Modules/Character/event_portrait_position.svg.import +++ b/addons/dialogic/Modules/Character/portrait_position.svg.import @@ -3,7 +3,7 @@ importer="texture" type="CompressedTexture2D" uid="uid://bn3nq7gw67kye" -path="res://.godot/imported/event_portrait_position.svg-f91e8e0cc02545b0b28152d6ef70ff10.ctex" +path="res://.godot/imported/portrait_position.svg-f025767e40032b9ebdeab1f9e882467a.ctex" metadata={ "has_editor_variant": true, "vram_texture": false @@ -11,8 +11,8 @@ metadata={ [deps] -source_file="res://addons/dialogic/Modules/Character/event_portrait_position.svg" -dest_files=["res://.godot/imported/event_portrait_position.svg-f91e8e0cc02545b0b28152d6ef70ff10.ctex"] +source_file="res://addons/dialogic/Modules/Character/portrait_position.svg" +dest_files=["res://.godot/imported/portrait_position.svg-f025767e40032b9ebdeab1f9e882467a.ctex"] [params] diff --git a/addons/dialogic/Modules/Character/settings_portraits.gd b/addons/dialogic/Modules/Character/settings_portraits.gd index a637d9f45..5e13819ca 100644 --- a/addons/dialogic/Modules/Character/settings_portraits.gd +++ b/addons/dialogic/Modules/Character/settings_portraits.gd @@ -2,79 +2,94 @@ extends DialogicSettingsPage +const POSITION_SUGGESTION_KEY := 'dialogic/portraits/position_suggestion_names' + +const DEFAULT_PORTRAIT_SCENE_KEY := 'dialogic/portraits/default_portrait' + +const ANIMATION_JOIN_DEFAULT_KEY := 'dialogic/animations/join_default' +const ANIMATION_JOIN_DEFAULT_LENGTH_KEY := 'dialogic/animations/join_default_length' +const ANIMATION_JOIN_DEFAULT_WAIT_KEY := 'dialogic/animations/join_default_wait' +const ANIMATION_LEAVE_DEFAULT_KEY := 'dialogic/animations/leave_default' +const ANIMATION_LEAVE_DEFAULT_LENGTH_KEY := 'dialogic/animations/leave_default_length' +const ANIMATION_LEAVE_DEFAULT_WAIT_KEY := 'dialogic/animations/leave_default_wait' +const ANIMATION_CROSSFADE_DEFAULT_KEY := 'dialogic/animations/cross_fade_default' +const ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY:= 'dialogic/animations/cross_fade_default_length' + + func _ready(): %JoinDefault.get_suggestions_func = get_join_animation_suggestions %JoinDefault.mode = 1 %LeaveDefault.get_suggestions_func = get_leave_animation_suggestions %LeaveDefault.mode = 1 + %CrossFadeDefault.get_suggestions_func = get_crossfade_animation_suggestions + %CrossFadeDefault.mode = 1 + %PositionSuggestions.text_submitted.connect(save_setting.bind(POSITION_SUGGESTION_KEY)) + %CustomPortraitScene.value_changed.connect(save_setting_with_name.bind(DEFAULT_PORTRAIT_SCENE_KEY)) -func _refresh(): - %CustomPortraitScene.resource_icon = get_theme_icon("PackedScene", "EditorIcons") - %CustomPortraitScene.set_value(ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')) + %JoinDefault.value_changed.connect( + save_setting_with_name.bind(ANIMATION_JOIN_DEFAULT_KEY)) + %JoinDefaultLength.value_changed.connect( + save_setting.bind(ANIMATION_JOIN_DEFAULT_LENGTH_KEY)) + %JoinDefaultWait.toggled.connect( + save_setting.bind(ANIMATION_JOIN_DEFAULT_WAIT_KEY)) + %LeaveDefault.value_changed.connect( + save_setting_with_name.bind(ANIMATION_LEAVE_DEFAULT_KEY)) + %LeaveDefaultLength.value_changed.connect( + save_setting.bind(ANIMATION_LEAVE_DEFAULT_LENGTH_KEY)) + %LeaveDefaultWait.toggled.connect( + save_setting.bind(ANIMATION_LEAVE_DEFAULT_WAIT_KEY)) - %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) + %CrossFadeDefault.value_changed.connect( + save_setting_with_name.bind(ANIMATION_CROSSFADE_DEFAULT_KEY)) + %CrossFadeDefaultLength.value_changed.connect( + save_setting.bind(ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY)) -func _on_custom_portrait_scene_value_changed(property_name:String, value:String) -> void: - ProjectSettings.set_setting('dialogic/portraits/default_portrait', value) - ProjectSettings.save() +func _refresh(): + %PositionSuggestions.text = ProjectSettings.get_setting(POSITION_SUGGESTION_KEY, 'leftmost, left, center, right, rightmost') + %CustomPortraitScene.resource_icon = get_theme_icon(&"PackedScene", &"EditorIcons") + %CustomPortraitScene.set_value(ProjectSettings.get_setting(DEFAULT_PORTRAIT_SCENE_KEY, '')) -func _on_LeaveDefault_value_changed(property_name:String, value:String) -> void: - ProjectSettings.set_setting('dialogic/animations/leave_default', value) - ProjectSettings.save() + # JOIN + %JoinDefault.resource_icon = get_theme_icon(&"Animation", &"EditorIcons") + %JoinDefault.set_value(ProjectSettings.get_setting(ANIMATION_JOIN_DEFAULT_KEY, "Fade In Up")) + %JoinDefaultLength.set_value(ProjectSettings.get_setting(ANIMATION_JOIN_DEFAULT_LENGTH_KEY, 0.5)) + %JoinDefaultWait.button_pressed = ProjectSettings.get_setting(ANIMATION_JOIN_DEFAULT_WAIT_KEY, true) + # LEAVE + %LeaveDefault.resource_icon = get_theme_icon(&"Animation", &"EditorIcons") + %LeaveDefault.set_value(ProjectSettings.get_setting(ANIMATION_LEAVE_DEFAULT_KEY, "Fade Out Down")) + %LeaveDefaultLength.set_value(ProjectSettings.get_setting(ANIMATION_LEAVE_DEFAULT_LENGTH_KEY, 0.5)) + %LeaveDefaultWait.button_pressed = ProjectSettings.get_setting(ANIMATION_LEAVE_DEFAULT_WAIT_KEY, true) -func _on_JoinDefault_value_changed(property_name:String, value:String) -> void: - ProjectSettings.set_setting('dialogic/animations/join_default', value) - ProjectSettings.save() + # CROSS FADE + %CrossFadeDefault.resource_icon = get_theme_icon(&"Animation", &"EditorIcons") + %CrossFadeDefault.set_value(ProjectSettings.get_setting(ANIMATION_CROSSFADE_DEFAULT_KEY, "Fade Cross")) + %CrossFadeDefaultLength.set_value(ProjectSettings.get_setting(ANIMATION_CROSSFADE_DEFAULT_LENGTH_KEY, 0.5)) -func _on_JoinDefaultLength_value_changed(value:float) -> void: - ProjectSettings.set_setting('dialogic/animations/join_default_length', value) - ProjectSettings.save() +func save_setting_with_name(property_name:String, value:Variant, settings_key:String) -> void: + save_setting(value, settings_key) -func _on_LeaveDefaultLength_value_changed(value:float) -> void: - ProjectSettings.set_setting('dialogic/animations/leave_default_length', value) - ProjectSettings.save() - -func _on_JoinDefaultWait_toggled(button_pressed:bool) -> void: - ProjectSettings.set_setting('dialogic/animations/join_default_wait', button_pressed) - ProjectSettings.save() - -func _on_LeaveDefaultWait_toggled(button_pressed:bool) -> void: - ProjectSettings.set_setting('dialogic/animations/leave_default_wait', button_pressed) +func save_setting(value:Variant, settings_key:String) -> void: + ProjectSettings.set_setting(settings_key, value) 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 + return DialogicPortraitAnimationUtil.get_suggestions(search_text, %JoinDefault.current_value, "", DialogicPortraitAnimationUtil.AnimationType.IN) + 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 + return DialogicPortraitAnimationUtil.get_suggestions(search_text, %LeaveDefault.current_value, "", DialogicPortraitAnimationUtil.AnimationType.OUT) + + +func get_crossfade_animation_suggestions(search_text:String) -> Dictionary: + return DialogicPortraitAnimationUtil.get_suggestions(search_text, %CrossFadeDefault.current_value, "", DialogicPortraitAnimationUtil.AnimationType.CROSSFADE) + + diff --git a/addons/dialogic/Modules/Character/settings_portraits.tscn b/addons/dialogic/Modules/Character/settings_portraits.tscn index ad39f9174..5689200cd 100644 --- a/addons/dialogic/Modules/Character/settings_portraits.tscn +++ b/addons/dialogic/Modules/Character/settings_portraits.tscn @@ -1,53 +1,62 @@ -[gd_scene load_steps=7 format=3 uid="uid://cp463rpri5j8a"] +[gd_scene load_steps=5 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/field_options_dynamic.tscn" id="3"] [ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="3_m06d8"] -[sub_resource type="Image" id="Image_8p738"] -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_wre7v"] -image = SubResource("Image_8p738") - [node name="Portraits" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource("2") -[node name="Animations3" type="HBoxContainer" parent="."] +[node name="PositionsTitle" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.5 + +[node name="Title2" type="Label" parent="PositionsTitle"] +layout_mode = 2 +theme_type_variation = &"DialogicSettingsSection" +text = "Position Suggestions +" + +[node name="HintTooltip" parent="PositionsTitle" instance=ExtResource("2_dce78")] +layout_mode = 2 +tooltip_text = "You can change the position names that will be suggested in the timeline editor here." +texture = null +hint_text = "You can change the position names that will be suggested in the timeline editor here." + +[node name="PositionSuggestions" type="LineEdit" parent="."] +unique_name_in_owner = true +layout_mode = 2 + +[node name="DefaultSceneTitle" type="HBoxContainer" parent="."] layout_mode = 2 size_flags_horizontal = 3 size_flags_stretch_ratio = 0.5 -[node name="Title2" type="Label" parent="Animations3"] +[node name="Title2" type="Label" parent="DefaultSceneTitle"] layout_mode = 2 theme_type_variation = &"DialogicSettingsSection" text = "Default Portrait Scene " -[node name="HintTooltip" parent="Animations3" instance=ExtResource("2_dce78")] +[node name="HintTooltip" parent="DefaultSceneTitle" instance=ExtResource("2_dce78")] layout_mode = 2 tooltip_text = "If this is set, this scene will be what is used by default for any portrait that has no scene specified" -texture = SubResource("ImageTexture_wre7v") +texture = null hint_text = "If this is set, this scene will be what is used by default for any portrait that has no scene specified" -[node name="HBoxContainer" type="HBoxContainer" parent="."] +[node name="DefaultScene" type="HBoxContainer" parent="."] layout_mode = 2 -[node name="Label" type="Label" parent="HBoxContainer"] +[node name="Label" type="Label" parent="DefaultScene"] layout_mode = 2 text = "Scene" -[node name="CustomPortraitScene" parent="HBoxContainer" instance=ExtResource("3_m06d8")] +[node name="CustomPortraitScene" parent="DefaultScene" instance=ExtResource("3_m06d8")] unique_name_in_owner = true layout_mode = 2 file_filter = "*.tscn, *.scn; PortraitScene" @@ -66,15 +75,21 @@ 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_wre7v") -hint_text = "These settings are used for Leave and Join events if no animation is selected." +tooltip_text = "These settings are used for Leave and Join events if no animation is selected. + +The Cross-Fade will play if the portrait of a character changes and +no animation is set." +texture = null +hint_text = "These settings are used for Leave and Join events if no animation is selected. + +The Cross-Fade will play if the portrait of a character changes and +no animation is set." [node name="GridContainer" type="GridContainer" parent="."] layout_mode = 2 columns = 2 -[node name="Label3" type="Label" parent="GridContainer"] +[node name="DefaultJoinLabel" type="Label" parent="GridContainer"] layout_mode = 2 text = "Join" @@ -84,6 +99,7 @@ layout_mode = 2 [node name="JoinDefault" parent="GridContainer/DefaultIn" instance=ExtResource("3")] unique_name_in_owner = true layout_mode = 2 +mode = 1 [node name="JoinDefaultLength" type="SpinBox" parent="GridContainer/DefaultIn"] unique_name_in_owner = true @@ -95,7 +111,7 @@ unique_name_in_owner = true layout_mode = 2 text = "Wait:" -[node name="Label4" type="Label" parent="GridContainer"] +[node name="DefaultOutLabel" type="Label" parent="GridContainer"] layout_mode = 2 text = "Leave" @@ -105,6 +121,7 @@ layout_mode = 2 [node name="LeaveDefault" parent="GridContainer/DefaultOut" instance=ExtResource("3")] unique_name_in_owner = true layout_mode = 2 +mode = 1 [node name="LeaveDefaultLength" type="SpinBox" parent="GridContainer/DefaultOut"] unique_name_in_owner = true @@ -116,10 +133,28 @@ unique_name_in_owner = true layout_mode = 2 text = "Wait:" -[connection signal="value_changed" from="HBoxContainer/CustomPortraitScene" to="." method="_on_custom_portrait_scene_value_changed"] +[node name="CrossFadeLabel" type="Label" parent="GridContainer"] +layout_mode = 2 +text = "Cross-Fade" + +[node name="DefaultCrossFade" type="HBoxContainer" parent="GridContainer"] +layout_mode = 2 + +[node name="CrossFadeDefault" parent="GridContainer/DefaultCrossFade" instance=ExtResource("3")] +unique_name_in_owner = true +layout_mode = 2 +mode = 1 + +[node name="CrossFadeDefaultLength" type="SpinBox" parent="GridContainer/DefaultCrossFade"] +unique_name_in_owner = true +layout_mode = 2 +step = 0.1 + [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"] +[connection signal="value_changed" from="GridContainer/DefaultCrossFade/CrossFadeDefault" to="." method="_on_LeaveDefault_value_changed"] +[connection signal="value_changed" from="GridContainer/DefaultCrossFade/CrossFadeDefaultLength" to="." method="_on_LeaveDefaultLength_value_changed"] diff --git a/addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png b/addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png new file mode 100644 index 000000000..656215f70 Binary files /dev/null and b/addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png differ diff --git a/addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png.import b/addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png.import new file mode 100644 index 000000000..d049ed00d --- /dev/null +++ b/addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qihj11n7kx3m" +path="res://.godot/imported/simple_image_portrait_thumbnail.png-3d6c6de8187fd7911017e2ef9d3c40a4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/Character/simple_image_portrait_thumbnail.png" +dest_files=["res://.godot/imported/simple_image_portrait_thumbnail.png-3d6c6de8187fd7911017e2ef9d3c40a4.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/subsystem_containers.gd b/addons/dialogic/Modules/Character/subsystem_containers.gd new file mode 100644 index 000000000..ba968f131 --- /dev/null +++ b/addons/dialogic/Modules/Character/subsystem_containers.gd @@ -0,0 +1,284 @@ +extends DialogicSubsystem + +## Subsystem that manages portrait positions. + +signal position_changed(info: Dictionary) + + +var transform_regex := r"(?position|pos|size|siz|rotation|rot)\W*=(?((?!(pos|siz|rot)).)*)" + +#region STATE +#################################################################################################### + + +#endregion + + +#region MAIN METHODS +#################################################################################################### + +func get_container(position_id: String) -> DialogicNode_PortraitContainer: + for portrait_position:DialogicNode_PortraitContainer in get_tree().get_nodes_in_group(&'dialogic_portrait_con_position'): + if portrait_position.is_visible_in_tree() and portrait_position.is_container(position_id): + return portrait_position + return null + + +func get_containers(position_id: String) -> Array[DialogicNode_PortraitContainer]: + return get_tree().get_nodes_in_group(&'dialogic_portrait_con_position').filter( + func(node:DialogicNode_PortraitContainer): + return node.is_visible_in_tree() and node.is_container(position_id)) + + +func get_container_container() -> CanvasItem: + var any_portrait_container := get_tree().get_first_node_in_group(&'dialogic_portrait_con_position') + if any_portrait_container: + return any_portrait_container.get_parent() + return null + + +## 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_container(position_id: String) -> DialogicNode_PortraitContainer: + 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.name = "Portrait_"+position_id.validate_node_name() + copy_container_setup(example_position, new_position) + new_position.container_ids = [position_id] + position_changed.emit({&'change':'added', &'container_node':new_position, &'position_id':position_id}) + return new_position + return null + + +## Moves the [container] to the [destionation] (using [tween] and [time]). +## The destination can be a position_id (e.g. "center") or translation, roataion and scale. +## When moving to a preset container, then some more will be "copied" (e.g. anchors, etc.) +func move_container(container:DialogicNode_PortraitContainer, destination:String, tween:Tween = null, time:float=1.0) -> void: + var target_position: Vector2 = container.position + container._get_origin_position() + var target_rotation: float = container.rotation + var target_size: Vector2 = container.size + + var destination_container := get_container(destination) + if destination_container: + container.set_meta("target_container", destination_container) + target_position = destination_container.position + destination_container._get_origin_position() + target_rotation = destination_container.rotation_degrees + target_size = destination_container.size + else: + var regex := RegEx.create_from_string(transform_regex) + for found in regex.search_all(destination): + match found.get_string('part'): + 'pos', 'position': + target_position = str_to_vector(found.get_string("value"), target_position) + 'rot', 'rotation': + target_rotation = float(found.get_string("value")) + 'siz', 'size': + target_size = str_to_vector(found.get_string("value"), target_size) + + translate_container(container, target_position, false, tween, time) + rotate_container(container, target_rotation, false, tween, time) + resize_container(container, target_size, false, tween, time) + + if destination_container: + if time: + tween.finished.connect(func(): + if container.has_meta("target_container"): + if container.get_meta("target_container") == destination_container: + copy_container_setup(destination_container, container) + ) + else: + copy_container_setup(destination_container, container) + + +func copy_container_setup(from:DialogicNode_PortraitContainer, to:DialogicNode_PortraitContainer) -> void: + to.ignore_resize = true + to.layout_mode = from.layout_mode + to.anchors_preset = from.anchors_preset + to.anchor_bottom = from.anchor_bottom + to.anchor_left = from.anchor_left + to.anchor_right = from.anchor_right + to.anchor_top = from.anchor_top + to.offset_bottom = from.offset_bottom + to.offset_top = from.offset_top + to.offset_right = from.offset_right + to.offset_left = from.offset_left + to.size_mode = from.size_mode + to.origin_anchor = from.origin_anchor + to.ignore_resize = false + to.update_portrait_transforms() + + +## Translates the given container. +## The given translation should be the target position of the ORIGIN point, not the container! +func translate_container(container:DialogicNode_PortraitContainer, translation:Variant, relative := false, tween:Tween=null, time:float=1.0) -> void: + if !container.has_meta(&'default_translation'): + container.set_meta(&'default_translation', container.position + container._get_origin_position()) + + var final_translation: Vector2 + if typeof(translation) == TYPE_STRING: + final_translation = str_to_vector(translation, container.position+container._get_origin_position()) + elif typeof(translation) == TYPE_VECTOR2: + final_translation = translation + + if relative: + final_translation += container.position + else: + final_translation -= container._get_origin_position() + + if tween: + tween.tween_method(DialogicUtil.multitween.bind(container, "position", "base"), container.position, final_translation, time) + if not tween.finished.is_connected(save_position_container): + tween.finished.connect(save_position_container.bind(container)) + else: + container.position = final_translation + save_position_container(container) + position_changed.emit({&'change':'moved', &'container_node':container}) + + +func rotate_container(container:DialogicNode_PortraitContainer, rotation:float, relative := false, tween:Tween=null, time:float=1.0) -> void: + if !container.has_meta(&'default_rotation'): + container.set_meta(&'default_rotation', container.rotation_degrees) + + var final_rotation := rotation + + if relative: + final_rotation += container.rotation_degrees + + container.pivot_offset = container._get_origin_position() + + if tween: + tween.tween_property(container, 'rotation_degrees', final_rotation, time) + if not tween.finished.is_connected(save_position_container): + tween.finished.connect(save_position_container.bind(container)) + else: + container.rotation_degrees = final_rotation + save_position_container(container) + + position_changed.emit({&'change':'rotated', &'container_node':container}) + + +func resize_container(container: DialogicNode_PortraitContainer, rect_size: Variant, relative := false, tween:Tween=null, time:float=1.0) -> void: + if !container.has_meta(&'default_size'): + container.set_meta(&'default_size', container.size) + + var final_rect_resize: Vector2 + if typeof(rect_size) == TYPE_STRING: + final_rect_resize = str_to_vector(rect_size, container.size) + elif typeof(rect_size) == TYPE_VECTOR2: + final_rect_resize = rect_size + + if relative: + final_rect_resize += container.rect_size + + var relative_position_change := container._get_origin_position()-container._get_origin_position(final_rect_resize) + + if tween: + tween.tween_method(DialogicUtil.multitween.bind(container, "position", "resize_move"), Vector2(), relative_position_change, time) + tween.tween_property(container, 'size', final_rect_resize, time) + if not tween.finished.is_connected(save_position_container): + tween.finished.connect(save_position_container.bind(container)) + else: + container.position = container.position + relative_position_change + container.size = final_rect_resize + save_position_container(container) + + position_changed.emit({&'change':'resized', &'container_node':container}) + + +func save_position_container(container: DialogicNode_PortraitContainer) -> void: + if not dialogic.current_state_info.has('portrait_containers'): + dialogic.current_state_info['portrait_containers'] = {} + + var info := { + "container_ids" : container.container_ids, + "position" : container.position, + "rotation" : container.rotation_degrees, + "size" : container.size, + "pivot_mode" : container.pivot_mode, + "pivot_value" : container.pivot_value, + "origin_anchor" : container.origin_anchor, + "anchor_left" : container.anchor_left, + "anchor_right" : container.anchor_right, + "anchor_top" : container.anchor_top, + "anchor_bottom" : container.anchor_bottom, + "offset_left" : container.offset_left, + "offset_right" : container.offset_right, + "offset_top" : container.offset_top, + "offset_bottom" : container.offset_bottom, + } + + dialogic.current_state_info.portrait_containers[container.container_ids[0]] = info + + +func load_position_container(position_id: String) -> DialogicNode_PortraitContainer: + # First check whether the container already exists: + var container := get_container(position_id) + if container: + return container + + if not dialogic.current_state_info.has('portrait_containers') or not dialogic.current_state_info.portrait_containers.has(position_id): + return null + + var info: Dictionary = dialogic.current_state_info.portrait_containers[position_id] + container = add_container(position_id) + + if not container: + return null + + container.container_ids = info.container_ids + container.position = info.position + container.rotation = info.rotation + container.size = info.size + container.pivot_mode = info.pivot_mode + container.pivot_value = info.pivot_value + container.origin_anchor = info.origin_anchor + container.anchor_left = info.anchor_left + container.anchor_right = info.anchor_right + container.anchor_top = info.anchor_top + container.anchor_bottom = info.anchor_bottom + container.offset_left = info.offset_left + container.offset_right = info.offset_right + container.offset_top = info.offset_top + container.offset_bottom = info.offset_bottom + + return container + + +func str_to_vector(input: String, base_vector:=Vector2()) -> Vector2: + var vector_regex := RegEx.create_from_string(r"(?x|y)\s*(?(-|\+)?(\d|\.|)*)(\s*(?%|px))?") + var vec := base_vector + for i in vector_regex.search_all(input): + var value := float(i.get_string(&'number')) + match i.get_string(&'type'): + 'px': + pass # Keep values as they are + '%', _: + match i.get_string(&'part'): + 'x': value *= get_viewport().get_window().size.x + 'y': value *= get_viewport().get_window().size.y + + match i.get_string(&'part'): + 'x': vec.x = value + 'y': vec.y = value + return vec + + +func vector_to_str(vec:Vector2) -> String: + return "x" + str(vec.x) + "px y" + str(vec.y) + "px" + + +func reset_all_containers(time:= 0.0, tween:Tween = null) -> void: + for container in get_tree().get_nodes_in_group(&'dialogic_portrait_con_position'): + reset_container(container, time, tween) + + +func reset_container(container:DialogicNode_PortraitContainer, time := 0.0, tween: Tween = null ) -> void: + if container.has_meta(&'default_translation'): + translate_container(container, vector_to_str(container.get_meta(&'default_translation')), false, tween, time) + if container.has_meta(&'default_rotation'): + rotate_container(container, container.get_meta(&'default_rotation'), false, tween, time) + if container.has_meta(&'default_size'): + resize_container(container, vector_to_str(container.get_meta(&'default_size')), false, tween, time) diff --git a/addons/dialogic/Modules/Character/subsystem_portraits.gd b/addons/dialogic/Modules/Character/subsystem_portraits.gd index 6c836f2b5..b78abd21d 100644 --- a/addons/dialogic/Modules/Character/subsystem_portraits.gd +++ b/addons/dialogic/Modules/Character/subsystem_portraits.gd @@ -6,7 +6,9 @@ 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) + +## Emitted when a portrait starts animating. +#signal portrait_animating(character_node: Node, portrait_node: Node, animation_name: String, animation_length: float) ## The default portrait scene. @@ -16,23 +18,29 @@ var default_portrait_scene: PackedScene = load(get_script().resource_path.get_ba #region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: +func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: for character in dialogic.current_state_info.get('portraits', {}).keys(): remove_character(load(character)) dialogic.current_state_info['portraits'] = {} -func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void: +func load_game_state(_load_flag:=LoadFlags.FULL_LOAD) -> void: + if not "portraits" in dialogic.current_state_info: + dialogic.current_state_info["portraits"] = {} + + # Load Position Portraits var portraits_info: Dictionary = dialogic.current_state_info.portraits.duplicate() dialogic.current_state_info.portraits = {} for character_path in portraits_info: var character_info: Dictionary = portraits_info[character_path] - await join_character(load(character_path), character_info.portrait, - character_info.position_index, - character_info.get('custom_mirror', false), - character_info.get('z_index', 0), - character_info.get('extra_data', ""), - "InstantInOrOut", 0, false) + var character: DialogicCharacter = load(character_path) + var container := dialogic.PortraitContainers.load_position_container(character.get_character_name()) + add_character(character, container, character_info.portrait, character_info.position_id) + change_character_mirror(character, character_info.get('custom_mirror', false)) + change_character_z_index(character, character_info.get('z_index', 0)) + change_character_extradata(character, character_info.get('extra_data', "")) + + # Load Speaker Portrait var speaker: Variant = dialogic.current_state_info.get('speaker', "") if speaker: dialogic.current_state_info['speaker'] = "" @@ -56,8 +64,6 @@ func _ready() -> void: if !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty(): default_portrait_scene = load(ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')) -#endregion - #region MAIN METHODS #################################################################################################### @@ -72,8 +78,11 @@ func _ready() -> void: ## 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: + if container == null: + return null var character_node := Node2D.new() character_node.name = character.get_character_name() character_node.set_meta('character', character) @@ -81,9 +90,10 @@ func _create_character_node(character:DialogicCharacter, container:DialogicNode_ return character_node -# Changes the portrait of a specific [character node]. -func _change_portrait(character_node:Node2D, portrait:String, update_transform:=true) -> Dictionary: +## Changes the portrait of a specific [character node]. +func _change_portrait(character_node: Node2D, portrait: String, fade_animation:="", fade_length := 0.5) -> Dictionary: var character: DialogicCharacter = character_node.get_meta('character') + if portrait.is_empty(): portrait = character.default_portrait @@ -93,27 +103,29 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= print_debug('[Dialogic] Change to not-existing portrait will be ignored!') return info - # path to the scene to use + # 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) + var previous_portrait: Node = null + var portrait_count := character_node.get_child_count() + + if portrait_count > 0: + previous_portrait = character_node.get_child(-1) + + # Check if the scene is the same as the currently loaded scene. + if (not previous_portrait == null and + previous_portrait.get_meta('scene', '') == scene_path and + # Also check if the scene supports changing to the given portrait. + previous_portrait._should_do_portrait_update(character, portrait)): + portrait_node = previous_portrait 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 ResourceLoader.exists(scene_path): var packed_scene: PackedScene = load(scene_path) + if packed_scene: portrait_node = packed_scene.instantiate() else: @@ -124,7 +136,9 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= portrait_node.set_meta('scene', scene_path) + if portrait_node: + portrait_node.set_meta('portrait', portrait) character_node.set_meta('portrait', portrait) DialogicUtil.apply_scene_export_overrides(portrait_node, character.portraits[portrait].get('export_overrides', {})) @@ -132,11 +146,19 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= if portrait_node.has_method('_update_portrait'): portrait_node._update_portrait(character, portrait) - if !portrait_node.is_inside_tree(): + if not portrait_node.is_inside_tree(): character_node.add_child(portrait_node) - if update_transform: - _update_portrait_transform(character_node) + _update_portrait_transform(portrait_node) + + ## Handle Cross-Animating + if previous_portrait and previous_portrait != portrait_node: + if not fade_animation.is_empty() and fade_length > 0: + var fade_out := _animate_node(previous_portrait, fade_animation, fade_length, 1, true) + var _fade_in := _animate_node(portrait_node, fade_animation, fade_length, 1, false) + fade_out.finished.connect(previous_portrait.queue_free) + else: + previous_portrait.queue_free() return info @@ -144,84 +166,106 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= ## 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') +func _change_portrait_mirror(character_node: Node2D, mirrored := false, force := false) -> void: + var latest_portrait := character_node.get_child(-1) + + if latest_portrait.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))) + latest_portrait._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: + var latest_portrait := character_node.get_child(-1) + if latest_portrait.has_method('_set_extra_data'): + latest_portrait._set_extra_data(extra_data) -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_character_transform(character_node:Node, time := 0.0) -> void: + for child in character_node.get_children(): + _update_portrait_transform(child, time) -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'), {}) +func _update_portrait_transform(portrait_node: Node, time:float = 0.0) -> void: + var character_node: Node = portrait_node.get_parent() + + var character: DialogicCharacter = character_node.get_meta('character') + var portrait_info: Dictionary = character.portraits.get(portrait_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 apply_character_scale: bool = not 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', 1)*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') + tween.stop() 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: + if not 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_method(DialogicUtil.multitween.bind(character_node, "position", "base"), 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() +## Animates the node with the given animation. +## Is used both on the character node (most animations) and the portrait nodes (cross-fade animations) +func _animate_node(node: Node, animation_path: String, length: float, repeats := 1, is_reversed := false) -> DialogicAnimation: + if node.has_meta('animation_node') and is_instance_valid(node.get_meta('animation_node')): + 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.node = node + anim_node.base_position = node.position + anim_node.base_scale = node.scale anim_node.time = length anim_node.repeats = repeats + anim_node.is_reversed = is_reversed + add_child(anim_node) anim_node.animate() - character_node.set_meta('animation_node', anim_node) - return anim_node + node.set_meta("animation_path", animation_path) + node.set_meta("animation_length", length) + node.set_meta("animation_node", anim_node) + #if not is_silent: + #portrait_animating.emit(portrait_node.get_parent(), portrait_node, animation_path, length) -## Moves the given portrait to the given container. -func _move_portrait(character_node:Node2D, portrait_container:DialogicNode_PortraitContainer, time:= 0.0) -> void: + return anim_node - 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) +## Moves the given portrait to the given container. +func _move_character(character_node: Node2D, transform:="", time := 0.0, easing:= Tween.EASE_IN_OUT, trans:= Tween.TRANS_SINE) -> void: + var tween := character_node.create_tween().set_ease(easing).set_trans(trans).set_parallel() + if time == 0: + tween.kill() + tween = null + var container: DialogicNode_PortraitContainer = character_node.get_parent() + dialogic.PortraitContainers.move_container(container, transform, tween, time) - character_node.position = global_pos-character_node.get_parent().global_position - _update_portrait_transform(character_node, time) + for portrait_node in character_node.get_children(): + _update_portrait_transform(portrait_node, time) ## Changes the given portraits z_index. -func _change_portrait_z_index(character_node:Node2D, z_index:int, update_zindex:= true) -> void: +func _change_portrait_z_index(character_node: Node, z_index:int, update_zindex:= true) -> void: if update_zindex: character_node.get_parent().set_meta('z_index', z_index) @@ -233,15 +277,33 @@ func _change_portrait_z_index(character_node:Node2D, z_index:int, update_zindex: idx += 1 -func z_sort_portrait_containers(con1:DialogicNode_PortraitContainer, con2:DialogicNode_PortraitContainer) -> bool: +## Checks if [para, character] has joined the scene, if so, returns its +## active [DialogicPortrait] node. +## +## The difference between an active and inactive nodes is whether the node is +## the latest node. [br] +## If a portrait is fading/animating from portrait A and B, both will exist +## in the scene, but only the new portrait is active, even if it is not +## fully visible yet. +func get_character_portrait(character: DialogicCharacter) -> DialogicPortrait: + if is_character_joined(character): + var portrait_node: DialogicPortrait = dialogic.current_state_info['portraits'][character.resource_path].node.get_child(-1) + return portrait_node + + return null + + +func z_sort_portrait_containers(con1: DialogicNode_PortraitContainer, con2: DialogicNode_PortraitContainer) -> bool: if con1.get_meta('z_index', 0) < con2.get_meta('z_index', 0): return true + return false -func _remove_portrait(character_node:Node2D) -> void: - character_node.get_parent().remove_child(character_node) - character_node.queue_free() +## Private method to remove a [param portrait_node]. +func _remove_portrait(portrait_node: Node) -> void: + portrait_node.get_parent().remove_child(portrait_node) + portrait_node.queue_free() ## Gets the default animation length for joining characters @@ -265,6 +327,30 @@ func _get_leave_default_length() -> float: return default_time + +## Checks multiple cases to return a valid portrait to use. +func get_valid_portrait(character:DialogicCharacter, portrait:String) -> String: + if character == null: + printerr('[Dialogic] Tried to use portrait "', portrait, '" on character.') + dialogic.print_debug_moment() + return "" + + if "{" in portrait and dialogic.has_subsystem("Expressions"): + var test: Variant = dialogic.Expressions.execute_string(portrait) + if test: + portrait = str(test) + + if not portrait in character.portraits: + if not portrait.is_empty(): + printerr('[Dialogic] Tried to use invalid portrait "', portrait, '" on character "', DialogicResourceUtil.get_unique_identifier(character.resource_path), '". Using default portrait instead.') + dialogic.print_debug_moment() + portrait = character.default_portrait + + if portrait.is_empty(): + portrait = character.default_portrait + + return portrait + #endregion @@ -277,24 +363,27 @@ func _get_leave_default_length() -> float: ## 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:= false, z_index:= 0, extra_data:= "", animation_name:= "", animation_length:= 0.0, animation_wait := false) -> Node: +func join_character(character:DialogicCharacter, portrait:String, position_id:String, mirrored:= false, z_index:= 0, extra_data:= "", animation_name:= "", animation_length:= 0.0, animation_wait := false) -> Node: if is_character_joined(character): change_character_portrait(character, portrait) + if animation_name.is_empty(): animation_length = _get_join_default_length() + if animation_wait: dialogic.current_state = DialogicGameHandler.States.ANIMATING await get_tree().create_timer(animation_length).timeout dialogic.current_state = DialogicGameHandler.States.IDLE - move_character(character, position_idx, animation_length) + move_character(character, position_id, animation_length) change_character_mirror(character, mirrored) return - var character_node := add_character(character, portrait, position_idx) + var container := dialogic.PortraitContainers.add_container(character.get_character_name()) + var character_node := add_character(character, container, portrait, position_id) 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} + dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_id':position_id, 'custom_mirror':mirrored} _change_portrait_mirror(character_node, mirrored) _change_portrait_extradata(character_node, extra_data) @@ -309,11 +398,10 @@ func join_character(character:DialogicCharacter, portrait:String, position_idx: animation_length = _get_join_default_length() animation_wait = ProjectSettings.get_setting('dialogic/animations/join_default_wait', true) - animation_name = DialogicResourceUtil.guess_special_resource("PortraitAnimation", animation_name, "") + animation_name = DialogicPortraitAnimationUtil.guess_animation(animation_name, DialogicPortraitAnimationUtil.AnimationType.IN) if animation_name and animation_length > 0: - var anim: DialogicAnimation = _animate_portrait(character_node, animation_name, animation_length) - + var anim: DialogicAnimation = _animate_node(character_node, animation_name, animation_length) if animation_wait: dialogic.current_state = DialogicGameHandler.States.ANIMATING await anim.finished @@ -322,52 +410,52 @@ func join_character(character:DialogicCharacter, portrait:String, position_idx: return character_node -func add_character(character:DialogicCharacter, portrait:String, position_idx:int) -> Node: +func add_character(character:DialogicCharacter, container: DialogicNode_PortraitContainer, portrait:String, position_id:String) -> 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 + portrait = get_valid_portrait(character, portrait) + if portrait.is_empty(): - portrait = character.default_portrait + return null 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 + var character_node := _create_character_node(character, container) if character_node == null: - printerr('[Dialogic] Failed to join character to position ', position_idx, ". Could not find position container.") + printerr('[Dialogic] Failed to join character to position ', position_id, ". Could not find position container.") return null - dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_index':position_idx} + dialogic.current_state_info['portraits'][character.resource_path] = {'portrait':portrait, 'node':character_node, 'position_id':position_id} + + _move_character(character_node, position_id) _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): +func change_character_portrait(character: DialogicCharacter, portrait: String, fade_animation:="DEFAULT", fade_length := -1.0) -> void: + if not is_character_joined(character): return + portrait = get_valid_portrait(character, portrait) + 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) + if fade_animation == "DEFAULT": + fade_animation = ProjectSettings.get_setting('dialogic/animations/cross_fade_default', "Fade Cross") + fade_length = ProjectSettings.get_setting('dialogic/animations/cross_fade_default_length', 0.5) + + fade_animation = DialogicPortraitAnimationUtil.guess_animation(fade_animation, DialogicPortraitAnimationUtil.AnimationType.CROSSFADE) + + var info := _change_portrait(dialogic.current_state_info.portraits[character.resource_path].node, portrait, fade_animation, fade_length) dialogic.current_state_info.portraits[character.resource_path].portrait = info.portrait _change_portrait_mirror( dialogic.current_state_info.portraits[character.resource_path].node, @@ -389,6 +477,7 @@ func change_character_mirror(character:DialogicCharacter, mirrored:= false, forc 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) if update_zindex: dialogic.current_state_info.portraits[character.resource_path]['z_index'] = z_index @@ -403,105 +492,115 @@ func change_character_extradata(character:DialogicCharacter, 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): +func animate_character(character: DialogicCharacter, animation_path: String, length: float, repeats := 1, is_reversed := false) -> DialogicAnimation: + if not is_character_joined(character): return null - animation_path = DialogicResourceUtil.guess_special_resource("PortraitAnimation", animation_path, "") + animation_path = DialogicPortraitAnimationUtil.guess_animation(animation_path) - return _animate_portrait(dialogic.current_state_info.portraits[character.resource_path].node, animation_path, length, repeats) + var character_node: Node = dialogic.current_state_info.portraits[character.resource_path].node + + return _animate_node(character_node, animation_path, length, repeats, is_reversed) ## Moves the given character to the given position. Only works with joined characters -func move_character(character:DialogicCharacter, position_idx:int, time:= 0.0) -> void: +func move_character(character:DialogicCharacter, position_id:String, time:= 0.0, easing:=Tween.EASE_IN_OUT, trans:=Tween.TRANS_SINE) -> void: if !is_character_joined(character): return - if dialogic.current_state_info.portraits[character.resource_path].position_index == position_idx: + if dialogic.current_state_info.portraits[character.resource_path].position_id == position_id: 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.") + _move_character(dialogic.current_state_info.portraits[character.resource_path].node, position_id, time, easing, trans) + dialogic.current_state_info.portraits[character.resource_path].position_id = position_id + character_moved.emit({'character':character, 'position_id':position_id, 'time':time}) ## Removes a character with a given animation or the default animation. -func leave_character(character:DialogicCharacter, animation_name:= "", animation_length:= 0.0, animation_wait := false) -> void: - if !is_character_joined(character): +func leave_character(character: DialogicCharacter, animation_name:= "", animation_length:= 0.0, animation_wait := false) -> void: + if not 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_name = ProjectSettings.get_setting('dialogic/animations/leave_default', "Fade Out Down") animation_length = _get_leave_default_length() animation_wait = ProjectSettings.get_setting('dialogic/animations/leave_default_wait', true) - animation_name = DialogicResourceUtil.guess_special_resource("PortraitAnimation", animation_name, "") - - if !animation_name.is_empty(): - var anim := animate_character(character, animation_name, animation_length) + animation_name = DialogicPortraitAnimationUtil.guess_animation(animation_name, DialogicPortraitAnimationUtil.AnimationType.OUT) - anim.finished.connect(remove_character.bind(character)) + if not animation_name.is_empty(): + var character_node := get_character_node(character) - if animation_wait: - dialogic.current_state = DialogicGameHandler.States.ANIMATING - await anim.finished - dialogic.current_state = DialogicGameHandler.States.IDLE - else: - remove_character(character) + var animation := _animate_node(character_node, animation_name, animation_length, 1, true) + if animation_length > 0: + if animation_wait: + dialogic.current_state = DialogicGameHandler.States.ANIMATING + await animation.finished + dialogic.current_state = DialogicGameHandler.States.IDLE + remove_character(character) + else: + animation.finished.connect(func(): remove_character(character)) + else: + remove_character(character) ## Removes all joined characters with a given animation or the default animation. -func leave_all_characters(animation_name:="", animation_length:=0.0, animation_wait:= false) -> void: +func leave_all_characters(animation_name:="", animation_length:=0.0, animation_wait := false) -> void: for character in get_joined_characters(): - leave_character(character, animation_name, animation_length, false) + await leave_character(character, animation_name, animation_length, animation_wait) - if animation_name.is_empty(): - animation_length = _get_leave_default_length() - animation_wait = ProjectSettings.get_setting('dialogic/animations/leave_default_wait', true) - if animation_wait: - dialogic.current_state = DialogicGameHandler.States.ANIMATING - await get_tree().create_timer(animation_length).timeout - dialogic.current_state = DialogicGameHandler.States.IDLE +## Finds the character node for a [param character]. +## Return `null` if the [param character] is not part of the scene. +func get_character_node(character: DialogicCharacter) -> Node: + if is_character_joined(character): + if is_instance_valid(dialogic.current_state_info['portraits'][character.resource_path].node): + return dialogic.current_state_info['portraits'][character.resource_path].node + return null -## Removes the given characters portrait. Only works with joined characters -func remove_character(character:DialogicCharacter) -> void: - if !is_character_joined(character): - return - if is_instance_valid(dialogic.current_state_info['portraits'][character.resource_path].node) and \ - 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}) +## Removes the given characters portrait. +## Only works with joined characters. +func remove_character(character: DialogicCharacter) -> void: + var character_node := get_character_node(character) + + if is_instance_valid(character_node) and character_node is Node: + var container := character_node.get_parent() + container.get_parent().remove_child(container) + container.queue_free() + character_node.queue_free() + character_left.emit({'character': character}) + dialogic.current_state_info['portraits'].erase(character.resource_path) +func get_current_character() -> DialogicCharacter: + if dialogic.current_state_info.get('speaker', null): + return load(dialogic.current_state_info.speaker) + return null + + + ## Returns true if the given character is currently joined. -func is_character_joined(character:DialogicCharacter) -> bool: - if not character or !character.resource_path in dialogic.current_state_info['portraits']: +func is_character_joined(character: DialogicCharacter) -> bool: + if character == null or not character.resource_path in dialogic.current_state_info['portraits']: return false - if dialogic.current_state_info['portraits'][character.resource_path].get('node', null) != null and \ - is_instance_valid(dialogic.current_state_info['portraits'][character.resource_path].node): - return true - return false + + return true ## 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(): + var chars: Array[DialogicCharacter] = [] + + for char_path: String 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] +## Keys can be [joined, character, node (for the portrait node), position_id] ## Only joined is included (and false) for not joined characters func get_character_info(character:DialogicCharacter) -> Dictionary: if is_character_joined(character): @@ -514,105 +613,78 @@ func get_character_info(character:DialogicCharacter) -> Dictionary: #endregion -#region Positions -#################################################################################################### - -func get_portrait_container(postion_index:int) -> DialogicNode_PortraitContainer: - 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 == postion_index: - return portrait_position - return null - - -## 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:= false, time:= 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_all_portrait_positions(time:= 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:= 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) - -#endregion - - #region 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) +func change_speaker(speaker: DialogicCharacter = null, portrait := "") -> void: + for container: Node in get_tree().get_nodes_in_group('dialogic_portrait_con_speaker'): + var just_joined := true + for character_node: Node in container.get_children(): + if not character_node.get_meta('character') == speaker: + var leave_animation: String = ProjectSettings.get_setting('dialogic/animations/leave_default', "Fade Out") + leave_animation = DialogicPortraitAnimationUtil.guess_animation(leave_animation, DialogicPortraitAnimationUtil.AnimationType.OUT) + var leave_animation_length := _get_leave_default_length() + + if leave_animation and leave_animation_length: + var animate_out := _animate_node(character_node, leave_animation, leave_animation_length, 1, true) + animate_out.finished.connect(character_node.queue_free) + else: + character_node.get_parent().remove_child(character_node) + character_node.queue_free() + else: + just_joined = false if speaker == null or speaker.portraits.is_empty(): continue - if con.get_children().is_empty(): - _create_character_node(speaker, con) + if just_joined: + _create_character_node(speaker, container) + elif portrait.is_empty(): continue 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) + var fade_animation: String = ProjectSettings.get_setting('dialogic/animations/cross_fade_default', "Fade Cross") + var fade_length: float = ProjectSettings.get_setting('dialogic/animations/cross_fade_default_length', 0.5) + + fade_animation = DialogicPortraitAnimationUtil.guess_animation(fade_animation, DialogicPortraitAnimationUtil.AnimationType.CROSSFADE) + + if container.portrait_prefix+portrait in speaker.portraits: + portrait = container.portrait_prefix+portrait + + _change_portrait(container.get_child(-1), portrait, fade_animation, fade_length) # if the character has no portraits _change_portrait won't actually add a child node - if con.get_child(0).get_child_count() == 0: + if container.get_child(-1).get_child_count() == 0: continue - _change_portrait_mirror(con.get_child(0)) + if just_joined: + # Change speaker is called before the text is changed. + # In styles where the speaker is IN the textbox, + # this can mean the portrait container isn't sized correctly yet. + if not container.is_visible_in_tree(): + await get_tree().process_frame + var join_animation: String = ProjectSettings.get_setting('dialogic/animations/join_default', "Fade In Up") + join_animation = DialogicPortraitAnimationUtil.guess_animation(join_animation, DialogicPortraitAnimationUtil.AnimationType.IN) + var join_animation_length := _get_join_default_length() + + if join_animation and join_animation_length: + _animate_node(container.get_child(-1), join_animation, join_animation_length) + + _change_portrait_mirror(container.get_child(-1)) if speaker: if speaker.resource_path != dialogic.current_state_info['speaker']: if dialogic.current_state_info['speaker'] and is_character_joined(load(dialogic.current_state_info['speaker'])): - dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(0)._unhighlight() + dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(-1)._unhighlight() + if speaker and is_character_joined(speaker): - dialogic.current_state_info['portraits'][speaker.resource_path].node.get_child(0)._highlight() + dialogic.current_state_info['portraits'][speaker.resource_path].node.get_child(-1)._highlight() + elif dialogic.current_state_info['speaker'] and is_character_joined(load(dialogic.current_state_info['speaker'])): - dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(0)._unhighlight() + dialogic.current_state_info['portraits'][dialogic.current_state_info['speaker']].node.get_child(-1)._unhighlight() #endregion @@ -621,7 +693,7 @@ func change_speaker(speaker:DialogicCharacter = null, portrait:= ""): #################################################################################################### ## Called from the [portrait=something] text effect. -func text_effect_portrait(text_node:Control, skipped:bool, argument:String) -> void: +func text_effect_portrait(_text_node:Control, _skipped:bool, argument:String) -> void: if argument: if dialogic.current_state_info.get('speaker', null): change_character_portrait(load(dialogic.current_state_info.speaker), argument) diff --git a/addons/dialogic/Modules/Choice/event_choice.gd b/addons/dialogic/Modules/Choice/event_choice.gd index 1eef08c14..7e4364c32 100644 --- a/addons/dialogic/Modules/Choice/event_choice.gd +++ b/addons/dialogic/Modules/Choice/event_choice.gd @@ -9,25 +9,32 @@ enum ElseActions {HIDE=0, DISABLE=1, DEFAULT=2} ### Settings ## The text that is displayed on the choice button. -var text :String = "" +var text := "" ## If not empty this condition will determine if this choice is active. -var condition: String = "" +var condition := "" ## Determines what happens if [condition] is false. Default will use the action set in the settings. -var else_action: = ElseActions.DEFAULT +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 = "" +var disabled_text := "" +## A dictionary that can be filled with arbitrary information +## This can then be interpreted by a custom choice layer +var extra_data := {} + + +## UI helper +var _has_condition := false #endregion +var regex := RegEx.create_from_string(r'- (?(?>\\\||(?(?=.*\|)[^|]|(?!\[if)[^|]))*)\|?\s*(\[if(?([^\]\[]|\[[^\]]*\])+)\])?\s*(\[(?[^]]*)\])?') #region EXECUTION ################################################################################ func _execute() -> void: - if dialogic.Choices.is_question(dialogic.current_event_idx): - dialogic.Choices.show_current_choices(false) + dialogic.Choices.show_current_question(false) dialogic.current_state = dialogic.States.AWAITING_CHOICE #endregion @@ -58,40 +65,51 @@ func to_text() -> String: var result_string := "" result_string = "- "+text.strip_edges() - if condition: - result_string += " [if "+condition+"]" - + var shortcode := store_to_shortcode_parameters() + if (condition and _has_condition) or shortcode or extra_data: + result_string += " |" + if condition and _has_condition: + result_string += " [if " + condition + "]" + + if shortcode or extra_data: + result_string += " [" + shortcode + if extra_data: + var extra_data_string := "" + for i in extra_data: + extra_data_string += " " + i + '="' + value_to_string(extra_data[i]) + '"' + if shortcode: + result_string += " " + result_string += extra_data_string.strip_edges() + result_string += "]" - 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)|.)*)(\\[if (?[^\\]]+)])?\\s?(\\s*\\[(?.*)\\])?') - var result = regex.search(string.strip_edges()) + var result := regex.search(string.strip_edges()) if result == null: return - text = result.get_string('text') - condition = result.get_string('condition') + text = result.get_string('text').strip_edges() + condition = result.get_string('condition').strip_edges() + _has_condition = not condition.is_empty() 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) + load_from_shortcode_parameters(result.get_string("shortcode")) + var shortcode := parse_shortcode_parameters(result.get_string('shortcode')) + shortcode.erase("else") + shortcode.erase("alt_text") + extra_data = shortcode.duplicate() - disabled_text = shortcode_params.get('alt_text', '') + +func get_shortcode_parameters() -> Dictionary: + return { + "else" : {"property": "else_action", "default": ElseActions.DEFAULT, + "suggestions": func(): return { + "Default" :{'value':ElseActions.DEFAULT, 'text_alt':['default']}, + "Hide" :{'value':ElseActions.HIDE,'text_alt':['hide'] }, + "Disable" :{'value':ElseActions.DISABLE,'text_alt':['disable']}}}, + "alt_text" : {"property": "disabled_text", "default": ""}, + "extra_data" : {"property": "extra_data", "default": {}, "custom_stored":true}, + } func is_valid_event(string:String) -> bool: @@ -123,8 +141,11 @@ func _get_property_original_translation(property:String) -> String: func build_event_editor() -> void: add_header_edit("text", ValueType.SINGLELINE_TEXT, {'autofocus':true}) - add_body_edit("condition", ValueType.CONDITION, {'left_text':'if '}) - add_body_edit("else_action", ValueType.FIXED_OPTIONS, {'left_text':'else ', + add_body_edit("", ValueType.LABEL, {"text":"Condition:"}) + add_body_edit("_has_condition", ValueType.BOOL_BUTTON, {"editor_icon":["Add", "EditorIcons"], "tooltip":"Add Condition"}, "not _has_condition") + add_body_edit("condition", ValueType.CONDITION, {}, "_has_condition") + add_body_edit("_has_condition", ValueType.BOOL_BUTTON, {"editor_icon":["Remove", "EditorIcons"], "tooltip":"Remove Condition"}, "_has_condition") + add_body_edit("else_action", ValueType.FIXED_OPTIONS, {'left_text':'Else:', 'options': [ { 'label': 'Default', @@ -138,14 +159,16 @@ func build_event_editor() -> void: 'label': 'Disable', 'value': ElseActions.DISABLE, } - ]}, '!condition.is_empty()') + ]}, '_has_condition') add_body_edit("disabled_text", ValueType.SINGLELINE_TEXT, { 'left_text':'Disabled text:', 'placeholder':'(Empty for same)'}, 'allow_alt_text()') + add_body_line_break() + add_body_edit("extra_data", ValueType.DICTIONARY, {"left_text":"Extra Data:"}) func allow_alt_text() -> bool: - return condition and ( + return _has_condition and ( else_action == ElseActions.DISABLE or (else_action == ElseActions.DEFAULT and ProjectSettings.get_setting("dialogic/choices/def_false_behaviour", 0) == 1)) @@ -155,10 +178,9 @@ func allow_alt_text() -> bool: #region CODE COMPLETION ################################################################################ -func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: +func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, _word:String, symbol:String) -> void: line = CodeCompletionHelper.get_line_untill_caret(line) - if !'[if' in line: if symbol == '{': CodeCompletionHelper.suggest_variables(TextNode) @@ -184,14 +206,27 @@ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:Str ################################################################################ func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: + var result := regex.search(line) + dict[0] = {'color':event_color} - dict = Highlighter.color_region(dict, event_color.lerp(Highlighter.variable_color, 0.5), line, '{','}', 0, line.find('[if'), event_color) - if '[if' in line: + + if not result: + return dict + + var condition_begin := result.get_start("condition") + var condition_end := result.get_end("condition") + + var shortcode_begin := result.get_start("shortcode") + + dict = Highlighter.color_region(dict, event_color.lerp(Highlighter.variable_color, 0.5), line, '{','}', 0, condition_begin, event_color) + + if condition_begin > 0: var from := line.find('[if') dict[from] = {"color":Highlighter.normal_color} - dict = Highlighter.color_word(dict, Highlighter.code_flow_color, line, 'if', from, line.find(']', from)) - dict = Highlighter.color_condition(dict, line, from, line.find(']', from)) - dict = Highlighter.color_shortcode_content(dict, line, line.find(']', from), 0,event_color) + dict[from+1] = {"color":Highlighter.code_flow_color} + dict[condition_begin] = {"color":Highlighter.normal_color} + dict = Highlighter.color_condition(dict, line, condition_begin, condition_end) + if shortcode_begin: + dict = Highlighter.color_shortcode_content(dict, line, shortcode_begin, 0, event_color) return dict #endregion - diff --git a/addons/dialogic/Modules/Choice/node_button_sound.gd b/addons/dialogic/Modules/Choice/node_button_sound.gd index e6d7e5217..3f6cdbefd 100644 --- a/addons/dialogic/Modules/Choice/node_button_sound.gd +++ b/addons/dialogic/Modules/Choice/node_button_sound.gd @@ -3,15 +3,15 @@ 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. +## 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 +@export var sound_pressed: AudioStream ## Sound to be played on hover. See [sound_pressed] for more. -@export var sound_hover:AudioStream +@export var sound_hover: AudioStream ## Sound to be played on focus. See [sound_pressed] for more. -@export var sound_focus:AudioStream +@export var sound_focus: AudioStream -func _ready(): +func _ready() -> void: add_to_group('dialogic_button_sound') _connect_all_buttons() @@ -21,7 +21,7 @@ func play_sound(sound) -> void: stream = sound play() -func _connect_all_buttons(): +func _connect_all_buttons() -> void: for child in get_parent().get_children(): if child is DialogicNode_ChoiceButton: child.button_up.connect(_on_pressed.bind(child.sound_pressed)) diff --git a/addons/dialogic/Modules/Choice/node_choice_button.gd b/addons/dialogic/Modules/Choice/node_choice_button.gd index 1b0dfe8a8..8e82436b6 100644 --- a/addons/dialogic/Modules/Choice/node_choice_button.gd +++ b/addons/dialogic/Modules/Choice/node_choice_button.gd @@ -12,7 +12,6 @@ extends Button ## not supported on buttons at this point. - ## 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 @@ -26,16 +25,22 @@ extends Button @export var text_node: Node -## Called when the text changes. -func _set_text_changed(new_text: String) -> void: - if text_node == null: - text = new_text - - else: - text_node.text = new_text - - func _ready() -> void: add_to_group('dialogic_choice_button') shortcut_in_tooltip = false hide() + + +func _load_info(choice_info: Dictionary) -> void: + set_choice_text(choice_info.text) + visible = choice_info.visible + disabled = choice_info.disabled + + +## Called when the text changes. +func set_choice_text(new_text: String) -> void: + if text_node: + text_node.text = new_text + else: + text = new_text + diff --git a/addons/dialogic/Modules/Choice/settings_choices.gd b/addons/dialogic/Modules/Choice/settings_choices.gd index cfd736d8b..66e4482a4 100644 --- a/addons/dialogic/Modules/Choice/settings_choices.gd +++ b/addons/dialogic/Modules/Choice/settings_choices.gd @@ -7,8 +7,8 @@ func _refresh() -> void: %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) + 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: diff --git a/addons/dialogic/Modules/Choice/subsystem_choices.gd b/addons/dialogic/Modules/Choice/subsystem_choices.gd index 3ee43f831..14e61a140 100644 --- a/addons/dialogic/Modules/Choice/subsystem_choices.gd +++ b/addons/dialogic/Modules/Choice/subsystem_choices.gd @@ -6,19 +6,20 @@ extends DialogicSubsystem signal choice_selected(info:Dictionary) ## Emitted when a set of choices is reached and shown. ## Info includes the keys 'choices' (an array of dictionaries with infos on all the choices). -signal choices_shown(info:Dictionary) +signal question_shown(info:Dictionary) ## Contains information on the latest question. var last_question_info := {} ## The delay between the text finishing revealing and the choices appearing -var reveal_delay: float = 0.0 +var reveal_delay := 0.0 ## If true the player has to click to reveal choices when they are reached -var reveal_by_input: bool = false +var reveal_by_input := false ## The delay between the choices becoming visible and being clickable. Can prevent accidental selection. -var block_delay: float = 0.2 +var block_delay := 0.2 ## If true, the first (top-most) choice will be focused -var autofocus_first_choice: bool = true +var autofocus_first_choice := true + enum FalseBehaviour {HIDE=0, DISABLE=1} ## The behaviour of choices with a false condition and else_action set to DEFAULT. @@ -37,7 +38,7 @@ var _choice_blocker := Timer.new() #region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: +func clear_game_state(_clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: hide_all_choices() @@ -63,123 +64,161 @@ func _ready() -> void: 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) + if node.is_connected('button_up', _on_choice_selected): + node.disconnect('button_up', _on_choice_selected) + + +## Collects information on all the choices of the current question. +## The result is a dictionary like this: +## {'choices': +## [ +## {'event_index':10, 'button_index':1, 'disabled':false, 'text':"My Choice", 'visible':true}, +## {'event_index':15, 'button_index':2, 'disabled':false, 'text':"My Choice2", 'visible':true}, +## ] +func get_current_question_info() -> Dictionary: + var question_info := {'choices':[]} + + var button_idx := 1 + last_question_info = {'choices':[]} + + for choice_index in get_current_choice_indexes(): + var event: DialogicEvent = dialogic.current_timeline_events[choice_index] + + if not event is DialogicChoiceEvent: + continue + + var choice_event: DialogicChoiceEvent = event + var choice_info := {} + choice_info['event_index'] = choice_index + choice_info['button_index'] = button_idx + + # Check Condition + var condition: String = choice_event.condition + + if condition.is_empty() or dialogic.Expressions.execute_condition(choice_event.condition): + choice_info['disabled'] = false + choice_info['text'] = choice_event.get_property_translated('text') + choice_info['visible'] = true + button_idx += 1 + else: + choice_info['disabled'] = true + if not choice_event.disabled_text.is_empty(): + choice_info['text'] = choice_event.get_property_translated('disabled_text') + else: + choice_info['text'] = choice_event.get_property_translated('text') + + var hide := choice_event.else_action == DialogicChoiceEvent.ElseActions.HIDE + hide = hide or choice_event.else_action == DialogicChoiceEvent.ElseActions.DEFAULT and default_false_behaviour == DialogicChoiceEvent.ElseActions.HIDE + choice_info['visible'] = not hide + + if not hide: + button_idx += 1 + + choice_info.text = dialogic.Text.parse_text(choice_info.text, true, true, false, true, false, false) + + choice_info.merge(choice_event.extra_data) + + if dialogic.has_subsystem('History'): + choice_info['visited_before'] = dialogic.History.has_event_been_visited(choice_index) + + question_info['choices'].append(choice_info) + + return question_info ## Lists all current choices and shows buttons. -func show_current_choices(instant:=true) -> void: +func show_current_question(instant:=true) -> void: hide_all_choices() _choice_blocker.stop() 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) + _choice_blocker.timeout.connect(show_current_question) if reveal_by_input: - dialogic.Inputs.dialogic_action.connect(show_current_choices) + dialogic.Inputs.dialogic_action.connect(show_current_question) return - if _choice_blocker.timeout.is_connected(show_current_choices): - _choice_blocker.timeout.disconnect(show_current_choices) - if dialogic.Inputs.dialogic_action.is_connected(show_current_choices): - dialogic.Inputs.dialogic_action.disconnect(show_current_choices) + if _choice_blocker.timeout.is_connected(show_current_question): + _choice_blocker.timeout.disconnect(show_current_question) + if dialogic.Inputs.dialogic_action.is_connected(show_current_question): + dialogic.Inputs.dialogic_action.disconnect(show_current_question) + var missing_button := false - 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.Expressions.execute_condition(choice_event.condition): - if choice_event.else_action == DialogicChoiceEvent.ElseActions.DEFAULT: - choice_event.else_action = default_false_behaviour - - # 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(block_delay) + var question_info := get_current_question_info() + for choice in question_info.choices: + var node: DialogicNode_ChoiceButton = get_choice_button_node(choice.button_index) -## 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 - var shown_at_all := false - for node: DialogicNode_ChoiceButton in get_tree().get_nodes_in_group('dialogic_choice_button'): - if !node.get_parent().is_visible_in_tree(): + if not node: + missing_button = true continue - if (node.choice_index == button_index) or (idx == button_index and node.choice_index == -1): - node.show() + node._load_info(choice) - if dialogic.has_subsystem('Text'): - text = dialogic.Text.parse_text(text, true, true, false, true, false, false) + if choice.button_index == 1 and autofocus_first_choice: + node.grab_focus() - node._set_text_changed(text) + match hotkey_behaviour: + ## Add 1 to 9 as shortcuts if it's enabled + HotkeyBehaviour.NUMBERS: + if choice.button_index > 0 or choice.button_index < 10: + var shortcut: Shortcut + if node.shortcut != null: + shortcut = node.shortcut + else: + shortcut = Shortcut.new() + + var input_key := InputEventKey.new() + input_key.keycode = OS.find_keycode_from_string(str(choice.button_index)) + shortcut.events.append(input_key) + node.shortcut = shortcut + + if node.pressed.is_connected(_on_choice_selected): + node.pressed.disconnect(_on_choice_selected) + node.pressed.connect(_on_choice_selected.bind(choice)) + + _choice_blocker.start(block_delay) + question_shown.emit(question_info) + + if missing_button: + printerr("[Dialogic] The layout you are using doesn't have enough Choice Buttons for the choices you are trying to display.") - if idx == 1 and autofocus_first_choice: - node.grab_focus() - ## Add 1 to 9 as shortcuts if it's enabled - match hotkey_behaviour: - HotkeyBehaviour.NUMBERS: - if idx > 0 or idx < 10: - var shortcut: Shortcut - if node.shortcut != null: - shortcut = node.shortcut - else: - shortcut = Shortcut.new() - - var shortcut_events: Array[InputEventKey] = [] - var input_key := InputEventKey.new() - input_key.keycode = OS.find_keycode_from_string(str(idx)) - shortcut.events.append(input_key) - node.shortcut = shortcut - - node.disabled = not enabled - - if node.pressed.is_connected(_on_ChoiceButton_choice_selected): - node.pressed.disconnect(_on_ChoiceButton_choice_selected) - - node.pressed.connect(_on_ChoiceButton_choice_selected.bind(event_index, - {'button_index':button_index, 'text':text, 'enabled':enabled, 'event_index':event_index})) - shown_at_all = true + +func get_choice_button_node(button_index:int) -> DialogicNode_ChoiceButton: + var idx := 1 + for node: DialogicNode_ChoiceButton 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 (node.choice_index == -1 and idx == button_index): + return node if node.choice_index > 0: idx = node.choice_index idx += 1 - if not shown_at_all: - printerr("[Dialogic] The layout you are using doesn't have enough Choice Buttons for the choices you are trying to display.") + return null -func _on_ChoiceButton_choice_selected(event_index:int, choice_info:={}) -> void: +func _on_choice_selected(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+1) - 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(choice_info.text), "Choice", {'all_choices': all_choices}) else: dialogic.History.store_simple_history_entry(choice_info.text, "Choice", {'all_choices': all_choices}) + if dialogic.has_subsystem("History"): + dialogic.History.mark_event_as_visited(choice_info.event_index) + + choice_selected.emit(choice_info) + hide_all_choices() + dialogic.current_state = dialogic.States.IDLE + dialogic.handle_event(choice_info.event_index + 1) @@ -219,7 +258,7 @@ func is_question(index:int) -> bool: if dialogic.current_timeline_events[index] is DialogicChoiceEvent: if index != 0 and dialogic.current_timeline_events[index-1] is DialogicEndBranchEvent: - if dialogic.current_timeline_events[dialogic.current_timeline_events[index-1].find_opening_index()] is DialogicChoiceEvent: + if dialogic.current_timeline_events[dialogic.current_timeline_events[index-1].find_opening_index(index-1)] is DialogicChoiceEvent: return false else: return true diff --git a/addons/dialogic/Modules/Choice/ui_choice_end.gd b/addons/dialogic/Modules/Choice/ui_choice_end.gd index 8bfb21873..acc5d827b 100644 --- a/addons/dialogic/Modules/Choice/ui_choice_end.gd +++ b/addons/dialogic/Modules/Choice/ui_choice_end.gd @@ -3,7 +3,7 @@ extends HBoxContainer var parent_resource: DialogicChoiceEvent = null -func refresh(): +func refresh() -> void: $AddChoice.icon = get_theme_icon("Add", "EditorIcons") if parent_resource is DialogicChoiceEvent: @@ -17,9 +17,9 @@ func refresh(): func _on_add_choice_pressed() -> void: - var timeline = find_parent('VisualEditor') + var timeline := find_parent('VisualEditor') if timeline: - var resource = DialogicChoiceEvent.new() + var resource := DialogicChoiceEvent.new() resource.created_by_button = true timeline.add_event_undoable(resource, get_parent().get_index()+1) timeline.indent_events() diff --git a/addons/dialogic/Modules/Clear/event_clear.gd b/addons/dialogic/Modules/Clear/event_clear.gd index c69acaa9d..c125ae96d 100644 --- a/addons/dialogic/Modules/Clear/event_clear.gd +++ b/addons/dialogic/Modules/Clear/event_clear.gd @@ -1,5 +1,5 @@ @tool -class_name DialogiClearEvent +class_name DialogicClearEvent extends DialogicEvent ## Event that clears audio & visuals (not variables). @@ -34,7 +34,7 @@ func _execute() -> void: if clear_portraits and dialogic.has_subsystem('Portraits') and len(dialogic.Portraits.get_joined_characters()) != 0: if final_time == 0: - dialogic.Portraits.leave_all_characters(DialogicResourceUtil.guess_special_resource("PortraitAnimation", 'Instant In Or Out'), final_time, step_by_step) + dialogic.Portraits.leave_all_characters("Instant", final_time, step_by_step) else: dialogic.Portraits.leave_all_characters("", final_time, step_by_step) if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout @@ -48,11 +48,14 @@ func _execute() -> void: if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout if clear_style and dialogic.has_subsystem('Styles'): - dialogic.Styles.load_style() + dialogic.Styles.change_style() if clear_portrait_positions and dialogic.has_subsystem('Portraits'): - dialogic.Portraits.reset_all_portrait_positions() - + dialogic.PortraitContainers.reset_all_containers() + + if not step_by_step: + await dialogic.get_tree().create_timer(final_time).timeout + finish() @@ -93,7 +96,7 @@ func get_shortcode_parameters() -> Dictionary: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_label('Clear') add_body_edit('time', ValueType.NUMBER, {'left_text':'Time:'}) diff --git a/addons/dialogic/Modules/Comment/event_comment.gd b/addons/dialogic/Modules/Comment/event_comment.gd index d70a34ac5..ad0b1dd2b 100644 --- a/addons/dialogic/Modules/Comment/event_comment.gd +++ b/addons/dialogic/Modules/Comment/event_comment.gd @@ -8,7 +8,7 @@ extends DialogicEvent ### Settings ## Content of the comment. -var text :String = "" +var text := "" ################################################################################ @@ -36,8 +36,7 @@ func _init() -> void: ################################################################################ func to_text() -> String: - var result_string = "# "+text - return result_string + return "# "+text func from_text(string:String) -> void: @@ -54,13 +53,13 @@ func is_valid_event(string:String) -> bool: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_edit('text', ValueType.SINGLELINE_TEXT, {'left_text':'#', 'autofocus':true}) #################### SYNTAX HIGHLIGHTING ####################################### ################################################################################ -func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: +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/Condition/event_condition.gd b/addons/dialogic/Modules/Condition/event_condition.gd index 3902a894d..d56f34ac6 100644 --- a/addons/dialogic/Modules/Condition/event_condition.gd +++ b/addons/dialogic/Modules/Condition/event_condition.gd @@ -10,7 +10,7 @@ enum ConditionTypes {IF, ELIF, ELSE} ## 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 = "" +var condition := "" ################################################################################ @@ -24,9 +24,9 @@ func _execute() -> void: if condition.is_empty(): condition = "true" - var result :bool= dialogic.Expressions.execute_condition(condition) + var result: bool = dialogic.Expressions.execute_condition(condition) if not result: - var idx :int= dialogic.current_event_idx + var idx: int = dialogic.current_event_idx var ignore := 1 while true: idx += 1 @@ -103,7 +103,7 @@ func is_valid_event(string:String) -> bool: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_edit('condition_type', ValueType.FIXED_OPTIONS, { 'options': [ { @@ -125,12 +125,12 @@ func build_event_editor(): ####################### CODE COMPLETION ######################################## ################################################################################ -func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: +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: +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) diff --git a/addons/dialogic/Modules/Condition/ui_condition_end.gd b/addons/dialogic/Modules/Condition/ui_condition_end.gd index 747955cef..4a5ac1d0e 100644 --- a/addons/dialogic/Modules/Condition/ui_condition_end.gd +++ b/addons/dialogic/Modules/Condition/ui_condition_end.gd @@ -1,13 +1,15 @@ @tool extends HBoxContainer -var parent_resource = null +var parent_resource: DialogicEvent = null -func _ready(): + +func _ready() -> void: $AddElif.button_up.connect(add_elif) $AddElse.button_up.connect(add_else) -func refresh(): + +func refresh() -> void: if parent_resource is DialogicConditionEvent: # hide add elif and add else button on ELSE event $AddElif.visible = parent_resource.condition_type != DialogicConditionEvent.ConditionTypes.ELSE @@ -15,9 +17,9 @@ func refresh(): $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') + var timeline_editor := find_parent('VisualEditor') if timeline_editor: - var next_event = null + var next_event: DialogicEvent = null if timeline_editor.get_block_below(get_parent()): next_event = timeline_editor.get_block_below(get_parent()).resource if next_event is DialogicConditionEvent: @@ -28,19 +30,21 @@ func refresh(): else: hide() -func add_elif(): - var timeline = find_parent('VisualEditor') + +func add_elif() -> void: + var timeline := find_parent('VisualEditor') if timeline: - var resource = DialogicConditionEvent.new() + var resource := DialogicConditionEvent.new() resource.condition_type = DialogicConditionEvent.ConditionTypes.ELIF timeline.add_event_undoable(resource, get_parent().get_index()+1) timeline.indent_events() timeline.something_changed() -func add_else(): - var timeline = find_parent('VisualEditor') + +func add_else() -> void: + var timeline := find_parent('VisualEditor') if timeline: - var resource = DialogicConditionEvent.new() + var resource := DialogicConditionEvent.new() resource.condition_type = DialogicConditionEvent.ConditionTypes.ELSE timeline.add_event_undoable(resource, get_parent().get_index()+1) timeline.indent_events() diff --git a/addons/dialogic/Modules/Core/event_end_branch.gd b/addons/dialogic/Modules/Core/event_end_branch.gd index c9dc90c0b..60a53dd0d 100644 --- a/addons/dialogic/Modules/Core/event_end_branch.gd +++ b/addons/dialogic/Modules/Core/event_end_branch.gd @@ -34,8 +34,8 @@ func find_next_index() -> int: return idx -func find_opening_index() -> int: - var idx: int = dialogic.current_event_idx +func find_opening_index(at_index:int) -> int: + var idx: int = at_index var ignore: int = 1 while true: @@ -72,7 +72,7 @@ func to_text() -> String: return "<>" -func from_text(string:String) -> void: +func from_text(_string:String) -> void: pass diff --git a/addons/dialogic/Modules/Core/index.gd b/addons/dialogic/Modules/Core/index.gd index a9a17919a..7082d4bac 100644 --- a/addons/dialogic/Modules/Core/index.gd +++ b/addons/dialogic/Modules/Core/index.gd @@ -20,3 +20,8 @@ func _get_text_effects() -> Array[Dictionary]: {'command':'ns', 'subsystem':'Inputs', 'method':'effect_noskip'}, {'command':'input', 'subsystem':'Inputs', 'method':'effect_input'}, ] + +func _get_text_modifiers() -> Array[Dictionary]: + return [ + {'subsystem':'Expressions', 'method':"modifier_condition", 'command':'if', 'mode':-1}, + ] diff --git a/addons/dialogic/Modules/Core/subsystem_animation.gd b/addons/dialogic/Modules/Core/subsystem_animation.gd index 8ab0f2841..476913442 100644 --- a/addons/dialogic/Modules/Core/subsystem_animation.gd +++ b/addons/dialogic/Modules/Core/subsystem_animation.gd @@ -3,24 +3,40 @@ extends DialogicSubsystem ## Subsystem that allows entering and leaving an animation state. signal finished +signal animation_interrupted -var prev_state: int = 0 +var prev_state: DialogicGameHandler.States = DialogicGameHandler.States.IDLE +var _is_animating := false #region MAIN METHODS #################################################################################################### +func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: + stop_animation() + + func is_animating() -> bool: - return dialogic.current_state == dialogic.States.ANIMATING + return _is_animating func start_animating() -> void: prev_state = dialogic.current_state dialogic.current_state = dialogic.States.ANIMATING + _is_animating = true -func animation_finished(arg := "") -> void: - dialogic.current_state = prev_state +func animation_finished(_arg := "") -> void: + # It can happen that the animation state has already been stopped + if not is_animating(): + return + _is_animating = false + dialogic.current_state = prev_state as DialogicGameHandler.States finished.emit() + +func stop_animation() -> void: + animation_finished() + animation_interrupted.emit() + #endregion diff --git a/addons/dialogic/Modules/Core/subsystem_expression.gd b/addons/dialogic/Modules/Core/subsystem_expression.gd index 9bc2eccd0..fd4489aa6 100644 --- a/addons/dialogic/Modules/Core/subsystem_expression.gd +++ b/addons/dialogic/Modules/Core/subsystem_expression.gd @@ -20,6 +20,9 @@ func execute_string(string:String, default: Variant = null, no_warning := false) for res in regex.search_all(string): var value: Variant = dialogic.VAR.get_variable(res.get_string()) string = string.replace(res.get_string(), var_to_str(value)) + + if string.begins_with("{") and string.ends_with('}') and string.count("{") == 1: + string = string.trim_prefix("{").trim_suffix("}") var expr := Expression.new() @@ -31,17 +34,17 @@ func execute_string(string:String, default: Variant = null, no_warning := false) if expr.parse(string, autoload_names) != OK: if not no_warning: - printerr('Dialogic: Expression failed to parse: ', expr.get_error_text()) - printerr(' Expression: ', string) - print("\n") + printerr('[Dialogic] Expression "', string, '" failed to parse.') + printerr(' ', expr.get_error_text()) + dialogic.print_debug_moment() return default var result: Variant = expr.execute(autoloads, self) if expr.has_execute_failed(): if not no_warning: - printerr('Dialogic: Expression failed to execute: ', expr.get_error_text()) - printerr(' Expression: ', string) - print("\n") + printerr('[Dialogic] Expression "', string, '" failed to parse.') + printerr(' ', expr.get_error_text()) + dialogic.print_debug_moment() return default return result @@ -51,6 +54,15 @@ func execute_condition(condition:String) -> bool: return true return false + +var condition_modifier_regex := RegEx.create_from_string(r"(?(DEFINE)(?([^{}]|\{(?P>nobraces)\})*))\[if *(?\{(?P>nobraces)\})(?(\\\]|\\\/|[^\]\/])*)(\/(?(\\\]|[^\]])*))?\]") +func modifier_condition(text:String) -> String: + for find in condition_modifier_regex.search_all(text): + if execute_condition(find.get_string("condition")): + text = text.replace(find.get_string(), find.get_string("truetext").strip_edges()) + else: + text = text.replace(find.get_string(), find.get_string("falsetext").strip_edges()) + return text #endregion diff --git a/addons/dialogic/Modules/Core/subsystem_input.gd b/addons/dialogic/Modules/Core/subsystem_input.gd index b94fd71d1..1c21f695f 100644 --- a/addons/dialogic/Modules/Core/subsystem_input.gd +++ b/addons/dialogic/Modules/Core/subsystem_input.gd @@ -12,12 +12,15 @@ signal dialogic_action signal autoskip_timer_finished +const _SETTING_INPUT_ACTION := "dialogic/text/input_action" +const _SETTING_INPUT_ACTION_DEFAULT := "dialogic_default_action" + var input_block_timer := Timer.new() var _auto_skip_timer_left: float = 0.0 var action_was_consumed := false var auto_skip: DialogicAutoSkip = null -var auto_advance : DialogicAutoAdvance = null +var auto_advance: DialogicAutoAdvance = null var manual_advance: DialogicManualAdvance = null @@ -28,8 +31,8 @@ func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) if not is_node_ready(): await ready - manual_advance.enabled_until_next_event = false - manual_advance.enabled_forced = true + manual_advance.disabled_until_next_event = false + manual_advance.system_enabled = true func pause() -> void: @@ -48,6 +51,7 @@ func resume() -> void: func post_install() -> void: dialogic.Settings.connect_to_change('autoadvance_delay_modifier', auto_advance._update_autoadvance_delay_modifier) auto_skip.toggled.connect(_on_autoskip_toggled) + auto_skip._init() add_child(input_block_timer) input_block_timer.one_shot = true @@ -88,8 +92,8 @@ func handle_input() -> void: ## Unhandled Input is used for all NON-Mouse based inputs. func _unhandled_input(event:InputEvent) -> void: - if Input.is_action_pressed(ProjectSettings.get_setting('dialogic/text/input_action', 'dialogic_default_action')): - if event is InputEventMouse: + if is_input_pressed(event, true): + if event is InputEventMouse or event is InputEventScreenTouch: return handle_input() @@ -97,21 +101,32 @@ func _unhandled_input(event:InputEvent) -> void: ## Input is used for all mouse based inputs. ## If any DialogicInputNode is present this won't do anything (because that node handles MouseInput then). func _input(event:InputEvent) -> void: - if Input.is_action_pressed(ProjectSettings.get_setting('dialogic/text/input_action', 'dialogic_default_action')): - + if is_input_pressed(event): if not event is InputEventMouse or get_tree().get_nodes_in_group('dialogic_input').any(func(node):return node.is_visible_in_tree()): return handle_input() +func is_input_pressed(event: InputEvent, exact := false) -> bool: + var action: String = ProjectSettings.get_setting(_SETTING_INPUT_ACTION, _SETTING_INPUT_ACTION_DEFAULT) + return (event is InputEventAction and event.action == action) or Input.is_action_just_pressed(action, exact) + + +## This is called from the gui_input of the InputCatcher and DialogText nodes +func handle_node_gui_input(event:InputEvent) -> void: + if Input.is_action_just_pressed(ProjectSettings.get_setting(_SETTING_INPUT_ACTION, _SETTING_INPUT_ACTION_DEFAULT)): + if event is InputEventMouseButton and event.pressed: + DialogicUtil.autoload().Inputs.handle_input() + + func is_input_blocked() -> bool: return input_block_timer.time_left > 0.0 func block_input(time:=0.1) -> void: if time > 0: - input_block_timer.wait_time = time + input_block_timer.wait_time = max(time, input_block_timer.time_left) input_block_timer.start() @@ -169,7 +184,7 @@ func _process(delta: float) -> void: ################################################################################ -func effect_input(text_node:Control, skipped:bool, argument:String) -> void: +func effect_input(_text_node:Control, skipped:bool, _argument:String) -> void: if skipped: return dialogic.Text.show_next_indicators() @@ -180,11 +195,11 @@ func effect_input(text_node:Control, skipped:bool, argument:String) -> void: func effect_noskip(text_node:Control, skipped:bool, argument:String) -> void: dialogic.Text.set_text_reveal_skippable(false, true) - manual_advance.enabled_until_next_event = true + manual_advance.disabled_until_next_event = true effect_autoadvance(text_node, skipped, argument) -func effect_autoadvance(text_node: Control, skipped:bool, argument:String) -> void: +func effect_autoadvance(_text_node: Control, _skipped:bool, argument:String) -> void: if argument.ends_with('?'): argument = argument.trim_suffix('?') else: diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Base_Default/default_layout_base.gd b/addons/dialogic/Modules/DefaultLayoutParts/Base_Default/default_layout_base.gd index 648b2cd26..af6878cac 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Base_Default/default_layout_base.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Base_Default/default_layout_base.gd @@ -4,6 +4,7 @@ extends DialogicLayoutBase ## The default layout base scene. @export var canvas_layer: int = 1 +@export var follow_viewport: bool = false @export_subgroup("Global") @export var global_bg_color: Color = Color(0, 0, 0, 0.9) @@ -15,5 +16,6 @@ extends DialogicLayoutBase func _apply_export_overrides() -> void: # apply layer set(&'layer', canvas_layer) + set(&'follow_viewport_enabled', follow_viewport) diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd b/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd index 99a2dbf49..ed4707ab4 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd @@ -1,50 +1,94 @@ @tool extends DialogicLayoutBase -## This layout won't do anything on it's own +## This layout won't do anything on its own -var bubbles: Dictionary = {} -var fallback_bubble :Control = null +var bubbles: Array = [] +var registered_characters: Dictionary = {} +@export_group("Main") +@export_range(1, 25, 1) var bubble_count: int = 2 -func _ready(): + +func _ready() -> void: if Engine.is_editor_hint(): return DialogicUtil.autoload().Text.about_to_show_text.connect(_on_dialogic_text_event) - $Example/ExamplePoint.position = $Example.get_viewport_rect().size/2 + $Example/CRT.position = $Example.get_viewport_rect().size/2 if not has_node('TextBubbleLayer'): return - fallback_bubble = get_node("TextBubbleLayer").add_bubble() - fallback_bubble.speaker_node = $Example/ExamplePoint + if len(bubbles) < bubble_count: + add_bubble() + + +func register_character(character:Variant, node:Node): + if typeof(character) == TYPE_STRING: + var character_string: String = character + if character.begins_with("res://"): + character = load(character) + else: + character = DialogicResourceUtil.get_character_resource(character) + if not character: + printerr("[Dialogic] Textbubble: Tried registering character from invalid string '", character_string, "'.") + + registered_characters[character] = node + if len(registered_characters) > len(bubbles) and len(bubbles) < bubble_count: + add_bubble() + + +func _get_persistent_info() -> Dictionary: + return {"textbubble_registers": registered_characters} + + +func _load_persistent_info(info: Dictionary) -> void: + var register_info: Dictionary = info.get("textbubble_registers", {}) + for character in register_info: + if is_instance_valid(register_info[character]): + register_character(character, register_info[character]) -func register_character(character:DialogicCharacter, node:Node2D): +func add_bubble() -> void: if not has_node('TextBubbleLayer'): return var new_bubble: Control = get_node("TextBubbleLayer").add_bubble() - 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" - bubbles[character] = new_bubble + bubbles.append(new_bubble) + func _on_dialogic_text_event(info:Dictionary): - var no_bubble_open := true + var bubble_to_use: Node + for bubble in bubbles: + if bubble.current_character == info.character: + bubble_to_use = bubble - for character in bubbles: - if info.character == character: - no_bubble_open = false - bubbles[character].open() - else: - bubbles[character].close() + if bubble_to_use == null: + for bubble in bubbles: + if bubble.current_character == null: + bubble_to_use = bubble - if no_bubble_open: - $Example.show() - fallback_bubble.open() - else: + if bubble_to_use == null: + bubble_to_use = bubbles[0] + + var node_to_point_at: Node + if info.character in registered_characters: + node_to_point_at = registered_characters[info.character] $Example.hide() - fallback_bubble.close() + else: + node_to_point_at = $Example/CRT/Marker + $Example.show() + + bubble_to_use.current_character = info.character + bubble_to_use.node_to_point_at = node_to_point_at + bubble_to_use.reset() + if has_node('TextBubbleLayer'): + get_node("TextBubbleLayer").bubble_apply_overrides(bubble_to_use) + bubble_to_use.open() + ## Now close other bubbles + for bubble in bubbles: + if bubble != bubble_to_use: + bubble.close() + bubble.current_character = null diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.tscn index 312c47185..27a00fc6d 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.tscn @@ -1,11 +1,19 @@ -[gd_scene load_steps=2 format=3 uid="uid://syki6k0e6aac"] +[gd_scene load_steps=3 format=3 uid="uid://syki6k0e6aac"] [ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd" id="1_urqwc"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_70ljh"] +content_margin_left = 5.0 +content_margin_top = 5.0 +content_margin_right = 5.0 +content_margin_bottom = 5.0 +bg_color = Color(0, 0, 0, 0.654902) + [node name="TextBubbleHolder" type="CanvasLayer"] script = ExtResource("1_urqwc") [node name="Example" type="Control" parent="."] +visible = false layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -13,25 +21,35 @@ 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 = -227.0 -offset_right = 836.0 -offset_bottom = -16.0 +offset_left = 12.0 +offset_top = -235.0 +offset_right = 835.0 +offset_bottom = -14.0 grow_vertical = 0 +theme_override_styles/normal = SubResource("StyleBoxFlat_70ljh") 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 .dch file). -[color=lightblue]@node[/color] should be the 2D node the bubble should point at. +- [color=lightblue]character_resource[/color] should be a loaded DialogicCharacter (a .dch file). +- [color=lightblue]node[/color] should be the 2D or 3D node the bubble should point at. -> E.g. [color=darkgray]layout.register_character(load(\"res://path/to/my/character.dch\"), $BubbleMarker)" + +[node name="CRT" type="ColorRect" parent="Example"] +layout_mode = 0 +offset_left = 504.0 +offset_top = 290.0 +offset_right = 540.0 +offset_bottom = 324.0 +rotation = 0.785397 +color = Color(1, 0.313726, 1, 1) + +[node name="Marker" type="Marker2D" parent="Example/CRT"] +position = Vector2(10.6066, 9.1924) diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn index b4766352f..f1d44c03d 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn @@ -4,6 +4,7 @@ [ext_resource type="Script" path="res://addons/dialogic/Modules/Background/node_background_holder.gd" id="2_ghan2"] [node name="BackgroundLayer" type="Control"] +layout_direction = 2 layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -20,4 +21,5 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 +color = Color(1, 1, 1, 0) script = ExtResource("2_ghan2") diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.gd index 8d9600a5a..60f4e9142 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.gd @@ -74,90 +74,34 @@ func _ready() -> void: _error = text_system.connect(&'animation_textbox_hide', get_pointer().hide) _error = text_system.connect(&'meta_hover_started', _on_dialogic_display_dialog_text_meta_hover_started) _error = text_system.connect(&'meta_hover_ended', _on_dialogic_display_dialog_text_meta_hover_ended) - _error = text_system.connect(&'meta_clicked', _on_dialogic_display_dialog_text_meta_clicked) -func _try_translate(tr_base: String, property: StringName, fallback_entry: Dictionary) -> String: - var tr_key := tr_base.path_join(property) - var tr_value := tr(tr_key) - - if tr_key == tr_value: - tr_value = fallback_entry.get(property, "") - - return tr_value - ## Method that shows the bubble and fills in the info func _on_dialogic_display_dialog_text_meta_hover_started(meta: String) -> void: - var glossary: DialogicGlossary = DialogicUtil.autoload().Glossary.find_glossary(meta) - - var entry_title := "" - var entry_text := "" - var entry_extra := "" - var entry_color: Variant = null - - var title_color := title_custom_color - var text_color := text_custom_color - var extra_color := extra_custom_color + var entry_info := DialogicUtil.autoload().Glossary.get_entry(meta) - if glossary == null: + if entry_info.is_empty(): return - var is_translation_enabled: bool = ProjectSettings.get_setting('dialogic/translation/enabled', false) - - if not is_translation_enabled or glossary._translation_id.is_empty(): - var entry := glossary.get_entry(meta) - - if entry.is_empty(): - return - - entry_title = entry.get("title", "") - entry_text = entry.get("text", "") - entry_extra = entry.get("extra", "") - entry_color = entry.get("color") - - else: - var translation_key: String = glossary._translation_keys.get(meta) - var last_slash := translation_key.rfind('/') - - if last_slash == MISSING_INDEX: - return - - var tr_base := translation_key.substr(0, last_slash) - - var entry := glossary.get_entry(meta) - entry_color = entry.get('color') - - entry_title = _try_translate(tr_base, "title", entry) - entry_text = _try_translate(tr_base, "text", entry) - entry_extra = _try_translate(tr_base, "extra", entry) - - if not entry_color == null: - title_color = entry_color - text_color = entry_color - extra_color = entry_color - get_pointer().show() - get_title().text = entry_title - get_text().text = entry_text + get_title().text = entry_info.title + get_text().text = entry_info.text get_text().text = ['', '[center]', '[right]'][text_alignment] + get_text().text - get_extra().text = entry_extra + get_extra().text = entry_info.extra get_extra().text = ['', '[center]', '[right]'][extra_alignment] + get_extra().text get_pointer().global_position = get_pointer().get_global_mouse_position() - if title_color_mode == TextColorModes.ENTRY: - get_title().add_theme_color_override(&"font_color", title_color) + get_title().add_theme_color_override(&"font_color", entry_info.color) if text_color_mode == TextColorModes.ENTRY: - get_text().add_theme_color_override(&"default_color", text_color) + get_text().add_theme_color_override(&"default_color", entry_info.color) if extra_color_mode == TextColorModes.ENTRY: - get_extra().add_theme_color_override(&"default_color", extra_color) + get_extra().add_theme_color_override(&"default_color", entry_info.color) match box_modulate_mode: ModulateModes.ENTRY_COLOR_ON_BOX: - get_panel().self_modulate = title_color - get_panel_point().self_modulate = title_color - - DialogicUtil.autoload().Inputs.action_was_consumed = true + get_panel().self_modulate = entry_info.color + get_panel_point().self_modulate = entry_info.color ## Method that keeps the bubble at mouse position when visible @@ -173,20 +117,15 @@ func _process(_delta: float) -> void: ## Method that hides the bubble func _on_dialogic_display_dialog_text_meta_hover_ended(_meta:String) -> void: get_pointer().hide() - DialogicUtil.autoload().Inputs.action_was_consumed = false -func _on_dialogic_display_dialog_text_meta_clicked(_meta:String) -> void: - DialogicUtil.autoload().Inputs.action_was_consumed = true - func _apply_export_overrides() -> void: - var font_setting: String = get_global_setting("font", "") - # Apply fonts var font: FontFile - if font_use_global and ResourceLoader.exists(get_global_setting(&'font', '') as String): - font = load(get_global_setting(&'font', '') as String) + var global_font_setting: String = get_global_setting(&"font", '') + if font_use_global and ResourceLoader.exists(global_font_setting): + font = load(global_font_setting) elif ResourceLoader.exists(font_custom): font = load(font_custom) @@ -211,16 +150,17 @@ func _apply_export_overrides() -> void: # Apply text colors + # this applies Global or Custom colors, entry colors are applied on hover var controls: Array[Control] = [get_title(), get_text(), get_extra()] - var global_settings: Array[StringName] = [&'font_color', &'default_color', &'default_color'] + var settings: Array[StringName] = [&'font_color', &'default_color', &'default_color'] var color_modes: Array[TextColorModes] = [title_color_mode, text_color_mode, extra_color_mode] var custom_colors: PackedColorArray = [title_custom_color, text_custom_color, extra_custom_color] for i : int in len(controls): match color_modes[i]: TextColorModes.GLOBAL: - controls[i].add_theme_color_override(global_settings[i], get_global_setting(&'font_color', custom_colors[i]) as Color) + controls[i].add_theme_color_override(settings[i], get_global_setting(&'font_color', custom_colors[i]) as Color) TextColorModes.CUSTOM: - controls[i].add_theme_color_override(global_settings[i], custom_colors[i]) + controls[i].add_theme_color_override(settings[i], custom_colors[i]) # Apply box size var panel: PanelContainer = get_panel() diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/example_history_item.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/example_history_item.gd index f01fa0ffa..83fef5013 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/example_history_item.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/example_history_item.gd @@ -14,17 +14,17 @@ func get_icon() -> TextureRect: func load_info(text:String, character:String = "", character_color: Color =Color(), icon:Texture= null) -> void: get_text_box().text = text - var name_label : Label = get_name_label() + var name_label: Label = get_name_label() if character: name_label.text = character name_label.add_theme_color_override('font_color', character_color) name_label.show() else: name_label.hide() - - var icon_node : TextureRect = get_icon() + + var icon_node: TextureRect = get_icon() if icon == null: icon_node.hide() else: icon_node.show() - icon_node.texture = icon \ No newline at end of file + icon_node.texture = icon diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd index 8d3a8b28c..d4200214e 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd @@ -53,8 +53,8 @@ func get_history_log() -> VBoxContainer: func _ready() -> void: if Engine.is_editor_hint(): return - Dialogic.History.open_requested.connect(_on_show_history_pressed) - Dialogic.History.close_requested.connect(_on_hide_history_pressed) + DialogicUtil.autoload().History.open_requested.connect(_on_show_history_pressed) + DialogicUtil.autoload().History.close_requested.connect(_on_hide_history_pressed) func _apply_export_overrides() -> void: diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker_portrait_textbox_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker_portrait_textbox_layer.gd index 5e28d9f73..61e3714b0 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker_portrait_textbox_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker_portrait_textbox_layer.gd @@ -94,7 +94,7 @@ func _apply_export_overrides() -> void: panel.position = Vector2(-box_size.x/2, -box_size.y-box_distance) portrait_panel.size_flags_stretch_ratio = portrait_stretch_factor - var stylebox: StyleBoxFlat = load(box_panel) + var stylebox: StyleBox = load(box_panel) panel.add_theme_stylebox_override(&'panel', stylebox) ## PORTRAIT SETTINGS diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/textbox_with_speaker_portrait.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/textbox_with_speaker_portrait.tscn index 766783c55..369973c4c 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/textbox_with_speaker_portrait.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/textbox_with_speaker_portrait.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=6 format=3 uid="uid://by6waso0mjpjp"] +[gd_scene load_steps=7 format=3 uid="uid://by6waso0mjpjp"] [ext_resource type="Script" path="res://addons/dialogic/Modules/Character/node_portrait_container.gd" id="1_4jxq7"] [ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker_portrait_textbox_layer.gd" id="1_7jt4d"] [ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_name_label.gd" id="2_y0h34"] [ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_dialog_text.gd" id="3_11puy"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/Text/node_type_sound.gd" id="5_sr2qw"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dmg1w"] bg_color = Color(0.254902, 0.254902, 0.254902, 1) @@ -20,6 +21,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 script = ExtResource("1_7jt4d") box_panel = "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/default_stylebox.tres" @@ -32,6 +34,7 @@ anchor_right = 0.5 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 0 +mouse_filter = 2 [node name="Panel" type="PanelContainer" parent="Anchor"] unique_name_in_owner = true @@ -48,9 +51,11 @@ offset_right = 250.0 offset_bottom = -50.0 grow_horizontal = 2 grow_vertical = 0 +mouse_filter = 2 [node name="HBox" type="HBoxContainer" parent="Anchor/Panel"] layout_mode = 2 +mouse_filter = 2 theme_override_constants/separation = 15 [node name="PortraitPanel" type="Panel" parent="Anchor/Panel/HBox"] @@ -59,6 +64,7 @@ clip_children = 1 layout_mode = 2 size_flags_horizontal = 3 size_flags_stretch_ratio = 0.3 +mouse_filter = 2 theme_override_styles/panel = SubResource("StyleBoxFlat_dmg1w") [node name="PortraitBackgroundColor" type="ColorRect" parent="Anchor/Panel/HBox/PortraitPanel"] @@ -73,6 +79,7 @@ offset_right = 7.0 offset_bottom = 3.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 color = Color(0, 0, 0, 0.231373) [node name="DialogicNode_PortraitContainer" type="Control" parent="Anchor/Panel/HBox/PortraitPanel/PortraitBackgroundColor"] @@ -83,13 +90,16 @@ anchor_bottom = 1.0 offset_top = 4.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 script = ExtResource("1_4jxq7") mode = 1 +container_ids = PackedStringArray("1") debug_character_portrait = "speaker" [node name="VBoxContainer" type="VBoxContainer" parent="Anchor/Panel/HBox"] layout_mode = 2 size_flags_horizontal = 3 +mouse_filter = 2 [node name="DialogicNode_NameLabel" type="Label" parent="Anchor/Panel/HBox/VBoxContainer"] unique_name_in_owner = true @@ -98,7 +108,7 @@ theme_override_font_sizes/font_size = 8 text = "Name" script = ExtResource("2_y0h34") -[node name="DialogicNode_DialogText" type="RichTextLabel" parent="Anchor/Panel/HBox/VBoxContainer"] +[node name="DialogicNode_DialogText" type="RichTextLabel" parent="Anchor/Panel/HBox/VBoxContainer" node_paths=PackedStringArray("textbox_root")] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 @@ -106,4 +116,9 @@ theme_override_font_sizes/normal_font_size = 6 bbcode_enabled = true text = "Some text" scroll_following = true +visible_characters_behavior = 1 script = ExtResource("3_11puy") +textbox_root = NodePath("../../..") + +[node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="Anchor/Panel/HBox/VBoxContainer/DialogicNode_DialogText"] +script = ExtResource("5_sr2qw") diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader index 17c529aab..c1e348fc8 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader @@ -1,24 +1,17 @@ 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; +uniform sampler2D deformation_sampler : filter_linear, repeat_enable; +uniform float radius :hint_range(1.0, 200, 0.01)= 25; +uniform vec2 box_size = vec2(100, 100); +uniform float box_padding = 15; +uniform float wobble_amount : hint_range(0.0, 1.0, 0.01) = 0.2; +uniform float wobble_speed : hint_range(0.0, 10.0, 0.01) = 1; +uniform float wobble_detail : hint_range(0.01, 1, 0.01) = 0.5; 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; + float adjusted_radius = min(min(radius, box_size.x/2.0), box_size.y/2.0); + vec2 deformation_sample = texture(deformation_sampler, UV*wobble_detail+TIME*wobble_speed*0.05).xy*(vec2(box_padding)/box_size)*0.9; + vec2 deformed_UV = UV+((deformation_sample)-vec2(0.5)*vec2(box_padding)/box_size)*wobble_amount; + float rounded_box = length(max(abs(deformed_UV*(box_size+vec2(box_padding))-vec2(0.5)*(box_size+vec2(box_padding)))+adjusted_radius-vec2(0.5)*box_size,0))-adjusted_radius; + COLOR.a = min(smoothstep(0.0, -1, rounded_box), COLOR.a); } diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd index 05781033a..669c2e2ff 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd @@ -1,57 +1,58 @@ extends Control -class_name DialogicNode_TextBubble -var speaker_node : Node = null -var character : DialogicCharacter = null +@onready var tail: Line2D = ($Group/Tail as Line2D) +@onready var bubble: Control = ($Group/Background as Control) +@onready var text: DialogicNode_DialogText = (%DialogText as DialogicNode_DialogText) +# The choice container is added by the TextBubble layer +@onready var choice_container: Container = null +@onready var name_label: Label = (%NameLabel as Label) +@onready var name_label_box: PanelContainer = (%NameLabelPanel as PanelContainer) +@onready var name_label_holder: HBoxContainer = $DialogText/NameLabelPositioner + +var node_to_point_at: Node = null +var current_character: DialogicCharacter = null + var max_width := 300 -var bubble_rect : Rect2 = Rect2(0.0, 0.0, 2.0, 2.0) +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() +var name_label_alignment := HBoxContainer.ALIGNMENT_BEGIN +var name_label_offset := Vector2() +var force_choices_on_separate_lines := false -func get_tail() -> Line2D: - return $Tail - - -func get_bubble() -> Control: - return $Background - - -func get_choice_container() -> Container: - return $DialogText/ChoiceContainer - - -func get_name_label_panel() -> PanelContainer: - return $DialogText/NameLabel - - -func get_name_label() -> DialogicNode_NameLabel: - return %NameLabel +# Sets the padding shader paramter. +# It's the amount of spacing around the background to allow some wobbeling. +var bg_padding := 30 -func get_dialog_text() -> DialogicNode_DialogText: - return %DialogText +func _ready() -> void: + reset() + DialogicUtil.autoload().Choices.question_shown.connect(_on_question_shown) -func _ready() -> void: +func reset() -> void: scale = Vector2.ZERO modulate.a = 0.0 - if speaker_node: - position = speaker_node.get_global_transform_with_canvas().origin + tail.points = [] + bubble_rect = Rect2(0,0,2,2) + + base_position = get_speaker_canvas_position() + position = base_position -func _process(delta): - if speaker_node: - base_position = speaker_node.get_global_transform_with_canvas().origin + +func _process(delta:float) -> void: + base_position = get_speaker_canvas_position() 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 dist_x := absf(base_position.x - center.x) + var dist_y := absf(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) @@ -62,15 +63,17 @@ func _process(delta): 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) + 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) + position = position.lerp(p, 5 * delta) - var point_a : Vector2 = Vector2.ZERO - var point_b : Vector2 = (base_position - position) * 0.5 + var point_a: Vector2 = Vector2.ZERO + var point_b: Vector2 = (base_position - position) * 0.75 - var offset = Vector2.from_angle(point_a.angle_to_point(point_b)) * bubble_rect.size * abs(direction.x) * 0.4 + var offset: Vector2 = 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 @@ -79,59 +82,121 @@ func _process(delta): 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) - get_tail().points = curve.tessellate(5) - get_tail().width = bubble_rect.size.x * 0.15 + tail.points = curve.tessellate(5) + tail.width = bubble_rect.size.x * 0.15 func open() -> void: + set_process(true) show() - get_dialog_text().enabled = true + text.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: - get_dialog_text().enabled = false + text.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) + close_tween.tween_property(self, "scale", Vector2.ONE * 0.8, 0.2) + close_tween.tween_property(self, "modulate:a", 0.0, 0.2) await close_tween.finished hide() + set_process(false) -func _on_dialog_text_started_revealing_text(): - var dialog_text : DialogicNode_DialogText = get_dialog_text() - var font :Font = dialog_text.get_theme_font("normal_font") - dialog_text.size = font.get_multiline_string_size(dialog_text.get_parsed_text(), HORIZONTAL_ALIGNMENT_LEFT, max_width, dialog_text.get_theme_font_size("normal_font_size")) - if DialogicUtil.autoload().Choices.is_question(DialogicUtil.autoload().current_event_idx): - font = $DialogText/ChoiceContainer/DialogicNode_ChoiceButton.get_theme_font('font') - dialog_text.size.y += font.get_string_size(dialog_text.get_parsed_text(), HORIZONTAL_ALIGNMENT_LEFT, max_width, $DialogText/ChoiceContainer/DialogicNode_ChoiceButton.get_theme_font_size("font_size")).y - dialog_text.position = -dialog_text.size/2 +func _on_dialog_text_started_revealing_text() -> void: + _resize_bubble(get_base_content_size(), true) - _resize_bubble() - -func _resize_bubble() -> void: - var bubble : Control = get_bubble() - var bubble_size :Vector2 = get_dialog_text().size+(padding*2) - var half_size :Vector2= (bubble_size / 2.0) - get_dialog_text().pivot_offset = half_size +func _resize_bubble(content_size:Vector2, popup:=false) -> void: + var bubble_size: Vector2 = content_size+(padding*2)+Vector2.ONE*bg_padding + var half_size: Vector2= (bubble_size / 2.0) bubble.pivot_offset = half_size bubble_rect = Rect2(position, bubble_size * Vector2(1.1, 1.1)) - bubble.size = bubble_size bubble.position = -half_size + bubble.size = bubble_size - var t : Tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK) - t.tween_property(bubble, ^"scale", Vector2.ONE, 0.2).from(Vector2.ZERO) + text.size = content_size + text.position = -(content_size/2.0) - # 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 + if popup: + 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) else: - bubble_ratio.x = bubble_rect.size.x / bubble_rect.size.y - - bubble.material.set(&"shader_parameter/ratio", bubble_ratio) - + bubble.scale = Vector2.ONE + + bubble.material.set(&"shader_parameter/box_size", bubble_size) + name_label_holder.position = Vector2(0, bubble.position.y - text.position.y - name_label_holder.size.y/2.0) + name_label_holder.position += name_label_offset + name_label_holder.alignment = name_label_alignment + name_label_holder.size.x = text.size.x + + +func _on_question_shown(info:Dictionary) -> void: + if !is_visible_in_tree(): + return + + await get_tree().process_frame + + var content_size := get_base_content_size() + content_size.y += choice_container.size.y + content_size.x = max(content_size.x, choice_container.size.x) + _resize_bubble(content_size) + + +func get_base_content_size() -> Vector2: + var font: Font = text.get_theme_font(&"normal_font") + return font.get_multiline_string_size( + text.get_parsed_text(), + HORIZONTAL_ALIGNMENT_LEFT, + max_width, + text.get_theme_font_size(&"normal_font_size") + ) + + +func add_choice_container(node:Container, alignment:=FlowContainer.ALIGNMENT_BEGIN) -> void: + if choice_container: + choice_container.get_parent().remove_child(choice_container) + choice_container.queue_free() + + node.name = "ChoiceContainer" + choice_container = node + node.set_anchors_preset(LayoutPreset.PRESET_BOTTOM_WIDE) + node.grow_vertical = Control.GROW_DIRECTION_BEGIN + text.add_child(node) + + if node is HFlowContainer: + (node as HFlowContainer).alignment = alignment + + for i:int in range(5): + choice_container.add_child(DialogicNode_ChoiceButton.new()) + if node is HFlowContainer: + continue + match alignment: + HBoxContainer.ALIGNMENT_BEGIN: + (choice_container.get_child(-1) as Control).size_flags_horizontal = SIZE_SHRINK_BEGIN + HBoxContainer.ALIGNMENT_CENTER: + (choice_container.get_child(-1) as Control).size_flags_horizontal = SIZE_SHRINK_CENTER + HBoxContainer.ALIGNMENT_END: + (choice_container.get_child(-1) as Control).size_flags_horizontal = SIZE_SHRINK_END + + for child:Button in choice_container.get_children(): + var prev := child.get_parent().get_child(wrap(child.get_index()-1, 0, choice_container.get_child_count()-1)).get_path() + var next := child.get_parent().get_child(wrap(child.get_index()+1, 0, choice_container.get_child_count()-1)).get_path() + child.focus_next = next + child.focus_previous = prev + child.focus_neighbor_left = prev + child.focus_neighbor_top = prev + child.focus_neighbor_right = next + child.focus_neighbor_bottom = next + + +func get_speaker_canvas_position() -> Vector2: + if is_instance_valid(node_to_point_at): + if node_to_point_at is Node3D: + base_position = get_viewport().get_camera_3d().unproject_position( + (node_to_point_at as Node3D).global_position) + if node_to_point_at is CanvasItem: + base_position = (node_to_point_at as CanvasItem).get_global_transform_with_canvas().origin + return base_position diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gdshader b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gdshader new file mode 100644 index 000000000..60ebcee12 --- /dev/null +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gdshader @@ -0,0 +1,17 @@ +shader_type canvas_item; + +uniform sampler2D deformation_sampler : filter_linear, repeat_enable; +uniform float radius : hint_range(1.0, 200, 0.01) = 25; +uniform vec2 box_size = vec2(100, 100); +uniform float box_padding = 15; +uniform float wobble_amount : hint_range(0.0, 1.0, 0.01) = 0.2; +uniform float wobble_speed : hint_range(0.0, 10.0, 0.01) = 1; +uniform float wobble_detail : hint_range(0.01, 1, 0.01) = 0.5; + +void fragment() { + float adjusted_radius = min(min(radius, box_size.x/2.0), box_size.y/2.0); + vec2 deformation_sample = texture(deformation_sampler, UV*wobble_detail+TIME*wobble_speed*0.05).xy*(vec2(box_padding)/box_size)*0.9; + vec2 deformed_UV = UV+((deformation_sample)-vec2(0.5)*vec2(box_padding)/box_size)*wobble_amount; + float rounded_box = length(max(abs(deformed_UV*(box_size+vec2(box_padding))-vec2(0.5)*(box_size+vec2(box_padding)))+adjusted_radius-vec2(0.5)*box_size,0))-adjusted_radius; + COLOR.a = min(smoothstep(0.0, -1, rounded_box), COLOR.a); +} diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.tscn index 4e0d4cc77..6277c5f5b 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.tscn @@ -1,23 +1,15 @@ -[gd_scene load_steps=17 format=3 uid="uid://dlx7jcvm52tyw"] +[gd_scene load_steps=11 format=3 uid="uid://dlx7jcvm52tyw"] [ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd" id="1_jdhpk"] -[ext_resource type="Shader" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader" id="2_1mhvf"] +[ext_resource type="Shader" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_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/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 @@ -27,64 +19,51 @@ cellular_jitter = 0.15 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"] +resource_local_to_scene = true 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/radius = 200.0 +shader_parameter/box_size = Vector2(100, 100) +shader_parameter/box_padding = 10.0 +shader_parameter/wobble_amount = 0.75 +shader_parameter/wobble_speed = 10.0 +shader_parameter/wobble_detail = 0.51 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) +bg_color = Color(1, 1, 1, 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_color = Color(0.152941, 0.152941, 0.152941, 0.12549) 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 -mouse_filter = 2 script = ExtResource("1_jdhpk") -[node name="Tail" type="Line2D" parent="."] +[node name="Group" type="CanvasGroup" parent="."] + +[node name="Tail" type="Line2D" parent="Group"] +unique_name_in_owner = true +points = PackedVector2Array(-9, 7, -29, 118, -95, 174, -193, 195) width = 96.0 width_curve = SubResource("Curve_0j8nu") -[node name="Background" type="ColorRect" parent="."] +[node name="Background" type="ColorRect" parent="Group"] +unique_name_in_owner = true material = SubResource("ShaderMaterial_60xbe") -layout_mode = 1 -offset_left = -69.0 -offset_top = -21.0 -offset_right = 225.0 -offset_bottom = 79.0 +offset_left = -115.0 +offset_top = -69.0 +offset_right = 108.0 +offset_bottom = 83.0 mouse_filter = 2 -[node name="DialogText" type="RichTextLabel" parent="."] +[node name="DialogText" type="RichTextLabel" parent="." node_paths=PackedStringArray("textbox_root")] unique_name_in_owner = true clip_contents = false layout_mode = 1 @@ -99,77 +78,34 @@ offset_right = 53.0 offset_bottom = 12.0 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 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") +textbox_root = NodePath("..") [node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="DialogText"] script = ExtResource("4_7bm4b") -[node name="NameLabel" type="PanelContainer" parent="DialogText"] +[node name="NameLabelPositioner" type="HBoxContainer" parent="DialogText"] layout_mode = 1 -anchors_preset = -1 -offset_left = 16.0 -offset_top = -26.0 -offset_right = 27.0 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 23.0 grow_horizontal = 2 -mouse_filter = 2 +alignment = 1 + +[node name="NameLabelPanel" type="PanelContainer" parent="DialogText/NameLabelPositioner"] +unique_name_in_owner = true +layout_mode = 2 theme_override_styles/panel = SubResource("StyleBoxFlat_h6ls0") -[node name="NameLabel" type="Label" parent="DialogText/NameLabel" node_paths=PackedStringArray("name_label_root")] +[node name="NameLabel" type="Label" parent="DialogText/NameLabelPositioner/NameLabelPanel" node_paths=PackedStringArray("name_label_root")] unique_name_in_owner = true layout_mode = 2 horizontal_alignment = 1 script = ExtResource("6_5gd03") name_label_root = NodePath("..") - -[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 -mouse_filter = 2 -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") +use_character_color = false [connection signal="started_revealing_text" from="DialogText" to="." method="_on_dialog_text_started_revealing_text"] diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd index c155de286..27644745f 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd @@ -1,7 +1,7 @@ @tool extends DialogicLayoutLayer -## This layout won't do anything on it's own +## This layout won't do anything on its own @export_group("Main") @export_subgroup("Text") @@ -17,60 +17,69 @@ extends DialogicLayoutLayer @export var box_modulate: Color = Color.WHITE @export var box_modulate_by_character_color: bool = false @export var box_padding: Vector2 = Vector2(10,10) -@export_range(0.1, 2) var box_corner_radius: float = 0.3 -@export_range(0.1, 5) var box_wobble_speed: float= 1 -@export_range(0, 1) var box_wobbliness: float = 0.2 +@export_range(1, 999) var box_corner_radius: int = 25 +@export_range(0.1, 5) var box_wobble_speed: float = 1 +@export_range(0, 1) var box_wobble_amount: float = 0.5 +@export_range(0, 1) var box_wobble_detail: float = 0.2 @export_subgroup('Behaviour') @export var behaviour_distance: int = 50 @export var behaviour_direction: Vector2 = Vector2(1, -1) -@export_group('Name Label & Choices') +@export_group('Name Label') @export_subgroup("Name Label") @export var name_label_enabled: bool = true @export var name_label_font_size: int = 15 @export_file('*.ttf') var name_label_font: String = "" @export var name_label_use_character_color: bool = true @export var name_label_color: Color = Color.BLACK +@export_subgroup("Name Label Box") @export var name_label_box_modulate: Color = Color.WHITE +@export var name_label_box_modulate_use_character_color: bool = false @export var name_label_padding: Vector2 = Vector2(5,0) @export var name_label_offset: Vector2 = Vector2(0,0) +@export var name_label_alignment := HBoxContainer.ALIGNMENT_BEGIN + +@export_group('Choices') @export_subgroup('Choices Text') @export var choices_text_size: int = 15 -@export var choices_text_color: Color = Color.LIGHT_SLATE_GRAY -@export var choices_text_color_hover: Color = Color.DARK_GRAY -@export var choices_text_color_focus: Color = Color.BLACK +@export_file('*.ttf') var choices_text_font: String = "" +@export var choices_text_color: Color = Color.DARK_SLATE_GRAY +@export var choices_text_color_hover: Color = Color.DARK_MAGENTA +@export var choices_text_color_focus: Color = Color.DARK_MAGENTA +@export var choices_text_color_disabled: Color = Color.DARK_GRAY + +@export_subgroup('Choices Layout') +@export var choices_layout_alignment := FlowContainer.ALIGNMENT_END +@export var choices_layout_force_lines: bool = false +@export_file('*.tres', "*.res") var choices_base_theme: String = "" +const TextBubble := preload("res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd") -var bubbles: Array[DialogicNode_TextBubble] = [] -var fallback_bubble: DialogicNode_TextBubble = null +var bubbles: Array[TextBubble] = [] +var fallback_bubble: TextBubble = null -@export_group('Private') -@export var textbubble_scene: PackedScene = null +const textbubble_scene: PackedScene = preload("res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.tscn") -func add_bubble() -> DialogicNode_TextBubble: - var new_bubble: DialogicNode_TextBubble = textbubble_scene.instantiate() +func add_bubble() -> TextBubble: + var new_bubble: TextBubble = textbubble_scene.instantiate() add_child(new_bubble) - bubble_apply_overrides(new_bubble) bubbles.append(new_bubble) return new_bubble - ## Called by dialogic whenever export overrides might change func _apply_export_overrides() -> void: - for bubble: DialogicNode_TextBubble in bubbles: - bubble_apply_overrides(bubble) + pass - if fallback_bubble: - bubble_apply_overrides(fallback_bubble) -func bubble_apply_overrides(bubble:DialogicNode_TextBubble) -> void: +## Called by the base layer before opening the bubble +func bubble_apply_overrides(bubble:TextBubble) -> void: ## TEXT FONT AND COLOR - var rtl: RichTextLabel = bubble.get_dialog_text() + var rtl: RichTextLabel = bubble.text 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) @@ -91,55 +100,86 @@ func bubble_apply_overrides(bubble:DialogicNode_TextBubble) -> void: ## BOX & TAIL COLOR - var tail: Line2D = bubble.get_tail() - var background: Control = bubble.get_bubble() - var bubble_material: ShaderMaterial = background.get(&'material') - - tail.default_color = box_modulate - background.set(&'color', box_modulate) - bubble_material.set_shader_parameter(&'radius', box_corner_radius) - bubble_material.set_shader_parameter(&'crease', box_wobbliness*0.1) - bubble_material.set_shader_parameter(&'speed', box_wobble_speed) - if box_modulate_by_character_color and bubble.character != null: - tail.modulate = bubble.character.color - background.modulate = bubble.character.color + var tail_and_bg_group := (bubble.get_node("Group") as CanvasGroup) + tail_and_bg_group.self_modulate = box_modulate + if box_modulate_by_character_color and bubble.current_character != null: + tail_and_bg_group.self_modulate = bubble.current_character.color + + var background := (bubble.get_node('%Background') as ColorRect) + var bg_material: ShaderMaterial = (background.material as ShaderMaterial) + bg_material.set_shader_parameter(&'radius', box_corner_radius) + bg_material.set_shader_parameter(&'wobble_amount', box_wobble_amount) + bg_material.set_shader_parameter(&'wobble_speed', box_wobble_speed) + bg_material.set_shader_parameter(&'wobble_detail', box_wobble_detail) + bubble.padding = box_padding + + ## BEHAVIOUR + bubble.safe_zone = behaviour_distance + bubble.base_direction = behaviour_direction + + ## NAME LABEL SETTINGS - var nl: DialogicNode_NameLabel = bubble.get_name_label() + var nl: DialogicNode_NameLabel = bubble.name_label 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) as Font) - nl.use_character_color = name_label_use_character_color - if !nl.use_character_color: + + if name_label_use_character_color and bubble.current_character: + nl.add_theme_color_override(&"font_color", bubble.current_character.color) + else: nl.add_theme_color_override(&"font_color", name_label_color) - var nlp: PanelContainer = bubble.get_name_label_panel() + var nlp: PanelContainer = bubble.name_label_box nlp.self_modulate = name_label_box_modulate + if name_label_box_modulate_use_character_color and bubble.current_character: + nlp.self_modulate = bubble.current_character.color 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() - + bubble.name_label_offset = name_label_offset + bubble.name_label_alignment = name_label_alignment + + nlp.get_parent().visible = name_label_enabled ## CHOICE SETTINGS - var choice_theme: Theme = Theme.new() + if choices_layout_force_lines: + bubble.add_choice_container(VBoxContainer.new(), choices_layout_alignment) + else: + bubble.add_choice_container(HFlowContainer.new(), choices_layout_alignment) + + var choice_theme: Theme = null + if choices_base_theme.is_empty() or not ResourceLoader.exists(choices_base_theme): + choice_theme = Theme.new() + var base_style := StyleBoxFlat.new() + base_style.draw_center = false + base_style.border_width_bottom = 2 + base_style.border_color = choices_text_color + choice_theme.set_stylebox(&'normal', &'Button', base_style) + var focus_style := (base_style.duplicate() as StyleBoxFlat) + focus_style.border_color = choices_text_color_focus + choice_theme.set_stylebox(&'focus', &'Button', focus_style) + var hover_style := (base_style.duplicate() as StyleBoxFlat) + hover_style.border_color = choices_text_color_hover + choice_theme.set_stylebox(&'hover', &'Button', hover_style) + var disabled_style := (base_style.duplicate() as StyleBoxFlat) + disabled_style.border_color = choices_text_color_disabled + choice_theme.set_stylebox(&'disabled', &'Button', disabled_style) + choice_theme.set_stylebox(&'pressed', &'Button', base_style) + else: + choice_theme = (load(choices_base_theme) as Theme) + + if !choices_text_font.is_empty(): + choice_theme.default_font = (load(choices_text_font) as Font) + 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_choice_container().theme = choice_theme - - ## BEHAVIOUR - bubble.safe_zone = behaviour_distance - bubble.base_direction = behaviour_direction - - + choice_theme.set_color(&'font_disabled_color', &'Button', choices_text_color_disabled) + bubble.choice_container.theme = choice_theme diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.tscn index 6ed91452c..5909325e3 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.tscn @@ -1,11 +1,9 @@ -[gd_scene load_steps=3 format=3 uid="uid://d2it0xiap3gnt"] +[gd_scene load_steps=2 format=3 uid="uid://d2it0xiap3gnt"] [ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd" id="1_b37je"] -[ext_resource type="PackedScene" uid="uid://dlx7jcvm52tyw" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.tscn" id="2_xncjh"] [node name="TextBubbleLayer" type="Control"] layout_mode = 3 anchors_preset = 0 mouse_filter = 2 script = ExtResource("1_b37je") -textbubble_scene = ExtResource("2_xncjh") diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.gd index a0371f0e5..97195f5bb 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.gd @@ -31,6 +31,7 @@ extends DialogicLayoutLayer @export var boxes_v_separation: int = 10 @export var boxes_fill_width: bool = true @export var boxes_min_size: Vector2 = Vector2() +@export var boxes_offset: Vector2 = Vector2() @export_group('Sounds') @export_range(-80, 24, 0.01) var sounds_volume: float = -10 @@ -96,6 +97,7 @@ func _apply_export_overrides() -> void: layer_theme.set_stylebox(&'focus', &'Button', load(boxes_stylebox_focused) as StyleBox) get_choices().add_theme_constant_override(&"separation", boxes_v_separation) + self.position = boxes_offset for child: Node in get_choices().get_children(): if not child is DialogicNode_ChoiceButton: @@ -109,6 +111,7 @@ func _apply_export_overrides() -> void: choice.custom_minimum_size = boxes_min_size + set(&'theme', layer_theme) # apply sound settings diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn index 75482d9e0..7c9fb00f3 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn @@ -25,10 +25,10 @@ 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 +offset_left = -41.0 +offset_top = -47.0 +offset_right = 42.0 +offset_bottom = 47.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.tscn index b5e79f022..bb803eeb5 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.tscn @@ -25,58 +25,60 @@ mouse_filter = 2 [node name="DialogicNode_PortraitContainer1" type="Control" parent="Portraits"] layout_mode = 1 -anchor_right = 0.231771 +anchor_right = 0.2 anchor_bottom = 1.0 +offset_right = -1.52588e-05 grow_vertical = 2 +pivot_offset = Vector2(115.2, 648) mouse_filter = 2 script = ExtResource("1_rxdcc") +container_ids = PackedStringArray("leftmost", "0") metadata/_edit_use_anchors_ = true [node name="DialogicNode_PortraitContainer2" type="Control" parent="Portraits"] layout_mode = 1 -anchor_left = 0.190104 -anchor_right = 0.401042 +anchor_left = 0.2 +anchor_right = 0.4 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_rxdcc") -position_index = 1 +container_ids = PackedStringArray("left", "1") metadata/_edit_use_anchors_ = true [node name="DialogicNode_PortraitContainer3" type="Control" parent="Portraits"] layout_mode = 1 -anchor_left = 0.371528 -anchor_right = 0.625868 +anchor_left = 0.4 +anchor_right = 0.6 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_rxdcc") -position_index = 2 +container_ids = PackedStringArray("center", "middle", "2") metadata/_edit_use_anchors_ = true [node name="DialogicNode_PortraitContainer4" type="Control" parent="Portraits"] layout_mode = 1 -anchor_left = 0.592882 -anchor_right = 0.805556 -anchor_bottom = 0.996914 +anchor_left = 0.6 +anchor_right = 0.8 +anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_rxdcc") -position_index = 3 +container_ids = PackedStringArray("right", "3") metadata/_edit_use_anchors_ = true [node name="DialogicNode_PortraitContainer5" type="Control" parent="Portraits"] layout_mode = 1 -anchor_left = 0.776042 -anchor_top = -0.00462963 -anchor_right = 1.00434 +anchor_left = 0.8 +anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_rxdcc") -position_index = 4 +container_ids = PackedStringArray("rightmost", "4") metadata/_edit_use_anchors_ = true diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd index 43eca1f7d..c03b3ee0d 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd @@ -7,11 +7,12 @@ enum AnimationsIn {NONE, POP_IN, FADE_UP} enum AnimationsOut {NONE, POP_OUT, FADE_DOWN} enum AnimationsNewText {NONE, WIGGLE} -var animation_in : AnimationsIn -var animation_out : AnimationsOut -var animation_new_text : AnimationsNewText +var animation_in: AnimationsIn +var animation_out: AnimationsOut +var animation_new_text: AnimationsNewText + +var full_clear := true -var full_clear : bool = true func get_text_panel() -> PanelContainer: return %DialogTextPanel @@ -22,19 +23,20 @@ func get_dialog() -> DialogicNode_DialogText: func _ready() -> void: - var text_system : Node = DialogicUtil.autoload().get(&'Text') - var _error : int = 0 - _error = text_system.connect(&'animation_textbox_hide', _on_textbox_hide) - _error = text_system.connect(&'animation_textbox_show', _on_textbox_show) - _error = text_system.connect(&'animation_textbox_new_text', _on_textbox_new_text) - _error = text_system.connect(&'about_to_show_text', _on_about_to_show_text) + var text_system: Node = DialogicUtil.autoload().get(&'Text') + text_system.connect(&'animation_textbox_hide', _on_textbox_hide) + text_system.connect(&'animation_textbox_show', _on_textbox_show) + text_system.connect(&'animation_textbox_new_text', _on_textbox_new_text) + text_system.connect(&'about_to_show_text', _on_about_to_show_text) + var animation_system: Node = DialogicUtil.autoload().get(&'Animations') + animation_system.connect(&'animation_interrupted', _on_animation_interrupted) func _on_textbox_show() -> void: if animation_in == AnimationsIn.NONE: return play('RESET') - var animation_system : Node = DialogicUtil.autoload().get(&'Animations') + var animation_system: Node = DialogicUtil.autoload().get(&'Animations') animation_system.call(&'start_animating') get_text_panel().get_parent().get_parent().set(&'modulate', Color.TRANSPARENT) get_dialog().text = "" @@ -43,15 +45,15 @@ func _on_textbox_show() -> void: play("textbox_pop") AnimationsIn.FADE_UP: play("textbox_fade_up") - if not is_connected(&'animation_finished', Callable(animation_system, &'animation_finished')): - var _error : int = connect(&'animation_finished', Callable(animation_system, &'animation_finished'), CONNECT_ONE_SHOT) + if not animation_finished.is_connected(Callable(animation_system, &'animation_finished')): + animation_finished.connect(Callable(animation_system, &'animation_finished'), CONNECT_ONE_SHOT) func _on_textbox_hide() -> void: if animation_out == AnimationsOut.NONE: return play('RESET') - var animation_system : Node = DialogicUtil.autoload().get(&'Animations') + var animation_system: Node = DialogicUtil.autoload().get(&'Animations') animation_system.call(&'start_animating') match animation_out: AnimationsOut.POP_OUT: @@ -59,8 +61,8 @@ func _on_textbox_hide() -> void: AnimationsOut.FADE_DOWN: play_backwards("textbox_fade_up") - if not is_connected(&'animation_finished', Callable(animation_system, &'animation_finished')): - var _error : int = connect(&'animation_finished', Callable(animation_system, &'animation_finished'), CONNECT_ONE_SHOT) + if not animation_finished.is_connected(Callable(animation_system, &'animation_finished')): + animation_finished.connect(Callable(animation_system, &'animation_finished'), CONNECT_ONE_SHOT) func _on_about_to_show_text(info:Dictionary) -> void: @@ -74,7 +76,7 @@ func _on_textbox_new_text() -> void: if animation_new_text == AnimationsNewText.NONE: return - var animation_system : Node = DialogicUtil.autoload().get(&'Animation') + var animation_system: Node = DialogicUtil.autoload().get(&'Animations') animation_system.call(&'start_animating') if full_clear: get_dialog().text = "" @@ -82,5 +84,10 @@ func _on_textbox_new_text() -> void: AnimationsNewText.WIGGLE: play("new_text") - if not is_connected(&'animation_finished', Callable(animation_system, &'animation_finished')): - var _error : int = connect(&'animation_finished', Callable(animation_system, &'animation_finished'), CONNECT_ONE_SHOT) + if not animation_finished.is_connected(Callable(animation_system, &'animation_finished')): + animation_finished.connect(Callable(animation_system, &'animation_finished'), CONNECT_ONE_SHOT) + + +func _on_animation_interrupted() -> void: + if is_playing(): + stop() diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/autoadvance_indicator.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/autoadvance_indicator.gd index fa8bbffcb..a4384bd60 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/autoadvance_indicator.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/autoadvance_indicator.gd @@ -1,6 +1,6 @@ extends Range -var enabled : bool = true +var enabled: bool = true func _process(_delta : float) -> void: if !enabled: diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.gd index 8a1645394..95a4ce3cb 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.gd @@ -38,10 +38,10 @@ enum AnimationsNewText {NONE, WIGGLE} @export_subgroup('Font') @export var text_use_global_font: bool = true -@export_file('*.ttf', '*.tres') var normal_font:String = "" -@export_file('*.ttf', '*.tres') var bold_font:String = "" -@export_file('*.ttf', '*.tres') var italics_font:String = "" -@export_file('*.ttf', '*.tres') var bold_italics_font:String = "" +@export_file('*.ttf', '*.tres') var normal_font: String = "" +@export_file('*.ttf', '*.tres') var bold_font: String = "" +@export_file('*.ttf', '*.tres') var italics_font: String = "" +@export_file('*.ttf', '*.tres') var bold_italics_font: String = "" @export_group("Box") @@ -246,8 +246,8 @@ func _apply_indicator_settings() -> void: next_indicator.enabled = next_indicator_enabled if next_indicator_enabled: - next_indicator.animation = next_indicator_animation - if FileAccess.file_exists(next_indicator_texture): + next_indicator.animation = next_indicator_animation as DialogicNode_NextIndicator.Animations + if ResourceLoader.exists(next_indicator_texture): next_indicator.texture = load(next_indicator_texture) next_indicator.show_on_questions = next_indicator_show_on_questions next_indicator.show_on_autoadvance = next_indicator_show_on_autoadvance diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.tscn b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.tscn index f8f8e4203..5521ef82a 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/vn_textbox_layer.tscn @@ -258,11 +258,11 @@ offset_top = -50.0 offset_right = 150.0 grow_horizontal = 2 grow_vertical = 0 +mouse_filter = 2 [node name="DialogTextPanel" type="PanelContainer" parent="Anchor/AnimationParent/Sizer"] unique_name_in_owner = true self_modulate = Color(0.00784314, 0.00784314, 0.00784314, 0.843137) -custom_minimum_size = Vector2(300, 50) layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -298,6 +298,7 @@ unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 8 size_flags_vertical = 8 +mouse_filter = 2 script = ExtResource("5_40a50") show_on_questions = true texture = ExtResource("6_uch03") @@ -309,6 +310,7 @@ modulate = Color(1, 1, 1, 0.188235) custom_minimum_size = Vector2(0, 10) layout_mode = 2 size_flags_vertical = 8 +mouse_filter = 2 max_value = 1.0 step = 0.001 value = 0.5 @@ -326,6 +328,7 @@ layout_mode = 1 offset_top = -50.0 offset_right = 9.0 offset_bottom = -25.0 +mouse_filter = 2 theme_override_styles/panel = ExtResource("9_yg8ig") metadata/_edit_layout_mode = 1 metadata/_edit_use_custom_anchors = true diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres b/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres index 627d991c2..9ff8d5b21 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres +++ b/addons/dialogic/Modules/DefaultLayoutParts/Style_SpeakerTextbox/speaker_textbox_style.tres @@ -15,14 +15,14 @@ script = ExtResource("2_i34tx") scene = ExtResource("1_sde84") overrides = {} -[sub_resource type="Resource" id="Resource_gc1b5"] +[sub_resource type="Resource" id="Resource_x576n"] script = ExtResource("2_i34tx") -scene = ExtResource("3_epko4") +scene = ExtResource("4_8y2vo") overrides = {} -[sub_resource type="Resource" id="Resource_x576n"] +[sub_resource type="Resource" id="Resource_gc1b5"] script = ExtResource("2_i34tx") -scene = ExtResource("4_8y2vo") +scene = ExtResource("3_epko4") overrides = {} [sub_resource type="Resource" id="Resource_otikm"] @@ -51,4 +51,4 @@ name = "Speaker Textbox Style" base_overrides = { "global_bg_color": "Color(0.298039, 0.2, 0.113725, 0.901961)" } -layers = Array[ExtResource("2_i34tx")]([SubResource("Resource_35sbo"), SubResource("Resource_gc1b5"), SubResource("Resource_x576n"), SubResource("Resource_otikm"), SubResource("Resource_w8ec6"), SubResource("Resource_qmo1y"), SubResource("Resource_legp8")]) +layers = Array[ExtResource("2_i34tx")]([SubResource("Resource_35sbo"), SubResource("Resource_x576n"), SubResource("Resource_gc1b5"), SubResource("Resource_otikm"), SubResource("Resource_w8ec6"), SubResource("Resource_qmo1y"), SubResource("Resource_legp8")]) diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres b/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres index 7413de116..d9200d673 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres +++ b/addons/dialogic/Modules/DefaultLayoutParts/Style_VN_Default/default_vn_style.tres @@ -21,14 +21,14 @@ script = ExtResource("2_3b8ue") scene = ExtResource("4_q1t5h") overrides = {} -[sub_resource type="Resource" id="Resource_adxfb"] +[sub_resource type="Resource" id="Resource_eqyxb"] script = ExtResource("2_3b8ue") -scene = ExtResource("5_o6sv8") +scene = ExtResource("6_j6olx") overrides = {} -[sub_resource type="Resource" id="Resource_eqyxb"] +[sub_resource type="Resource" id="Resource_adxfb"] script = ExtResource("2_3b8ue") -scene = ExtResource("6_j6olx") +scene = ExtResource("5_o6sv8") overrides = {} [sub_resource type="Resource" id="Resource_nmutb"] @@ -55,4 +55,4 @@ overrides = {} script = ExtResource("1_mvpc0") name = "Visual Novel Style" base_overrides = {} -layers = Array[ExtResource("2_3b8ue")]([SubResource("Resource_x8thn"), SubResource("Resource_g5yti"), SubResource("Resource_adxfb"), SubResource("Resource_eqyxb"), SubResource("Resource_nmutb"), SubResource("Resource_dwo52"), SubResource("Resource_by0l6"), SubResource("Resource_fd6co")]) +layers = Array[ExtResource("2_3b8ue")]([SubResource("Resource_x8thn"), SubResource("Resource_g5yti"), SubResource("Resource_eqyxb"), SubResource("Resource_adxfb"), SubResource("Resource_nmutb"), SubResource("Resource_dwo52"), SubResource("Resource_by0l6"), SubResource("Resource_fd6co")]) diff --git a/addons/dialogic/Modules/End/event_end.gd b/addons/dialogic/Modules/End/event_end.gd index 0e6738365..b853119a7 100644 --- a/addons/dialogic/Modules/End/event_end.gd +++ b/addons/dialogic/Modules/End/event_end.gd @@ -38,7 +38,7 @@ func get_shortcode() -> String: #region EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_label('End Timeline') #endregion diff --git a/addons/dialogic/Modules/Glossary/event_glossary.gd b/addons/dialogic/Modules/Glossary/event_glossary.gd index a9881e5df..d8fce3ac2 100644 --- a/addons/dialogic/Modules/Glossary/event_glossary.gd +++ b/addons/dialogic/Modules/Glossary/event_glossary.gd @@ -38,5 +38,5 @@ func get_shortcode_parameters() -> Dictionary: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: pass diff --git a/addons/dialogic/Modules/Glossary/glossary_editor.gd b/addons/dialogic/Modules/Glossary/glossary_editor.gd index a4e7de8dc..403ed6d72 100644 --- a/addons/dialogic/Modules/Glossary/glossary_editor.gd +++ b/addons/dialogic/Modules/Glossary/glossary_editor.gd @@ -62,7 +62,7 @@ func _open(_argument: Variant = null) -> void: var idx := 0 for file: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): - if FileAccess.file_exists(file): + if ResourceLoader.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')) @@ -86,7 +86,7 @@ func _on_GlossaryList_item_selected(idx: int) -> void: %EntryList.clear() var tooltip_item: String = %GlossaryList.get_item_tooltip(idx) - if FileAccess.file_exists(tooltip_item): + if ResourceLoader.exists(tooltip_item): var glossary_item := load(tooltip_item) if not glossary_item is DialogicGlossary: @@ -144,7 +144,7 @@ func _on_load_glossary_file_pressed() -> void: func load_glossary_file(path:String) -> void: - var list :Array= ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) if not path in list: list.append(path) diff --git a/addons/dialogic/Modules/Glossary/glossary_resource.gd b/addons/dialogic/Modules/Glossary/glossary_resource.gd index 9a9f43d0b..638d03697 100644 --- a/addons/dialogic/Modules/Glossary/glossary_resource.gd +++ b/addons/dialogic/Modules/Glossary/glossary_resource.gd @@ -10,7 +10,7 @@ extends Resource ## a string, representing the actual key for the key used. ## The string key-value pairs are the alias keys, they allow to redirect ## the actual glossary entry. -@export var entries: Dictionary = {} +@export var entries := {} ## If false, no entries from this glossary will be shown @export var enabled: bool = true @@ -37,21 +37,17 @@ const REGEX_OPTION_PROPERTY := "regex_options" ## Ignored when entries are translated. const PRIVATE_PROPERTY_PREFIX := "_" -const _MISSING_ENTRY_INDEX := -1 ## Private ID assigned when this glossary is translated. -@export var _translation_id: String = "" +@export var _translation_id := "" ## Private lookup table used to find the translation ID of a glossary entry. ## The keys (String) are all translated words that may trigger a glossary entry to ## be shown. ## The values (String) are the translation ID. -@export var _translation_keys: Dictionary = {} +@export var _translation_keys := {} -func __get_property_list() -> Array: - return [] - ## Removes an entry and all its aliases (alternative property) from ## the glossary. @@ -226,6 +222,8 @@ func get_set_regex_option(entry_key: String) -> String: return regex_option +#region ADD AND CLEAR TRANSLATION KEYS + ## This is automatically called, no need to use this. func add_translation_id() -> String: _translation_id = DialogicUtil.get_next_translation_id() @@ -260,6 +258,10 @@ func clear_translation_keys() -> void: _translation_keys.clear() +#endregion + + +#region GET AND SET TRANSLATION IDS AND KEYS ## Returns a key used to reference this glossary in the translation CSV file. ## @@ -283,19 +285,6 @@ func get_property_translation_key(entry_key: String, property: String) -> String return glossary_csv_key -## Returns the matching translation key for the given [param word]. -## This key can be used via [method tr] to get the translation. -## -## Time complexity: O(1) -## Uses an internal dictionary to find the translation key. -## This dictionary is generated when the glossary is translated. -## See [member _translation_keys]. -func get_word_translation_key(word: String) -> String: - if _translation_keys.has(word): - return _translation_keys[word] - - return "" - ## Returns the translation key prefix for this glossary. ## The resulting format will look like this: Glossary/a2/ @@ -348,3 +337,4 @@ func get_set_glossary_translation_id() -> String: return _translation_id +#endregion diff --git a/addons/dialogic/Modules/Glossary/subsystem_glossary.gd b/addons/dialogic/Modules/Glossary/subsystem_glossary.gd index 389069db9..d438bc4bc 100644 --- a/addons/dialogic/Modules/Glossary/subsystem_glossary.gd +++ b/addons/dialogic/Modules/Glossary/subsystem_glossary.gd @@ -10,6 +10,8 @@ var enabled := true ## Any key in this dictionary will overwrite the color for any item with that name. var color_overrides := {} +const SETTING_DEFAULT_COLOR := 'dialogic/glossary/default_color' + #region STATE #################################################################################################### @@ -31,7 +33,7 @@ func parse_glossary(text: String) -> String: 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 def_color: Color = ProjectSettings.get_setting(SETTING_DEFAULT_COLOR, Color.POWDER_BLUE) var regex := RegEx.new() for glossary: DialogicGlossary in glossaries: @@ -98,10 +100,6 @@ func add_glossary(path:String) -> void: ## Iterates over all glossaries and returns the first one that matches the ## [param entry_key]. ## -## Returns null if none of the glossaries has an entry with that key. -## If translation is enabled, uses the [param entry_key] as well to check -## [member _translation_keys]. -## ## Runtime complexity: ## O(n), where n is the number of glossaries. func find_glossary(entry_key: String) -> DialogicGlossary: @@ -110,7 +108,67 @@ func find_glossary(entry_key: String) -> DialogicGlossary: if glossary.entries.has(entry_key): return glossary - if glossary.entries.has(entry_key): - return glossary - return null + + +## Returns the first match for a given entry key. +## If translation is available and enabled, it will be translated +func get_entry(entry_key: String) -> Dictionary: + var glossary: DialogicGlossary = dialogic.Glossary.find_glossary(entry_key) + + var result := { + "title": "", + "text": "", + "extra": "", + "color": Color.WHITE, + } + + if glossary == null: + return {} + + var is_translation_enabled: bool = ProjectSettings.get_setting('dialogic/translation/enabled', false) + + var entry := glossary.get_entry(entry_key) + + if entry.is_empty(): + return {} + + result.color = entry.get("color") + if result.color == null: + result.color = ProjectSettings.get_setting(SETTING_DEFAULT_COLOR, Color.POWDER_BLUE) + + if is_translation_enabled and not glossary._translation_id.is_empty(): + var translation_key: String = glossary._translation_keys.get(entry_key) + var last_slash := translation_key.rfind('/') + + if last_slash == -1: + return {} + + var tr_base := translation_key.substr(0, last_slash) + + result.title = translate(tr_base, "title", entry) + result.text = translate(tr_base, "text", entry) + result.extra = translate(tr_base, "extra", entry) + else: + result.title = entry.get("title", "") + result.text = entry.get("text", "") + result.extra = entry.get("extra", "") + + ## PARSE TEXTS FOR VARIABLES + result.title = dialogic.VAR.parse_variables(result.title) + result.text = dialogic.VAR.parse_variables(result.text) + result.extra = dialogic.VAR.parse_variables(result.extra) + + return result + + + +## Tries to translate the property with the given +func translate(tr_base: String, property: StringName, fallback_entry: Dictionary) -> String: + var tr_key := tr_base.path_join(property) + var tr_value := tr(tr_key) + + if tr_key == tr_value: + tr_value = fallback_entry.get(property, "") + + return tr_value diff --git a/addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png b/addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png new file mode 100644 index 000000000..33a594c77 Binary files /dev/null and b/addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png differ diff --git a/addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png.import b/addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png.import new file mode 100644 index 000000000..5efd4a71f --- /dev/null +++ b/addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dtt0l7qbkknfx" +path="res://.godot/imported/highlight_portrait_thumbnail.png-6fa128edf7f6ec29e7a5313b9c3e7412.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Modules/HighlightPortrait/highlight_portrait_thumbnail.png" +dest_files=["res://.godot/imported/highlight_portrait_thumbnail.png-6fa128edf7f6ec29e7a5313b9c3e7412.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/HighlightPortrait/index.gd b/addons/dialogic/Modules/HighlightPortrait/index.gd new file mode 100644 index 000000000..ec9b31537 --- /dev/null +++ b/addons/dialogic/Modules/HighlightPortrait/index.gd @@ -0,0 +1,17 @@ +@tool +extends DialogicIndexer + + +func _get_portrait_scene_presets() -> Array[Dictionary]: + return [ + { + "path": this_folder.path_join("simple_highlight_portrait.tscn"), + "name": "Simple Highlight Portrait", + "description": "A portrait scene that displays a simple image, but changes color and moves to the front when this character is speaking.", + "author":"Dialogic", + "type": "General", + "icon":"", + "preview_image":[this_folder.path_join("highlight_portrait_thumbnail.png")], + "documentation":"", + }, + ] diff --git a/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd b/addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.gd similarity index 76% rename from addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd rename to addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.gd index 64aac03b4..5ed6649df 100644 --- a/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd +++ b/addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.gd @@ -2,10 +2,10 @@ extends DialogicPortrait @export_group('Main') -@export_file var image: String = "" +@export_file var image := "" var unhighlighted_color := Color.DARK_GRAY -var prev_z_index := 0 +var _prev_z_index := 0 ## Load anything related to the given character and portrait func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: @@ -19,12 +19,12 @@ func _ready() -> void: self.modulate = unhighlighted_color -func _highlight(): +func _highlight() -> void: create_tween().tween_property(self, 'modulate', Color.WHITE, 0.15) - prev_z_index = DialogicUtil.autoload().Portraits.get_character_info(character).get('z_index', 0) + _prev_z_index = DialogicUtil.autoload().Portraits.get_character_info(character).get('z_index', 0) DialogicUtil.autoload().Portraits.change_character_z_index(character, 99) -func _unhighlight(): +func _unhighlight() -> void: create_tween().tween_property(self, 'modulate', unhighlighted_color, 0.15) - DialogicUtil.autoload().Portraits.change_character_z_index(character, prev_z_index) + DialogicUtil.autoload().Portraits.change_character_z_index(character, _prev_z_index) diff --git a/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.tscn b/addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.tscn similarity index 61% rename from addons/dialogic/Example Assets/portraits/simple_highlight_portrait.tscn rename to addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.tscn index 44cb571c6..cf814ebb9 100644 --- a/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.tscn +++ b/addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://br18lgpga2y2v"] -[ext_resource type="Script" path="res://addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd" id="1_ceqva"] +[ext_resource type="Script" path="res://addons/dialogic/Modules/HighlightPortrait/simple_highlight_portrait.gd" id="1_ceqva"] [node name="DefaultPortrait" type="Node2D"] script = ExtResource("1_ceqva") diff --git a/addons/dialogic/Modules/History/event_history.gd b/addons/dialogic/Modules/History/event_history.gd index 0b0297d7c..180d7f095 100644 --- a/addons/dialogic/Modules/History/event_history.gd +++ b/addons/dialogic/Modules/History/event_history.gd @@ -57,7 +57,7 @@ func get_shortcode_parameters() -> Dictionary: ## EDITOR REPRESENTATION ################################################################################ -func build_event_editor(): +func build_event_editor() -> void: add_header_edit('action', ValueType.FIXED_OPTIONS, { 'options': [ { diff --git a/addons/dialogic/Modules/History/settings_history.gd b/addons/dialogic/Modules/History/settings_history.gd index 756071201..97b5cd98b 100644 --- a/addons/dialogic/Modules/History/settings_history.gd +++ b/addons/dialogic/Modules/History/settings_history.gd @@ -8,7 +8,9 @@ func _get_priority() -> int: func _ready() -> void: %SimpleHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/simple_history_enabled')) + %SimpleHistorySave.toggled.connect(setting_toggled.bind('dialogic/history/simple_history_save')) %FullHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/full_history_enabled')) + %FullHistorySave.toggled.connect(setting_toggled.bind('dialogic/history/full_history_save')) %AlreadyReadHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/visited_event_history_enabled')) %SaveOnAutoSaveToggle.toggled.connect(setting_toggled.bind('dialogic/history/save_on_autosave')) %SaveOnSaveToggle.toggled.connect(setting_toggled.bind('dialogic/history/save_on_save')) @@ -16,7 +18,9 @@ func _ready() -> void: func _refresh() -> void: %SimpleHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', false) + %SimpleHistorySave.button_pressed = ProjectSettings.get_setting('dialogic/history/simple_history_save', false) %FullHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/full_history_enabled', false) + %FullHistorySave.button_pressed = ProjectSettings.get_setting('dialogic/history/full_history_save', false) %AlreadyReadHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/visited_event_history_enabled', false) diff --git a/addons/dialogic/Modules/History/settings_history.tscn b/addons/dialogic/Modules/History/settings_history.tscn index 7bf916d7e..ed55edb85 100644 --- a/addons/dialogic/Modules/History/settings_history.tscn +++ b/addons/dialogic/Modules/History/settings_history.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://addons/dialogic/Modules/History/settings_history.gd" id="1_hbhst"] [ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_wefye"] -[sub_resource type="Image" id="Image_3h4fk"] +[sub_resource type="Image" id="Image_3clns"] 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", @@ -12,8 +12,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_8li2l"] -image = SubResource("Image_3h4fk") +[sub_resource type="ImageTexture" id="ImageTexture_irr0a"] +image = SubResource("Image_3clns") [node name="History" type="PanelContainer"] anchors_preset = 15 @@ -45,7 +45,7 @@ text = "Enabled" layout_mode = 2 tooltip_text = "When enabled, some events (Text, Join, Leave, Choice) will store a log. Also, the default layout will feature the log panel option." -texture = SubResource("ImageTexture_8li2l") +texture = SubResource("ImageTexture_irr0a") hint_text = "When enabled, some events (Text, Join, Leave, Choice) will store a log. Also, the default layout will feature the log panel option." @@ -53,6 +53,23 @@ Also, the default layout will feature the log panel option." unique_name_in_owner = true layout_mode = 2 +[node name="HBoxContainer2" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="Label" type="Label" parent="HistoryOptions/HBoxContainer2"] +layout_mode = 2 +text = "Save and Load" + +[node name="HintTooltip" parent="HistoryOptions/HBoxContainer2" instance=ExtResource("2_wefye")] +layout_mode = 2 +tooltip_text = "When enabled, the simple history is included in the savegame." +texture = SubResource("ImageTexture_irr0a") +hint_text = "When enabled, the simple history is included in the savegame. Also, it is reset on Dialogic.clear(FULL_CLEAR)." + +[node name="SimpleHistorySave" type="CheckBox" parent="HistoryOptions/HBoxContainer2"] +unique_name_in_owner = true +layout_mode = 2 + [node name="Title" type="Label" parent="HistoryOptions"] layout_mode = 2 theme_type_variation = &"DialogicSettingsSection" @@ -68,13 +85,30 @@ text = "Enabled" [node name="HintTooltip" parent="HistoryOptions/HBoxContainer5" instance=ExtResource("2_wefye")] layout_mode = 2 tooltip_text = "When enabled, stores a copy of each event." -texture = SubResource("ImageTexture_8li2l") +texture = SubResource("ImageTexture_irr0a") hint_text = "When enabled, stores a copy of each event." [node name="FullHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer5"] unique_name_in_owner = true layout_mode = 2 +[node name="HBoxContainer6" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="Label" type="Label" parent="HistoryOptions/HBoxContainer6"] +layout_mode = 2 +text = "Save and Load" + +[node name="HintTooltip" parent="HistoryOptions/HBoxContainer6" instance=ExtResource("2_wefye")] +layout_mode = 2 +tooltip_text = "When enabled, the full history is included in the savegame." +texture = SubResource("ImageTexture_irr0a") +hint_text = "When enabled, the full history is included in the savegame. Also, it is reset on Dialogic.clear(FULL_CLEAR)." + +[node name="FullHistorySave" type="CheckBox" parent="HistoryOptions/HBoxContainer6"] +unique_name_in_owner = true +layout_mode = 2 + [node name="Title2" type="Label" parent="HistoryOptions"] layout_mode = 2 theme_type_variation = &"DialogicSettingsSection" @@ -92,7 +126,7 @@ layout_mode = 2 tooltip_text = "Remembers whether events were already met in the timeline. When enabled the signals \"Dialogic.History.visited_event\" and \"Dialogic.History.unvisited_event\" are emitted. " -texture = SubResource("ImageTexture_8li2l") +texture = SubResource("ImageTexture_irr0a") hint_text = "Remembers whether events were already met in the timeline. When enabled the signals \"Dialogic.History.visited_event\" and \"Dialogic.History.unvisited_event\" are emitted. " @@ -112,7 +146,7 @@ text = "Save on Auto-Save signal" layout_mode = 2 tooltip_text = "Stores the already-visited history in a global save file when an Auto-Save occurs. The Auto-Save is part of the Save settings." -texture = SubResource("ImageTexture_8li2l") +texture = SubResource("ImageTexture_irr0a") hint_text = "Stores the already-visited history in a global save file when an Auto-Save occurs. The Auto-Save is part of the Save settings." @@ -132,7 +166,7 @@ layout_mode = 2 tooltip_text = "Stores the already-visited history in a global save file when a normal Save occurs. This can be done via the Dialogic.Save.save method. This setting ignores Auto-Saves." -texture = SubResource("ImageTexture_8li2l") +texture = SubResource("ImageTexture_irr0a") hint_text = "Stores the already-visited history in a global save file when a normal Save occurs. This can be done via the Dialogic.Save.save method. This setting ignores Auto-Saves." diff --git a/addons/dialogic/Modules/History/subsystem_history.gd b/addons/dialogic/Modules/History/subsystem_history.gd index 01f910fb0..d1371ea4c 100644 --- a/addons/dialogic/Modules/History/subsystem_history.gd +++ b/addons/dialogic/Modules/History/subsystem_history.gd @@ -8,16 +8,18 @@ signal close_requested ## Simple history that stores limited information ## Used for the history display -var simple_history_enabled := true +var simple_history_enabled := false +var simple_history_save := false var simple_history_content : Array[Dictionary] = [] signal simple_history_changed ## Whether to keep a history of every Dialogic event encountered. var full_event_history_enabled := false +var full_event_history_save := false ## The full history of all Dialogic events encountered. ## Requires [member full_event_history_enabled] to be true. -var full_event_history_content := [] +var full_event_history_content: Array[DialogicEvent] = [] ## Emitted if a new event has been inserted into the full event history. signal full_event_history_changed @@ -63,12 +65,10 @@ var save_visited_history_on_save := false: ## Starts and stops the connection to the [subsystem Save] subsystem's [signal saved] signal. func _update_saved_connection(to_connect: bool) -> void: if to_connect: - if not DialogicUtil.autoload().Save.saved.is_connected(_on_save): - var _result := DialogicUtil.autoload().Save.saved.connect(_on_save) + DialogicUtil.autoload().Save.saved.connect(_on_save) else: - if DialogicUtil.autoload().Save.saved.is_connected(_on_save): DialogicUtil.autoload().Save.saved.disconnect(_on_save) @@ -77,11 +77,13 @@ func _update_saved_connection(to_connect: bool) -> void: #################################################################################################### func _ready() -> void: - var _result := dialogic.event_handled.connect(store_full_event) - _result = dialogic.event_handled.connect(_check_seen) + dialogic.event_handled.connect(store_full_event) + dialogic.event_handled.connect(_check_seen) - simple_history_enabled = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', simple_history_enabled ) + simple_history_enabled = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', simple_history_enabled) + simple_history_save = ProjectSettings.get_setting('dialogic/history/simple_history_save', simple_history_save) full_event_history_enabled = ProjectSettings.get_setting('dialogic/history/full_history_enabled', full_event_history_enabled) + full_event_history_save = ProjectSettings.get_setting('dialogic/history/full_history_save', full_event_history_save) visited_event_history_enabled = ProjectSettings.get_setting('dialogic/history/visited_event_history_enabled', visited_event_history_enabled) @@ -101,6 +103,47 @@ func post_install() -> void: save_visited_history_on_save = ProjectSettings.get_setting('dialogic/history/save_on_save', save_visited_history_on_save) +func clear_game_state(clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: + if clear_flag == DialogicGameHandler.ClearFlags.FULL_CLEAR: + if simple_history_save: + simple_history_content = [] + dialogic.current_state_info.erase("history_simple") + if full_event_history_save: + full_event_history_content = [] + dialogic.current_state_info.erase("history_full") + + +func load_game_state(load_flag := LoadFlags.FULL_LOAD) -> void: + if load_flag == LoadFlags.FULL_LOAD: + if simple_history_save and dialogic.current_state_info.has("history_simple"): + simple_history_content.assign(dialogic.current_state_info["history_simple"]) + + if full_event_history_save and dialogic.current_state_info.has("history_full"): + full_event_history_content = [] + + for event_text in dialogic.current_state_info["history_full"]: + var event: DialogicEvent + for i in DialogicResourceUtil.get_event_cache(): + if i.is_valid_event(event_text): + event = i.duplicate() + break + event.from_text(event_text) + full_event_history_content.append(event) + + +func save_game_state() -> void: + if simple_history_save: + dialogic.current_state_info["history_simple"] = Array(simple_history_content) + else: + dialogic.current_state_info.erase("history_simple") + if full_event_history_save: + dialogic.current_state_info["history_full"] = [] + for event in full_event_history_content: + dialogic.current_state_info["history_full"].append(event.to_text()) + else: + dialogic.current_state_info.erase("history_full") + + func open_history() -> void: open_requested.emit() @@ -161,16 +204,17 @@ func _get_event_key(event_index: int, timeline_path: String) -> String: return event_key -# Called if a Text event marks an unvisited Text event as visited. -func mark_event_as_visited(_event: DialogicEvent) -> void: +## Called if an event is marked as visited. +func mark_event_as_visited(event_index := dialogic.current_event_idx, timeline := dialogic.current_timeline) -> void: if !visited_event_history_enabled: return - var event_key := _current_event_key() + var event_key := _get_event_key(event_index, timeline.resource_path) + + visited_event_history_content[event_key] = event_index - visited_event_history_content[event_key] = dialogic.current_event_idx -# Called on each event, but we filter for Text events. +## Called on each event, but we filter for Text events. func _check_seen(event: DialogicEvent) -> void: if !visited_event_history_enabled: return diff --git a/addons/dialogic/Modules/Jump/event_jump.gd b/addons/dialogic/Modules/Jump/event_jump.gd index 1760147b0..050dfa9a4 100644 --- a/addons/dialogic/Modules/Jump/event_jump.gd +++ b/addons/dialogic/Modules/Jump/event_jump.gd @@ -8,15 +8,15 @@ extends DialogicEvent ### 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 +var timeline: DialogicTimeline ## If not empty, the event will try to find a Label event with this set as name. Empty by default.. -var label_name : String = "" +var label_name := "" ### Helpers ## Used to set the timeline resource from the unique name identifier and vice versa -var timeline_identifier: String = "": +var timeline_identifier := "": get: if timeline: var identifier := DialogicResourceUtil.get_unique_identifier(timeline.resource_path) @@ -75,7 +75,7 @@ func to_text() -> String: func from_text(string:String) -> void: - var result := RegEx.create_from_string('jump (?.*\\/)?(?