diff --git a/CHANGELOG.md b/CHANGELOG.md index 124aa82c..df3a77dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## v0.3.0 Combat Demo ⚔️ + +### New + +The demo scene has been reworked to include combat. + +The demo introduces: +- A Main scene separated into two main gameplay objects, the Field and Combat. +- 1 additional template Cutscene: + - Combat Trigger, that runs when colliding with the player. This is designed to be used with roaming encounters that may pounce on the player in the field. Winning removes the encounter from the Field, whereas losing will lead to a game-over. +- An example combat that is run from a conversation. Winning or losing leads to different dialogue options. +- Combat-related music. +- Smooth transitions to and from combat. + +### Changes +- Added a handful of combat events that allow objects to hook into changes in a given combat. +- Refactored ScreenTransition into an autoload, since only one should ever be active at a given moment anyways. +- Updated Dialogic 2 to the most recent build. ***Note that input has been modified to respond to the input events being released, rather than pressed.*** +- Miscellaneous fixes to the demo. + ## v0.2.0 Cutscene Demo 💬 ### New diff --git a/CREDITS.md b/CREDITS.md index c65d247d..bc9b6383 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -10,6 +10,11 @@ Author: Kenney URL: https://kenney.nl/assets/emotes-pack (accessed 2023-06-02) License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) +Fun Run, The +Author: Zane Little Music +URL: https://opengameart.org/content/the-fun-run-day-3 (accessed 2024-04-30) +License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) + Impact Sounds Author: Kenney URL: https://kenney.nl/assets/impact-sounds (accessed 2023-12-20) @@ -30,6 +35,11 @@ Author: Kenney URL: https://kenney.nl/assets/rpg-audio (accessed 2023-12-20) License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) +Squashin' Bugs +Author: Zane Little Music +URL: https://opengameart.org/content/squashin-bugs-day-15 (accessed 2023-06-06) +License: [CC0](https://creativecommons.org/publicdomain/zero/1.0/) + Tiny Series (Tiny Town & Tiny Dungeon) Author: Kenney URL: https://kenney.nl/assets/tiny-town & https://kenney.nl/assets/tiny-dungeon (accessed 2023-02-22) diff --git a/addons/dialogic/Core/DialogicGameHandler.gd b/addons/dialogic/Core/DialogicGameHandler.gd new file mode 100644 index 00000000..b5d8fd22 --- /dev/null +++ b/addons/dialogic/Core/DialogicGameHandler.gd @@ -0,0 +1,417 @@ +class_name DialogicGameHandler +extends Node + +## Class that is used as the Dialogic autoload. + +## Autoload script that allows you to interact with all of Dialogic's systems:[br] +## - Holds all important information about the current state of Dialogic.[br] +## - Provides access to all the subsystems.[br] +## - Has methods to start/end timelines.[br] + + +## States indicating different phases of dialog. +enum States { + IDLE, ## Dialogic is awaiting input to advance. + REVEALING_TEXT, ## Dialogic is currently revealing text. + ANIMATING, ## Some animation is happening. + AWAITING_CHOICE, ## Dialogic awaits the selection of a choice + WAITING ## Dialogic is currently awaiting something. + } + +## Flags indicating what to clear when calling [method clear]. +enum ClearFlags { + FULL_CLEAR = 0, ## Clears all subsystems + KEEP_VARIABLES = 1, ## Clears all subsystems and info except for variables + TIMELINE_INFO_ONLY = 2 ## Doesn't clear subsystems but current timeline and index + } + +## Reference to the currently executed timeline. +var current_timeline: DialogicTimeline = null +## Copy of the [member current_timeline]'s events. +var current_timeline_events: Array = [] + +## Index of the event the timeline handling is currently at. +var current_event_idx: int = 0 +## Contains all information that subsystems consider relevant for +## the current situation +var current_state_info: Dictionary = {} + +## Current state (see [member States] enum). +var current_state := States.IDLE: + get: + return current_state + + set(new_state): + current_state = new_state + state_changed.emit(new_state) + +## Emitted when [member current_state] change. +signal state_changed(new_state:States) + +## When `true`, many dialogic processes won't continue until it's `false` again. +var paused := false: + set(value): + paused = value + + if paused: + + for subsystem in get_children(): + + if subsystem is DialogicSubsystem: + (subsystem as DialogicSubsystem).pause() + + dialogic_paused.emit() + + else: + for subsystem in get_children(): + + if subsystem is DialogicSubsystem: + (subsystem as DialogicSubsystem).resume() + + dialogic_resumed.emit() + +## Emitted when [member paused] changes to `true`. +signal dialogic_paused +## Emitted when [member paused] changes to `false`. +signal dialogic_resumed + + +## Emitted when the timeline ends. +## This can be a timeline ending or [method end_timeline] being called. +signal timeline_ended +## Emitted when a timeline starts by calling either [method start] +## or [method start_timeline]. +signal timeline_started +## Emitted when an event starts being executed. +## The event may not have finished executing yet. +signal event_handled(resource: DialogicEvent) + +## Emitted when a [class SignalEvent] event was reached. +signal signal_event(argument: Variant) +## Emitted when a signal event gets fired from a [class TextEvent] event. +signal text_signal(argument: String) + + +# Careful, this section is repopulated automatically at certain moments. +#region SUBSYSTEMS + +var Audio := preload("res://addons/dialogic/Modules/Audio/subsystem_audio.gd").new(): + get: return get_subsystem("Audio") + +var Backgrounds := preload("res://addons/dialogic/Modules/Background/subsystem_backgrounds.gd").new(): + get: return get_subsystem("Backgrounds") + +var Portraits := preload("res://addons/dialogic/Modules/Character/subsystem_portraits.gd").new(): + get: return get_subsystem("Portraits") + +var Choices := preload("res://addons/dialogic/Modules/Choice/subsystem_choices.gd").new(): + get: return get_subsystem("Choices") + +var Expressions := preload("res://addons/dialogic/Modules/Core/subsystem_expression.gd").new(): + get: return get_subsystem("Expressions") + +var Animations := preload("res://addons/dialogic/Modules/Core/subsystem_animation.gd").new(): + get: return get_subsystem("Animations") + +var Inputs := preload("res://addons/dialogic/Modules/Core/subsystem_input.gd").new(): + get: return get_subsystem("Inputs") + +var Glossary := preload("res://addons/dialogic/Modules/Glossary/subsystem_glossary.gd").new(): + get: return get_subsystem("Glossary") + +var History := preload("res://addons/dialogic/Modules/History/subsystem_history.gd").new(): + get: return get_subsystem("History") + +var Jump := preload("res://addons/dialogic/Modules/Jump/subsystem_jump.gd").new(): + get: return get_subsystem("Jump") + +var Save := preload("res://addons/dialogic/Modules/Save/subsystem_save.gd").new(): + get: return get_subsystem("Save") + +var Settings := preload("res://addons/dialogic/Modules/Settings/subsystem_settings.gd").new(): + get: return get_subsystem("Settings") + +var Styles := preload("res://addons/dialogic/Modules/Style/subsystem_styles.gd").new(): + get: return get_subsystem("Styles") + +var Text := preload("res://addons/dialogic/Modules/Text/subsystem_text.gd").new(): + get: return get_subsystem("Text") + +var TextInput := preload("res://addons/dialogic/Modules/TextInput/subsystem_text_input.gd").new(): + get: return get_subsystem("TextInput") + +var VAR := preload("res://addons/dialogic/Modules/Variable/subsystem_variables.gd").new(): + get: return get_subsystem("VAR") + +var Voice := preload("res://addons/dialogic/Modules/Voice/subsystem_voice.gd").new(): + get: return get_subsystem("Voice") + +#endregion + + +## Autoloads are added first, so this happens REALLY early on game startup. +func _ready() -> void: + DialogicResourceUtil.update() + + _collect_subsystems() + + clear() + + +#region TIMELINE & EVENT HANDLING +################################################################################ + +## Method to start a timeline AND ensure that a layout scene is present. +## For argument info, checkout [method start_timeline]. +## -> 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'): + printerr("[Dialogic] You called Dialogic.start() but the Styles subsystem is missing!") + clear(ClearFlags.KEEP_VARIABLES) + start_timeline(timeline, label) + return null + + # Otherwise make sure there is a style active. + var scene: Node = null + if !self.Styles.has_active_layout_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 + + +## Method to start a timeline without adding a layout scene. +## @timeline can be either a loaded timeline resource or a path to a timeline file. +## @label_or_idx can be a label (string) or index (int) to skip to immediatly. +func start_timeline(timeline:Variant, label_or_idx:Variant = "") -> void: + # load the resource if only the path is given + if typeof(timeline) == TYPE_STRING: + #check the lookup table if it's not a full file name + if (timeline as String).contains("res://"): + timeline = load((timeline as String)) + else: + timeline = DialogicResourceUtil.get_timeline_resource((timeline as String)) + + if timeline == null: + printerr("[Dialogic] There was an error loading this timeline. Check the filename, and the timeline for errors") + return + + await (timeline as DialogicTimeline).process() + + current_timeline = timeline + current_timeline_events = current_timeline.events + current_event_idx = -1 + + if typeof(label_or_idx) == TYPE_STRING: + if label_or_idx: + if has_subsystem('Jump'): + Jump.jump_to_label((label_or_idx as String)) + elif typeof(label_or_idx) == TYPE_INT: + if label_or_idx >-1: + current_event_idx = label_or_idx -1 + + timeline_started.emit() + handle_next_event() + + +## Preloader function, prepares a timeline and returns an object to hold for later +## [param timeline_resource] can be either a path (string) or a loaded timeline (resource) +func preload_timeline(timeline_resource:Variant) -> Variant: + # I think ideally this should be on a new thread, will test + if typeof(timeline_resource) == TYPE_STRING: + timeline_resource = load((timeline_resource as String)) + if timeline_resource == null: + printerr("[Dialogic] There was an error preloading this timeline. Check the filename, and the timeline for errors") + return null + + await (timeline_resource as DialogicTimeline).process() + + return timeline_resource + + +## Clears and stops the current timeline. +func end_timeline() -> void: + await clear(ClearFlags.TIMELINE_INFO_ONLY) + _on_timeline_ended() + timeline_ended.emit() + + +## Handles the next event. +func handle_next_event(_ignore_argument: Variant = "") -> void: + handle_event(current_event_idx+1) + + +## Handles the event at the given index [param event_index]. +## You can call this manually, but if another event is still executing, it might have unexpected results. +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) + + if paused: + await dialogic_resumed + + if event_index >= len(current_timeline_events): + end_timeline() + return + + #actually process the event now, since we didnt earlier at runtime + #this needs to happen before we create the copy DialogicEvent variable, so it doesn't throw an error if not ready + if current_timeline_events[event_index].event_node_ready == false: + current_timeline_events[event_index]._load_from_string(current_timeline_events[event_index].event_node_as_text) + + current_event_idx = event_index + + if not current_timeline_events[event_index].event_finished.is_connected(handle_next_event): + current_timeline_events[event_index].event_finished.connect(handle_next_event) + + set_meta('previous_event', current_timeline_events[event_index]) + + current_timeline_events[event_index].execute(self) + event_handled.emit(current_timeline_events[event_index]) + + +## Resets Dialogic's state fully or partially. +## 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) -> void: + if !clear_flags & ClearFlags.TIMELINE_INFO_ONLY: + for subsystem in get_children(): + if subsystem is DialogicSubsystem: + (subsystem as DialogicSubsystem).clear_game_state(clear_flags) + + var timeline := current_timeline + + current_timeline = null + current_event_idx = -1 + current_timeline_events = [] + current_state = States.IDLE + + # Resetting variables + if timeline: + await timeline.clean() + +#endregion + + +#region SAVING & LOADING +################################################################################ + +## Returns a dictionary containing all necessary information to later recreate the same state with load_full_state. +## The [subsystem Save] subsystem might be more useful for you. +## However, this can be used to integrate the info into your own save system. +func get_full_state() -> Dictionary: + if current_timeline: + current_state_info['current_event_idx'] = current_event_idx + current_state_info['current_timeline'] = current_timeline.resource_path + else: + current_state_info['current_event_idx'] = -1 + current_state_info['current_timeline'] = null + + return current_state_info.duplicate(true) + + +## This method tries to load the state from the given [param state_info]. +## Will automatically start a timeline and add a layout if a timeline was running when +## the dictionary was retrieved with [method get_full_state]. +func load_full_state(state_info:Dictionary) -> void: + clear() + current_state_info = state_info + ## The Style subsystem needs to run first for others to load correctly. + var scene: Node = null + if has_subsystem('Styles'): + get_subsystem('Styles').load_game_state() + scene = self.Styles.get_layout_node() + + var load_subsystems := func() -> void: + for subsystem in get_children(): + if subsystem.name == 'Styles': + continue + (subsystem as DialogicSubsystem).load_game_state() + + if null != scene and not scene.is_node_ready(): + scene.ready.connect(load_subsystems) + else: + await get_tree().process_frame + load_subsystems.call() + + if current_state_info.get('current_timeline', null): + start_timeline(current_state_info.current_timeline, current_state_info.get('current_event_idx', 0)) + else: + end_timeline.call_deferred() +#endregion + + +#region SUB-SYTSEMS +################################################################################ + +func _collect_subsystems() -> void: + var subsystem_nodes := [] as Array[DialogicSubsystem] + for indexer in DialogicUtil.get_indexers(): + for subsystem in indexer._get_subsystems(): + var subsystem_node := add_subsystem(str(subsystem.name), str(subsystem.script)) + subsystem_nodes.push_back(subsystem_node) + + for subsystem in subsystem_nodes: + subsystem.post_install() + + +## Returns `true` if a subystem with the given [param subsystem_name] exists. +func has_subsystem(subsystem_name:String) -> bool: + return has_node(subsystem_name) + + +## Returns the subsystem node of the given [param subsystem_name] or null if it doesn't exist. +func get_subsystem(subsystem_name:String) -> DialogicSubsystem: + return get_node(subsystem_name) + + +## Adds a subsystem node with the given [param subsystem_name] and [param script_path]. +func add_subsystem(subsystem_name:String, script_path:String) -> DialogicSubsystem: + var node: Node = Node.new() + node.name = subsystem_name + node.set_script(load(script_path)) + node = node as DialogicSubsystem + node.dialogic = self + add_child(node) + return node + + +#endregion + + +#region HELPERS +################################################################################ + +## This handles the `Layout End Behaviour` setting that can be changed in the Dialogic settings. +func _on_timeline_ended() -> void: + if self.Styles.has_active_layout_node() and self.Styles.get_layout_node().is_inside_tree(): + match ProjectSettings.get_setting('dialogic/layout/end_behaviour', 0): + 0: + self.Styles.get_layout_node().get_parent().remove_child(self.Styles.get_layout_node()) + self.Styles.get_layout_node().queue_free() + 1: + @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/Other/DialogicResourceUtil.gd b/addons/dialogic/Core/DialogicResourceUtil.gd similarity index 89% rename from addons/dialogic/Other/DialogicResourceUtil.gd rename to addons/dialogic/Core/DialogicResourceUtil.gd index 0f2017b2..f38fe57d 100644 --- a/addons/dialogic/Other/DialogicResourceUtil.gd +++ b/addons/dialogic/Core/DialogicResourceUtil.gd @@ -21,7 +21,7 @@ static func get_directory(extension:String) -> Dictionary: if Engine.has_meta(extension+'_directory'): return Engine.get_meta(extension+'_directory', {}) - var directory := ProjectSettings.get_setting("dialogic/directories/"+extension+'_directory', {}) + var directory: Dictionary = ProjectSettings.get_setting("dialogic/directories/"+extension+'_directory', {}) Engine.set_meta(extension+'_directory', directory) return directory @@ -59,13 +59,17 @@ static func add_resource_to_directory(file_path:String, directory:Dictionary) -> return directory +## Returns the unique identifier for the given resource path. +## Returns an empty string if no identifier was found. static func get_unique_identifier(file_path:String) -> String: - var identifier := get_directory(file_path.get_extension()).find_key(file_path) + var identifier: String = get_directory(file_path.get_extension()).find_key(file_path) if typeof(identifier) == TYPE_STRING: return identifier return "" +## Returns the resource associated with the given unique identifier. +## The expected extension is needed to use the right directory. static func get_resource_from_identifier(identifier:String, extension:String) -> Resource: var path: String = get_directory(extension).get(identifier, '') if ResourceLoader.exists(path): @@ -75,7 +79,7 @@ static func get_resource_from_identifier(identifier:String, extension:String) -> static func change_unique_identifier(file_path:String, new_identifier:String) -> void: var directory := get_directory(file_path.get_extension()) - var key := directory.find_key(file_path) + var key: String = directory.find_key(file_path) while key != null: if key == new_identifier: break @@ -87,7 +91,7 @@ static func change_unique_identifier(file_path:String, new_identifier:String) -> static func change_resource_path(old_path:String, new_path:String) -> void: var directory := get_directory(new_path.get_extension()) - var key := directory.find_key(old_path) + var key: String = directory.find_key(old_path) while key != null: directory[key] = new_path key = directory.find_key(old_path) @@ -96,12 +100,13 @@ static func change_resource_path(old_path:String, new_path:String) -> void: static func remove_resource(file_path:String) -> void: var directory := get_directory(file_path.get_extension()) - var key := directory.find_key(file_path) + var key: String = directory.find_key(file_path) while key != null: directory.erase(key) key = directory.find_key(file_path) set_directory(file_path.get_extension(), directory) + static func is_identifier_unused(extension:String, identifier:String) -> bool: return not identifier in get_directory(extension) @@ -137,6 +142,7 @@ static func update_label_cache() -> void: #region EVENT CACHE ################################################################################ +## Dialogic keeps a list that has each event once. This allows retrieval of that list. static func get_event_cache() -> Array: if not event_cache.is_empty(): return event_cache @@ -150,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()) @@ -163,6 +169,9 @@ static func update_event_cache() -> Array: #endregion +#region SPECIAL RESOURCES +################################################################################ + static func update_special_resources() -> void: special_resources = [] for indexer in DialogicUtil.get_indexers(): @@ -185,7 +194,7 @@ static func guess_special_resource(type:String, name:String, default:="") -> Str return path return default - +#endregion #region HELPERS ################################################################################ diff --git a/addons/dialogic/Other/DialogicUtil.gd b/addons/dialogic/Core/DialogicUtil.gd similarity index 78% rename from addons/dialogic/Other/DialogicUtil.gd rename to addons/dialogic/Core/DialogicUtil.gd index 770c3c15..9b5a63ab 100644 --- a/addons/dialogic/Other/DialogicUtil.gd +++ b/addons/dialogic/Core/DialogicUtil.gd @@ -20,6 +20,7 @@ static func get_dialogic_plugin() -> Node: #endregion + ## Returns the autoload when in-game. static func autoload() -> DialogicGameHandler: if Engine.is_editor_hint(): @@ -28,9 +29,10 @@ static func autoload() -> DialogicGameHandler: return null return Engine.get_main_loop().root.get_node("Dialogic") + #region FILE SYSTEM ################################################################################ -static func listdir(path: String, files_only: bool = true, throw_error:bool = true, full_file_path:bool = 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): @@ -55,7 +57,6 @@ static func listdir(path: String, files_only: bool = true, throw_error:bool = tr return files - static func get_module_path(name:String, builtin:=true) -> String: if builtin: return "res://addons/dialogic/Modules".path_join(name) @@ -63,22 +64,45 @@ static func get_module_path(name:String, builtin:=true) -> String: return ProjectSettings.get_setting('dialogic/extensions_folder', 'res://addons/dialogic_additions').path_join(name) +## This is a private and editor-only function. +## +## Populates the [class DialogicGameHandler] with new custom subsystems by +## directly manipulating the file's content and then importing the file. +static func _update_autoload_subsystem_access() -> void: + if not Engine.is_editor_hint(): + printerr("[Dialogic] This function is only available in the editor.") + return + + var script: Script = load("res://addons/dialogic/Core/DialogicGameHandler.gd") + var new_subsystem_access_list := "#region SUBSYSTEMS\n" + + for indexer: DialogicIndexer in get_indexers(true, true): + + for subsystem: Dictionary in indexer._get_subsystems().duplicate(true): + 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) + ResourceSaver.save(script) + Engine.get_singleton("EditorInterface").get_resource_filesystem().reimport_files(["res://addons/dialogic/Core/DialogicGameHandler.gd"]) + + static func get_indexers(include_custom := true, force_reload := false) -> Array[DialogicIndexer]: if Engine.get_main_loop().has_meta('dialogic_indexers') and !force_reload: return Engine.get_main_loop().get_meta('dialogic_indexers') - var indexers : Array[DialogicIndexer] = [] + var indexers: Array[DialogicIndexer] = [] 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): + var possible_script: String = DialogicUtil.get_module_path(file).path_join("index.gd") + 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) @@ -182,19 +206,73 @@ static func get_next_translation_id() -> String: #region VARIABLES ################################################################################ +enum VarTypes {ANY, STRING, FLOAT, INT, BOOL} + + +static func get_default_variables() -> Dictionary: + return ProjectSettings.get_setting('dialogic/variables', {}) + + # helper that converts a nested variable dictionary into an array with paths -static func list_variables(dict:Dictionary, path := "") -> Array: +static func list_variables(dict:Dictionary, path := "", type:=VarTypes.ANY) -> Array: var array := [] for key in dict.keys(): if typeof(dict[key]) == TYPE_DICTIONARY: - array.append_array(list_variables(dict[key], path+key+".")) + array.append_array(list_variables(dict[key], path+key+".", type)) else: - array.append(path+key) + if type == VarTypes.ANY or get_variable_value_type(dict[key]) == type: + array.append(path+key) return array + +static func get_variable_value_type(value:Variant) -> int: + match typeof(value): + TYPE_STRING: + return VarTypes.STRING + TYPE_FLOAT: + return VarTypes.FLOAT + TYPE_INT: + return VarTypes.INT + TYPE_BOOL: + return VarTypes.BOOL + return VarTypes.ANY + + +static func get_variable_type(path:String, dict:Dictionary={}) -> VarTypes: + if dict.is_empty(): + dict = get_default_variables() + return get_variable_value_type(_get_value_in_dictionary(path, dict)) + + +## This will set a value in a dictionary (or a sub-dictionary based on the path) +## e.g. it could set "Something.Something.Something" in {'Something':{'Something':{'Someting':"value"}}} +static func _set_value_in_dictionary(path:String, dictionary:Dictionary, value): + if '.' in path: + var from := path.split('.')[0] + if from in dictionary.keys(): + dictionary[from] = _set_value_in_dictionary(path.trim_prefix(from+"."), dictionary[from], value) + else: + if path in dictionary.keys(): + dictionary[path] = value + return dictionary + + +## This will get a value in a dictionary (or a sub-dictionary based on the path) +## e.g. it could get "Something.Something.Something" in {'Something':{'Something':{'Someting':"value"}}} +static func _get_value_in_dictionary(path:String, dictionary:Dictionary, default= null) -> Variant: + if '.' in path: + var from := path.split('.')[0] + if from in dictionary.keys(): + return _get_value_in_dictionary(path.trim_prefix(from+"."), dictionary[from], default) + else: + if path in dictionary.keys(): + return dictionary[path] + return default + #endregion + #region STYLES ################################################################################ @@ -207,7 +285,7 @@ static func get_fallback_style() -> DialogicStyle: static func get_default_style() -> DialogicStyle: - var default := ProjectSettings.get_setting('dialogic/layout/default_style', '') + var default: String = ProjectSettings.get_setting('dialogic/layout/default_style', '') if !ResourceLoader.exists(default): return get_fallback_style() return load(default) @@ -217,7 +295,7 @@ static func get_style_by_name(name:String) -> DialogicStyle: if name.is_empty(): return get_default_style() - var styles := ProjectSettings.get_setting('dialogic/layout/style_list', []) + var styles: Array = ProjectSettings.get_setting('dialogic/layout/style_list', []) for style in styles: if not ResourceLoader.exists(style): continue @@ -275,7 +353,7 @@ static func get_scene_export_defaults(node:Node) -> Dictionary: ################################################################################ static func setup_script_property_edit_node(property_info: Dictionary, value:Variant, property_changed:Callable) -> Control: - var input :Control = null + var input: Control = null match property_info['type']: TYPE_BOOL: input = CheckBox.new() @@ -287,7 +365,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 = DialogicUtil.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() @@ -321,14 +399,15 @@ static func setup_script_property_edit_node(property_info: Dictionary, value:Var input.value_changed.connect(DialogicUtil._on_export_number_submitted.bind(property_info.name, property_changed)) if value != null: input.value = value - TYPE_VECTOR2: - input = load("res://addons/dialogic/Editor/Events/Fields/Vector2.tscn").instantiate() - input.set_value(value) + TYPE_VECTOR2, TYPE_VECTOR3, TYPE_VECTOR4: + var vectorSize: String = type_string(typeof(value))[-1] + input = load("res://addons/dialogic/Editor/Events/Fields/field_vector" + vectorSize + ".tscn").instantiate() input.property_name = property_info['name'] + input.set_value(value) input.value_changed.connect(DialogicUtil._on_export_vector_submitted.bind(property_changed)) TYPE_STRING: if property_info['hint'] & PROPERTY_HINT_FILE or property_info['hint'] & PROPERTY_HINT_DIR: - input = load("res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn").instantiate() + input = load("res://addons/dialogic/Editor/Events/Fields/field_file.tscn").instantiate() input.file_filter = property_info['hint_string'] input.file_mode = FileDialog.FILE_MODE_OPEN_FILE if property_info['hint'] == PROPERTY_HINT_DIR: @@ -341,7 +420,7 @@ static func setup_script_property_edit_node(property_info: Dictionary, value:Var input.value_changed.connect(DialogicUtil._on_export_file_submitted.bind(property_changed)) elif property_info['hint'] & PROPERTY_HINT_ENUM: input = OptionButton.new() - var options :PackedStringArray = [] + var options: PackedStringArray = [] for x in property_info['hint_string'].split(','): options.append(x.split(':')[0].strip_edges()) input.add_item(options[-1]) @@ -360,9 +439,10 @@ static func setup_script_property_edit_node(property_info: Dictionary, value:Var input = LineEdit.new() if value != null: input.text = value - input.text_submitted.connect(DialogicUtil._on_export_input_text_submitted.bind(property_info.name, property_changed)) + input.text_submitted.connect(_on_export_input_text_submitted.bind(property_info.name, property_changed)) return input + static func _on_export_input_text_submitted(text:String, property_name:String, callable: Callable) -> void: callable.call(property_name, var_to_str(text)) @@ -384,7 +464,7 @@ static func _on_export_file_submitted(property_name:String, value:String, callab static func _on_export_string_enum_submitted(value:int, property_name:String, list:PackedStringArray, callable: Callable): callable.call(property_name, var_to_str(list[value])) -static func _on_export_vector_submitted(property_name:String, value:Vector2, callable: Callable) -> void: +static func _on_export_vector_submitted(property_name:String, value:Variant, callable: Callable) -> void: callable.call(property_name, var_to_str(value)) #endregion diff --git a/addons/dialogic/Other/Dialogic_Subsystem.gd b/addons/dialogic/Core/Dialogic_Subsystem.gd similarity index 99% rename from addons/dialogic/Other/Dialogic_Subsystem.gd rename to addons/dialogic/Core/Dialogic_Subsystem.gd index 57d5d0ad..4b742a78 100644 --- a/addons/dialogic/Other/Dialogic_Subsystem.gd +++ b/addons/dialogic/Core/Dialogic_Subsystem.gd @@ -1,5 +1,5 @@ -extends Node class_name DialogicSubsystem +extends Node var dialogic: DialogicGameHandler = null @@ -10,21 +10,25 @@ enum LoadFlags {FULL_LOAD, ONLY_DNODES} func post_install() -> void: pass + # 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: 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: pass + # To be overriden by sub-classes func pause() -> void: pass + # To be overriden by sub-classes func resume() -> void: pass diff --git a/addons/dialogic/Other/index_class.gd b/addons/dialogic/Core/index_class.gd similarity index 98% rename from addons/dialogic/Other/index_class.gd rename to addons/dialogic/Core/index_class.gd index efc8adc8..25e276fc 100644 --- a/addons/dialogic/Other/index_class.gd +++ b/addons/dialogic/Core/index_class.gd @@ -13,7 +13,7 @@ var this_folder : String = get_script().resource_path.get_base_dir() ## 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 [] 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 bf16e4de..6dec0bf4 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd @@ -12,6 +12,7 @@ func _load_portrait_data(data:Dictionary) -> void: %IgnoreScale.set_pressed_no_signal(data.get('ignore_char_scale', false)) %PortraitScale.value = data.get('scale', 1.0)*100 %PortraitOffset.set_value(data.get('offset', Vector2())) + %PortraitOffset._load_display_info({'step':1}) %PortraitMirror.set_pressed_no_signal(data.get('mirror', false)) diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn index 10454082..9fa25c2d 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://crke8suvv52c6"] [ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.gd" id="1_76vf2"] -[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/Vector2.tscn" id="2_c8kyi"] +[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/field_vector2.tscn" id="2_c8kyi"] [node name="Layout" type="HFlowContainer"] offset_right = 428.0 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 77bb61d1..c77cf233 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd @@ -31,5 +31,6 @@ func _on_scene_picker_value_changed(prop_name:String, value:String) -> void: func _on_open_scene_button_pressed(): - if !%ScenePicker.current_value.is_empty() and FileAccess.file_exists(%ScenePicker.current_value): + if !%ScenePicker.current_value.is_empty() and ResourceLoader.exists(%ScenePicker.current_value): DialogicUtil.get_dialogic_plugin().get_editor_interface().open_scene_from_path(%ScenePicker.current_value) + EditorInterface.set_main_screen_editor("2D") 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 db9dd007..db355bd9 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=5 format=3 uid="uid://djq4aasoihexj"] [ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.gd" id="1_ht8lu"] -[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="2_k8xs0"] +[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="2_k8xs0"] [sub_resource type="Image" id="Image_sbh6e"] data = { diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd index e91734d6..6fcd21df 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd @@ -1,13 +1,17 @@ @tool extends DialogicCharacterEditorMainSection +var min_width := 200 + ## The general character settings tab func _get_title() -> String: return "General" + func _start_opened() -> bool: return true + func _ready() -> void: # Connecting all necessary signals %ColorPickerButton.custom_minimum_size.x = DialogicUtil.get_editor_scale()*30 @@ -15,17 +19,18 @@ func _ready() -> void: %DisplayNameLineEdit.text_changed.connect(character_editor.something_changed) %NicknameLineEdit.text_changed.connect(character_editor.something_changed) %DescriptionTextEdit.text_changed.connect(character_editor.something_changed) - + min_width = get_minimum_size().x + resized.connect(_on_resized) func _load_character(resource:DialogicCharacter) -> void: %DisplayNameLineEdit.text = resource.display_name %ColorPickerButton.color = resource.color - + %NicknameLineEdit.text = "" - for nickname in resource.nicknames: + for nickname in resource.nicknames: %NicknameLineEdit.text += nickname +", " %NicknameLineEdit.text = %NicknameLineEdit.text.trim_suffix(', ') - + %DescriptionTextEdit.text = resource.description @@ -37,5 +42,12 @@ func _save_changes(resource:DialogicCharacter) -> DialogicCharacter: nicknames.append(n_name.strip_edges()) resource.nicknames = nicknames resource.description = %DescriptionTextEdit.text - + return resource + + +func _on_resized() -> void: + if size.x > min_width+20: + self.columns = 2 + else: + self.columns = 1 diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn index 59842868..60f89a6f 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn @@ -1,6 +1,19 @@ -[gd_scene load_steps=2 format=3 uid="uid://bnkck3hocbkk5"] +[gd_scene load_steps=5 format=3 uid="uid://bnkck3hocbkk5"] [ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_section_general.gd" id="1_3e1i1"] +[ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_cxfqm"] + +[sub_resource type="Image" id="Image_yiygw"] +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_hx3oq"] +image = SubResource("Image_yiygw") [node name="General" type="GridContainer"] anchors_preset = 15 @@ -12,15 +25,25 @@ offset_right = -7.5 offset_bottom = -7.5 grow_horizontal = 2 grow_vertical = 2 -theme_override_constants/h_separation = 1 +theme_override_constants/h_separation = 6 theme_override_constants/v_separation = 6 columns = 2 script = ExtResource("1_3e1i1") -[node name="Label2" type="Label" parent="."] +[node name="HBox" type="HBoxContainer" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Display Name: " + +[node name="Label2" type="Label" parent="HBox"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Display Name" + +[node name="HintTooltip" parent="HBox" instance=ExtResource("2_cxfqm")] +layout_mode = 2 +tooltip_text = "This name will be displayed on the name label. You can use a dialogic variable. E.g. :{Player.name}" +texture = SubResource("ImageTexture_hx3oq") +hint_text = "This name will be displayed on the name label. You can use a dialogic variable. E.g. :{Player.name}" [node name="DisplayName" type="HBoxContainer" parent="."] layout_mode = 2 @@ -29,41 +52,63 @@ size_flags_horizontal = 3 [node name="DisplayNameLineEdit" type="LineEdit" parent="DisplayName"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "This name will be displayed on the name label. You can use a dialogic variable. E.g. :{Player.name}" -expand_to_text_length = true +size_flags_horizontal = 3 caret_blink = true caret_blink_interval = 0.5 +[node name="HintTooltip4" parent="DisplayName" instance=ExtResource("2_cxfqm")] +layout_mode = 2 +tooltip_text = "This color can be used on the name label and for occurences of the characters name in text (autocolor names)." +texture = SubResource("ImageTexture_hx3oq") +hint_text = "This color can be used on the name label and for occurences of the characters name in text (autocolor names)." + [node name="ColorPickerButton" type="ColorPickerButton" parent="DisplayName"] unique_name_in_owner = true custom_minimum_size = Vector2(30, 0) layout_mode = 2 -tooltip_text = "This color can be used on the name label and for occurences of the characters name in text (autocolor names)." color = Color(1, 1, 1, 1) edit_alpha = false -[node name="Label3" type="Label" parent="."] +[node name="HBox2" type="HBoxContainer" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Nicknames:" + +[node name="Label3" type="Label" parent="HBox2"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Nicknames" + +[node name="HintTooltip2" parent="HBox2" instance=ExtResource("2_cxfqm")] +layout_mode = 2 +tooltip_text = "If autocolor names is enabled, these will be colored in the characters color as well." +texture = SubResource("ImageTexture_hx3oq") +hint_text = "If autocolor names is enabled, these will be colored in the characters color as well." [node name="NicknameLineEdit" type="LineEdit" parent="."] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -tooltip_text = "If autocolor names is enabled, these will be colored in the characters color as well." caret_blink = true caret_blink_interval = 0.5 -[node name="Label4" type="Label" parent="."] +[node name="HBox3" type="HBoxContainer" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Description:" + +[node name="Label4" type="Label" parent="HBox3"] +layout_mode = 2 +size_flags_vertical = 0 +text = "Description" + +[node name="HintTooltip3" parent="HBox3" instance=ExtResource("2_cxfqm")] +layout_mode = 2 +tooltip_text = "No effect, just for you." +texture = SubResource("ImageTexture_hx3oq") +hint_text = "No effect, just for you." [node name="DescriptionTextEdit" type="TextEdit" parent="."] unique_name_in_owner = true custom_minimum_size = Vector2(0, 65) layout_mode = 2 size_flags_horizontal = 3 -tooltip_text = "No effect, just for you." wrap_mode = 1 diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd index ba630ead..44f4ec37 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd @@ -13,6 +13,7 @@ func _ready() -> void: # Connecting all necessary signals %DefaultPortraitPicker.value_changed.connect(default_portrait_changed) %MainScale.value_changed.connect(main_portrait_settings_update) + %MainOffset._load_display_info({'step':1}) %MainOffset.value_changed.connect(main_portrait_settings_update) %MainMirror.toggled.connect(main_portrait_settings_update) @@ -37,6 +38,11 @@ func default_portrait_changed(property:String, value:String) -> void: character_editor.update_default_portrait_star(value) +func set_default_portrait(portrait_name:String) -> void: + %DefaultPortraitPicker.set_value(portrait_name) + default_portrait_changed("", portrait_name) + + func _load_character(resource:DialogicCharacter) -> void: loading = true %DefaultPortraitPicker.set_value(resource.default_portrait) diff --git a/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn index a899bc19..0f1874a2 100644 --- a/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn +++ b/addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn @@ -1,8 +1,8 @@ -[gd_scene load_steps=4 format=3] +[gd_scene load_steps=4 format=3 uid="uid://cmrgbo8qi145o"] [ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.gd" id="1_6sxsl"] -[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="2_birla"] -[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/Vector2.tscn" id="3_vcvin"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="2_birla"] +[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/field_vector2.tscn" id="3_vcvin"] [node name="Portraits" type="GridContainer"] offset_right = 453.0 @@ -15,7 +15,7 @@ script = ExtResource("1_6sxsl") [node name="Label5" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Default:" +text = "Default" [node name="DefaultPortraitPicker" parent="." instance=ExtResource("2_birla")] unique_name_in_owner = true @@ -27,7 +27,7 @@ fit_text_length = false [node name="Label" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Main Scale:" +text = "Main Scale" [node name="MainScale" type="SpinBox" parent="."] unique_name_in_owner = true @@ -41,7 +41,7 @@ suffix = "%" [node name="Label2" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Main Offset:" +text = "Main Offset" [node name="MainOffset" parent="." instance=ExtResource("3_vcvin")] unique_name_in_owner = true @@ -51,7 +51,7 @@ alignment = 2 [node name="Label3" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 0 -text = "Main Mirror:" +text = "Main Mirror" [node name="MainMirror" type="CheckBox" parent="."] unique_name_in_owner = true diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor.gd b/addons/dialogic/Editor/CharacterEditor/character_editor.gd index 9dec6ef3..029b0fd2 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor.gd +++ b/addons/dialogic/Editor/CharacterEditor/character_editor.gd @@ -89,7 +89,7 @@ func _open(extra_info:Variant="") -> void: $NoCharacterScreen.show() return - update_preview() + update_preview(true) %PortraitChangeInfo.hide() @@ -138,6 +138,11 @@ func _ready() -> void: if get_parent() is SubViewport: return + DialogicUtil.get_dialogic_plugin().resource_saved.connect(_on_some_resource_saved) + # NOTE: This check is required because up to 4.2 this signal is not exposed. + if DialogicUtil.get_dialogic_plugin().has_signal("scene_saved"): + DialogicUtil.get_dialogic_plugin().scene_saved.connect(_on_some_resource_saved) + $NoCharacterScreen.color = get_theme_color("dark_color_2", "Editor") $NoCharacterScreen.show() setup_portrait_list_tab() @@ -336,7 +341,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) @@ -345,7 +350,7 @@ 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) @@ -357,7 +362,7 @@ 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 @@ -379,11 +384,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'): @@ -397,12 +402,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)+"/") @@ -411,7 +416,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) @@ -434,7 +439,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: @@ -444,11 +449,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: @@ -460,7 +467,8 @@ func _input(event:InputEvent) -> void: delete_portrait_item(%PortraitTree.get_selected()) 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() @@ -470,24 +478,26 @@ func _on_portrait_right_click_menu_index_pressed(id:int) -> void: # DUPLICATE ITEM elif id == 1: duplicate_item(%PortraitTree.get_selected()) + elif id == 4: + 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 := 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: @@ -501,14 +511,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'): @@ -528,18 +538,14 @@ func report_name_change(item:TreeItem) -> void: ########### PREVIEW ############################################################ #region Preview -func update_preview() -> void: +func update_preview(force := 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) - var mirror:bool = current_portrait_data.get('mirror', false) != current_resource.mirror - var scale:float = current_portrait_data.get('scale', 1) * current_resource.scale - if current_portrait_data.get('ignore_char_scale', false): - scale = current_portrait_data.get('scale', 1) - var offset:Vector2 =current_portrait_data.get('offset', Vector2()) + current_resource.offset - if current_previewed_scene != null \ + + if not force and current_previewed_scene != null \ and current_previewed_scene.get_meta('path', '') == current_portrait_data.get('scene') \ and current_previewed_scene.has_method('_should_do_portrait_update') \ and is_instance_valid(current_previewed_scene.get_script()) \ @@ -548,23 +554,31 @@ func update_preview() -> void: else: for node in %RealPreviewPivot.get_children(): node.queue_free() + current_previewed_scene = null - if current_portrait_data.get('scene', '').is_empty(): - if FileAccess.file_exists(def_portrait_path): - current_previewed_scene = load(def_portrait_path).instantiate() - current_previewed_scene.set_meta('path', '') - else: - if FileAccess.file_exists(current_portrait_data.get('scene')): - current_previewed_scene = load(current_portrait_data.get('scene')).instantiate() - current_previewed_scene.set_meta('path', current_portrait_data.get('scene')) + + var scene_path := def_portrait_path + if not current_portrait_data.get('scene', '').is_empty(): + scene_path = current_portrait_data.get('scene') + + if ResourceLoader.exists(scene_path): + current_previewed_scene = load(scene_path).instantiate() + if current_previewed_scene: %RealPreviewPivot.add_child(current_previewed_scene) if current_previewed_scene != null: - var scene = current_previewed_scene + var scene: Node = current_previewed_scene + scene.show_behind_parent = true DialogicUtil.apply_scene_export_overrides(scene, current_portrait_data.get('export_overrides', {})) + 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 @@ -573,13 +587,14 @@ func update_preview() -> void: scene._update_portrait(preview_character, %PortraitTree.get_full_item_name(selected_item)) if scene.has_method('_set_mirror'): scene._set_mirror(mirror) + if !%FitPreview_Toggle.button_pressed: scene.position = Vector2() + offset scene.scale = Vector2(1,1)*scale else: if is_instance_valid(scene.get_script()) and scene.script.is_tool() and scene.has_method('_get_covered_rect'): - var rect :Rect2= scene._get_covered_rect() - var available_rect:Rect2 = %FullPreviewAvailableRect.get_rect() + 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 @@ -598,6 +613,17 @@ func update_preview() -> void: current_previewed_scene = null +func _on_some_resource_saved(file:Variant) -> void: + if current_previewed_scene == null: + return + + if file is Resource and file == current_previewed_scene.script: + update_preview(true) + + if typeof(file) == TYPE_STRING and file == current_previewed_scene.get_meta("path", ""): + update_preview(true) + + func _on_full_preview_available_rect_resized(): if %FitPreview_Toggle.button_pressed: update_preview() @@ -628,4 +654,5 @@ func _on_fit_preview_toggle_toggled(button_pressed): ## Open the reference manager func _on_reference_manger_button_pressed(): 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 15c0493c..f77d1123 100644 --- a/addons/dialogic/Editor/CharacterEditor/character_editor.tscn +++ b/addons/dialogic/Editor/CharacterEditor/character_editor.tscn @@ -1,11 +1,23 @@ -[gd_scene load_steps=9 format=3 uid="uid://dlskc36c5hrwv"] +[gd_scene load_steps=11 format=3 uid="uid://dlskc36c5hrwv"] [ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor.gd" id="2"] [ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_uhhqs"] [ext_resource type="Script" path="res://addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd" id="2_vad0i"] [ext_resource type="Texture2D" uid="uid://babwe22dqjta" path="res://addons/dialogic/Editor/Images/Pieces/add-folder.svg" id="3_v1qnr"] -[sub_resource type="Image" id="Image_hdno4"] +[sub_resource type="Image" id="Image_yiygw"] +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_hx3oq"] +image = SubResource("Image_yiygw") + +[sub_resource type="Image" id="Image_1n61j"] 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,7 +27,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_u1a6g"] -image = SubResource("Image_hdno4") +image = SubResource("Image_1n61j") [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_es2rd"] @@ -56,9 +68,11 @@ text = "My Character" [node name="NameTooltip" parent="VBoxContainer/TopSection/NameContainer" instance=ExtResource("2_uhhqs")] layout_mode = 2 -tooltip_text = "This name is determined from the file name. Use this name in timelines to reference this character." -texture = SubResource("ImageTexture_u1a6g") -hint_text = "This name is determined from the file name. Use this name in timelines to reference this character." +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") +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"] unique_name_in_owner = true @@ -181,16 +195,21 @@ script = ExtResource("2_vad0i") [node name="PortraitRightClickMenu" type="PopupMenu" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits/PortraitTreePanel/PortraitTree"] size = Vector2i(118, 100) -item_count = 3 +item_count = 5 item_0/text = "Rename" -item_0/icon = SubResource("ImageTexture_u1a6g") +item_0/icon = SubResource("ImageTexture_hx3oq") item_0/id = 2 item_1/text = "Duplicate" -item_1/icon = SubResource("ImageTexture_u1a6g") +item_1/icon = SubResource("ImageTexture_hx3oq") item_1/id = 0 item_2/text = "Delete" -item_2/icon = SubResource("ImageTexture_u1a6g") +item_2/icon = SubResource("ImageTexture_hx3oq") item_2/id = 1 +item_3/text = "" +item_3/id = 3 +item_3/separator = true +item_4/text = "Make Default" +item_4/id = 4 [node name="PortraitChangeInfo" type="HBoxContainer" parent="VBoxContainer/MainHSplit/Split/HBoxContainer/MarginContainer/PortraitListSection/Portraits"] unique_name_in_owner = true diff --git a/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd b/addons/dialogic/Editor/CharacterEditor/character_editor_portrait_tree.gd index d9469d13..eac719a7 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 := {} @@ -11,6 +11,7 @@ func _ready() -> void: $PortraitRightClickMenu.set_item_icon(0, get_theme_icon('Rename', 'EditorIcons')) $PortraitRightClickMenu.set_item_icon(1, get_theme_icon('Duplicate', 'EditorIcons')) $PortraitRightClickMenu.set_item_icon(2, get_theme_icon('Remove', 'EditorIcons')) + $PortraitRightClickMenu.set_item_icon(4, get_theme_icon("Favorites", "EditorIcons")) func clear_tree() -> void: @@ -18,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(): @@ -31,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}) @@ -43,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 @@ -51,12 +52,12 @@ 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 := "" - + for i in Array(path.split('/')).slice(0, -1): item_path += "/"+i item_path = item_path.trim_prefix('/') @@ -69,7 +70,7 @@ func create_necessary_group_items(path:String) -> TreeItem: 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())) @@ -79,21 +80,21 @@ 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) preview.add_theme_stylebox_override('normal', get_theme_stylebox("Background", "EditorStyles")) set_drag_preview(preview) - + return get_selected() -func _can_drop_data(position:Vector2, data:Variant) -> bool: +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 @@ -103,36 +104,35 @@ func _drop_data(position:Vector2, item:Variant) -> void: test_item = test_item.get_parent() if test_item == get_root(): break - + var drop_section := get_drop_section_at_position(position) var parent := get_root() if to_item: parent = to_item.get_parent() - + if to_item and to_item.get_metadata(0).has('group') and drop_section == 1: parent = to_item - + var new_item := copy_branch_or_item(item, parent) - + if to_item and !to_item.get_metadata(0).has('group') and drop_section == 1: new_item.move_after(to_item) - + if drop_section == -1: new_item.move_before(to_item) - + editor.report_name_change(new_item) - + item.free() -func copy_branch_or_item(item:TreeItem, new_parent:TreeItem) -> TreeItem: - var new_item :TreeItem = null +func copy_branch_or_item(item: TreeItem, new_parent: TreeItem) -> TreeItem: + var new_item: TreeItem = null if item.get_metadata(0).has('group'): new_item = add_portrait_group(item.get_text(0), new_parent, item.get_meta('previous_name')) else: new_item = add_portrait_item(item.get_text(0), item.get_metadata(0), new_parent, item.get_meta('previous_name')) - + for child in item.get_children(): copy_branch_or_item(child, new_item) return new_item - diff --git a/addons/dialogic/Editor/Common/broken_reference_manager.gd b/addons/dialogic/Editor/Common/broken_reference_manager.gd index 7f43a81b..1067f4de 100644 --- a/addons/dialogic/Editor/Common/broken_reference_manager.gd +++ b/addons/dialogic/Editor/Common/broken_reference_manager.gd @@ -8,6 +8,12 @@ var reference_changes :Array[Dictionary] = []: reference_changes = changes update_indicator() +var search_regexes: Array[Array] +var finder_thread: Thread +var progress_mutex: Mutex +var progress_percent: float = 0.0 +var progress_message: String = "" + func _ready() -> void: if owner.get_parent() is SubViewport: @@ -24,7 +30,7 @@ func _ready() -> void: %Replace.icon = get_theme_icon("ArrowRight", "EditorIcons") %State.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")) - + visibility_changed.connect(func(): if !visible: close()) await get_parent().ready var tab_button: Control = %TabA @@ -46,6 +52,7 @@ func open() -> void: %ChangeTree.create_item() %ChangeTree.set_column_expand(0, false) %ChangeTree.set_column_expand(2, false) + %ChangeTree.set_column_custom_minimum_width(2, 50) var categories := {null:%ChangeTree.get_root()} for i in reference_changes: var parent : TreeItem = null @@ -83,6 +90,7 @@ func _on_change_tree_button_clicked(item:TreeItem, column:int, id:int, mouse_but %ReplacementSection.hide() + func _on_change_tree_item_edited() -> void: if !%ChangeTree.get_selected(): return @@ -109,50 +117,43 @@ func _on_check_button_pressed() -> void: func open_finder(replacements:Array[Dictionary]) -> void: %ReplacementSection.show() - var regexes : Array[Array] = [] + %Progress.show() + %ReferenceTree.hide() + search_regexes = [] for i in replacements: if i.has('character_names') and !i.character_names.is_empty(): i['character_regex'] = RegEx.create_from_string("(?m)^(join|update|leave)?\\s*("+str(i.character_names).replace('"', '').replace(', ', '|').trim_suffix(']').trim_prefix('[').replace('/', '\\/')+")(?(1).*|.*:)") for regex_string in i.regex: var regex := RegEx.create_from_string(regex_string) - regexes.append([regex, i]) - - var finds : Array[Dictionary] = [] + search_regexes.append([regex, i]) - for timeline_path in DialogicResourceUtil.list_resources_of_type('.dtl'): - %State.text = "Loading '"+timeline_path+"'" + finder_thread = Thread.new() + progress_mutex = Mutex.new() + finder_thread.start(search_timelines.bind(search_regexes)) - var timeline_file := FileAccess.open(timeline_path, FileAccess.READ) - var timeline_text :String = timeline_file.get_as_text() - var timeline_events : PackedStringArray = timeline_text.split('\n') - timeline_file.close() - for regex_info in regexes: - %State.text = "Searching '"+timeline_path+"' for "+regex_info[1].what+' -> '+regex_info[1].forwhat - for i in regex_info[0].search_all(timeline_text): - if regex_info[1].has('character_regex'): - if regex_info[1].character_regex.search(get_line(timeline_text, i.get_start()+1)) == null: - continue +func _process(delta: float) -> void: + if finder_thread and finder_thread.is_started(): + if finder_thread.is_alive(): + progress_mutex.lock() + %State.text = progress_message + %Progress.value = progress_percent + progress_mutex.unlock() + else: + var finds := finder_thread.wait_to_finish() + display_search_results(finds) - var line_number := timeline_text.count('\n', 0, i.get_start()+1)+1 - var line := timeline_text.get_slice('\n', line_number-1) - finds.append({ - 'match':i, - 'timeline':timeline_path, - 'info': regex_info[1], - 'line_number': line_number, - 'line': line, - 'line_start': timeline_text.rfind('\n', i.get_start()) - }) - regex_info[1]['count'] += 1 - for regex_info in regexes: +func display_search_results(finds:Array[Dictionary]) -> void: + %Progress.hide() + %ReferenceTree.show() + for regex_info in search_regexes: regex_info[1]['item'].set_text(2, str(regex_info[1]['count'])) - update_count_coloring() + update_count_coloring() %State.text = str(len(finds))+ " occurrences found" %ReferenceTree.clear() @@ -162,7 +163,7 @@ func open_finder(replacements:Array[Dictionary]) -> void: var timelines := {} var height := 0 for i in finds: - var parent : TreeItem = null + var parent: TreeItem = null if !i.timeline in timelines: parent = %ReferenceTree.create_item() parent.set_text(1, i.timeline) @@ -172,7 +173,7 @@ func open_finder(replacements:Array[Dictionary]) -> void: else: parent = timelines[i.timeline] - var item :TreeItem = %ReferenceTree.create_item(parent) + var item: TreeItem = %ReferenceTree.create_item(parent) item.set_text(1, 'Line '+str(i.line_number)+': '+i.line) item.set_tooltip_text(1, i.info.what+' -> '+i.info.forwhat) item.set_cell_mode(0, TreeItem.CELL_MODE_CHECK) @@ -180,11 +181,9 @@ func open_finder(replacements:Array[Dictionary]) -> void: item.set_editable(0, true) item.set_metadata(0, i) height += %ReferenceTree.get_item_area_rect(item).size.y+10 - var change_item :TreeItem = i.info.item + var change_item: TreeItem = i.info.item change_item.set_meta('found_items', change_item.get_meta('found_items', [])+[item]) - - %ReferenceTree.custom_minimum_size.y = min(height, 200) %ReferenceTree.visible = !finds.is_empty() @@ -195,6 +194,52 @@ func open_finder(replacements:Array[Dictionary]) -> void: %Replace.grab_focus() +func search_timelines(regexes:Array[Array]) -> Array[Dictionary]: + var finds: Array[Dictionary] = [] + + var timeline_paths := DialogicResourceUtil.list_resources_of_type('.dtl') + + var progress := 0 + var progress_max: float = len(timeline_paths)*len(regexes) + + for timeline_path:String in timeline_paths: + + var timeline_file := FileAccess.open(timeline_path, FileAccess.READ) + var timeline_text: String = timeline_file.get_as_text() + var timeline_event: PackedStringArray = timeline_text.split('\n') + timeline_file.close() + + for regex_info in regexes: + progress += 1 + progress_mutex.lock() + progress_percent = 1/progress_max*progress + progress_message = "Searching '"+timeline_path+"' for "+regex_info[1].what+' -> '+regex_info[1].forwhat + progress_mutex.unlock() + for i in regex_info[0].search_all(timeline_text): + if regex_info[1].has('character_regex'): + if regex_info[1].character_regex.search(get_line(timeline_text, i.get_start()+1)) == null: + continue + + var line_number := timeline_text.count('\n', 0, i.get_start()+1)+1 + var line := timeline_text.get_slice('\n', line_number-1) + finds.append({ + 'match':i, + 'timeline':timeline_path, + 'info': regex_info[1], + 'line_number': line_number, + 'line': line, + 'line_start': timeline_text.rfind('\n', i.get_start()) + }) + regex_info[1]['count'] += 1 + return finds + + +func _exit_tree() -> void: + # Shutting of + if finder_thread and finder_thread.is_alive(): + finder_thread.wait_to_finish() + + func get_line(string:String, at_index:int) -> String: return string.substr(max(string.rfind('\n', at_index), 0), string.find('\n', at_index)-string.rfind('\n', at_index)) @@ -316,7 +361,3 @@ func close() -> void: func _on_add_button_pressed() -> void: %ReplacementEditPanel._on_add_pressed() - - -func _on_help_button_pressed() -> void: - pass # Replace with function body. diff --git a/addons/dialogic/Editor/Common/reference_manager.tscn b/addons/dialogic/Editor/Common/reference_manager.tscn index 127ca90d..1774073c 100644 --- a/addons/dialogic/Editor/Common/reference_manager.tscn +++ b/addons/dialogic/Editor/Common/reference_manager.tscn @@ -1,14 +1,14 @@ -[gd_scene load_steps=15 format=3 uid="uid://c7lmt5cp7bxcm"] +[gd_scene load_steps=13 format=3 uid="uid://c7lmt5cp7bxcm"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Common/reference_manager.gd" id="1_3t531"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Common/broken_reference_manager.gd" id="1_agmg4"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Common/ReferenceManager_AddReplacementPanel.gd" id="2_tt4jd"] -[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3_yomsc"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3_yomsc"] [ext_resource type="Script" path="res://addons/dialogic/Editor/Common/unique_identifiers_manager.gd" id="5_wnvbq"] [sub_resource type="ButtonGroup" id="ButtonGroup_l6uiy"] -[sub_resource type="Image" id="Image_7j3bm"] +[sub_resource type="Image" id="Image_ii0d5"] 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,33 +18,21 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_a0gfq"] -image = SubResource("Image_7j3bm") +image = SubResource("Image_ii0d5") -[sub_resource type="Image" id="Image_scsup"] +[sub_resource type="Image" id="Image_k5gyt"] 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, 231, 231, 231, 31, 224, 224, 224, 169, 224, 224, 224, 231, 224, 224, 224, 232, 224, 224, 224, 169, 229, 229, 229, 29, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 31, 224, 224, 224, 240, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 238, 229, 229, 229, 29, 255, 255, 255, 0, 255, 255, 255, 0, 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, 169, 224, 224, 224, 255, 224, 224, 224, 180, 227, 227, 227, 27, 228, 228, 228, 28, 225, 225, 225, 182, 224, 224, 224, 255, 225, 225, 225, 167, 255, 255, 255, 0, 255, 255, 255, 0, 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, 231, 224, 224, 224, 255, 227, 227, 227, 27, 255, 255, 255, 0, 255, 255, 255, 0, 228, 228, 228, 28, 224, 224, 224, 255, 224, 224, 224, 230, 255, 255, 255, 0, 255, 255, 255, 0, 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, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 230, 225, 225, 225, 134, 255, 255, 255, 5, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 134, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 255, 224, 224, 224, 229, 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, 255, 255, 255, 0, 255, 255, 255, 2, 225, 225, 225, 133, 224, 224, 224, 230, 224, 224, 224, 226, 224, 224, 224, 255, 224, 224, 224, 255, 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, 255, 255, 255, 0, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 225, 224, 224, 224, 255, 227, 227, 227, 62, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 225, 224, 224, 224, 255, 227, 227, 227, 63, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 131, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 128, 224, 224, 224, 225, 224, 224, 224, 220, 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), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_g7hay"] -image = SubResource("Image_scsup") - -[sub_resource type="Image" id="Image_r25e4"] -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, 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, 224, 224, 224, 255, 224, 224, 224, 180, 228, 228, 228, 28, 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, 229, 229, 229, 29, 225, 225, 225, 183, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 227, 227, 227, 27, 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, 230, 230, 230, 30, 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, 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, 231, 231, 231, 31, 224, 224, 224, 255, 224, 224, 224, 255, 229, 229, 229, 29, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 31, 224, 224, 224, 186, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 230, 230, 230, 30, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"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_ix3td"] -image = SubResource("Image_r25e4") +[sub_resource type="ImageTexture" id="ImageTexture_8oycd"] +image = SubResource("Image_k5gyt") -[sub_resource type="Image" id="Image_2yu3b"] +[sub_resource type="Image" id="Image_qpnp8"] 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", @@ -54,7 +42,7 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_lce2m"] -image = SubResource("Image_2yu3b") +image = SubResource("Image_qpnp8") [node name="Manager" type="PanelContainer"] anchors_preset = 15 @@ -116,11 +104,6 @@ size_flags_horizontal = 2 tooltip_text = "Add custom rename" icon = SubResource("ImageTexture_a0gfq") -[node name="HelpButton" type="Button" parent="Tabs/BrokenReferences/ChangesList/VBox/HBoxContainer"] -layout_mode = 2 -disabled = true -text = "What is this about?" - [node name="ReplacementEditPanel" type="PanelContainer" parent="Tabs/BrokenReferences/ChangesList/VBox"] unique_name_in_owner = true visible = false @@ -180,14 +163,14 @@ unique_name_in_owner = true layout_mode = 2 tooltip_text = "Match Case" toggle_mode = true -icon = SubResource("ImageTexture_g7hay") +icon = SubResource("ImageTexture_8oycd") [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_ix3td") +icon = SubResource("ImageTexture_8oycd") [node name="HBoxContainer4" type="HBoxContainer" parent="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel/VBox"] layout_mode = 2 @@ -267,6 +250,11 @@ theme_override_constants/draw_relationship_lines = 1 columns = 2 hide_root = true +[node name="Progress" type="ProgressBar" parent="Tabs/BrokenReferences/ReplacementSection/FindList"] +unique_name_in_owner = true +layout_mode = 2 +max_value = 1.0 + [node name="Replace" type="Button" parent="Tabs/BrokenReferences/ReplacementSection/FindList"] unique_name_in_owner = true layout_mode = 2 @@ -287,12 +275,13 @@ layout_mode = 2 [node name="Tools" type="HBoxContainer" parent="Tabs/UniqueIdentifiers/VBox"] layout_mode = 2 -alignment = 2 +alignment = 1 -[node name="HelpButton" type="Button" parent="Tabs/UniqueIdentifiers/VBox/Tools"] +[node name="Search" type="LineEdit" parent="Tabs/UniqueIdentifiers/VBox/Tools"] +unique_name_in_owner = true +custom_minimum_size = Vector2(400, 0) layout_mode = 2 -disabled = true -text = "What is this about?" +placeholder_text = "Search" [node name="IdentifierTable" type="Tree" parent="Tabs/UniqueIdentifiers/VBox"] unique_name_in_owner = true @@ -309,8 +298,15 @@ 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="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?" +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"] -[connection signal="pressed" from="Tabs/BrokenReferences/ChangesList/VBox/HBoxContainer/HelpButton" to="Tabs/BrokenReferences" method="_on_help_button_pressed"] [connection signal="item_selected" from="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel/VBox/HBoxContainer3/Type" to="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel" method="_on_type_item_selected"] [connection signal="item_selected" from="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel/VBox/HBoxContainer4/Where" to="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel" method="_on_where_item_selected"] [connection signal="pressed" from="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel/VBox/AddButton" to="Tabs/BrokenReferences/ChangesList/VBox/ReplacementEditPanel" method="save"] @@ -318,6 +314,6 @@ autowrap_mode = 3 [connection signal="item_edited" from="Tabs/BrokenReferences/ChangesList/VBox/ChangeTree" to="Tabs/BrokenReferences" method="_on_change_tree_item_edited"] [connection signal="pressed" from="Tabs/BrokenReferences/ChangesList/VBox/CheckButton" to="Tabs/BrokenReferences" method="_on_check_button_pressed"] [connection signal="pressed" from="Tabs/BrokenReferences/ReplacementSection/FindList/Replace" to="Tabs/BrokenReferences" method="_on_replace_pressed"] -[connection signal="pressed" from="Tabs/UniqueIdentifiers/VBox/Tools/HelpButton" to="Tabs/UniqueIdentifiers" method="_on_help_button_pressed"] +[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"] diff --git a/addons/dialogic/Editor/Common/reference_manager_window.gd b/addons/dialogic/Editor/Common/reference_manager_window.gd index 33395663..fae954c1 100644 --- a/addons/dialogic/Editor/Common/reference_manager_window.gd +++ b/addons/dialogic/Editor/Common/reference_manager_window.gd @@ -54,27 +54,39 @@ func add_ref_change(old_name:String, new_name:String, type:Types, where:=Where.T if '' in old_name: regexes = [old_name] else: - regexes = ['(?'+old_name.replace('/', '\\/')+')'] + regexes = [ + r'(?%s)' % old_name.replace('/', '\\/') + ] if !case_sensitive: regexes[0] = '(?i)'+regexes[0] if whole_words: regexes = ['\\b'+regexes[0]+'\\b'] Types.VARIABLE: - regexes = ['{(?\\s*'+old_name.replace('/', '\\/')+'\\s*)}', 'var\\s*=\\s*"(?\\s*'+old_name.replace('/', '\\/')+'\\s*)"'] + regexes = [ + r'{(?\s*%s\s*)}' % old_name.replace("/", "\\/"), + r'var\s*=\s*"(?\s*%s\s*)"' % old_name.replace("/", "\\/") + ] category_name = "Variables" Types.PORTRAIT: - regexes = ['(?m)^[^:(]*\\((?'+old_name.replace('/', '\\/')+')\\)', '\\[\\s*portrait\\s*=(?\\s*'+old_name.replace('/', '\\/')+'\\s*)\\]'] + regexes = [ + r'(?m)^[^:(\n]*\((?%s)\)' % old_name.replace('/', '\\/'), + r'\[\s*portrait\s*=(?\s*%s\s*)\]' % old_name.replace('/', '\\/') + ] category_name = "Portraits by "+character_names[0] Types.CHARACTER_NAME: # for reference: ((join|leave|update) )?(?NAME)(?!\B)(?(1)|(?!([^:\n]|\\:)*(\n|$))) - regexes = ['((join|leave|update) )?(?'+old_name+')(?!\\B)(?(1)|(?!([^:\\n]|\\\\:)*(\\n|$)))'] + regexes = [ + r'((join|leave|update) )?(?%s)(?!\B)(?(1)|(?!([^:\n]|\\:)*(\n|$)))' % old_name + ] category_name = "Renamed Character Files" Types.TIMELINE_NAME: - regexes = ['timeline ?= ?" ?(?'+old_name+') ?"'] + regexes = [ + r'timeline ?= ?" ?(?%s) ?"' % old_name + ] category_name = "Renamed Timeline Files" if where != Where.BY_CHARACTER: diff --git a/addons/dialogic/Editor/Common/side_bar.tscn b/addons/dialogic/Editor/Common/side_bar.tscn index a9dc0d0d..1f08b393 100644 --- a/addons/dialogic/Editor/Common/side_bar.tscn +++ b/addons/dialogic/Editor/Common/side_bar.tscn @@ -77,6 +77,7 @@ size_flags_vertical = 3 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 @@ -89,6 +90,11 @@ text = "Some Version" flat = true clip_text = true +[node name="RightClickMenu" type="PopupMenu" parent="."] +unique_name_in_owner = true +size = Vector2i(164, 100) + [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"] +[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 6dacf0ad..dd30105a 100644 --- a/addons/dialogic/Editor/Common/sidebar.gd +++ b/addons/dialogic/Editor/Common/sidebar.gd @@ -37,6 +37,13 @@ func _ready(): $VBox/Margin.set("theme_override_constants/margin_left", 4 * editor_scale) $VBox/Margin.set("theme_override_constants/margin_bottom", 4 * 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("Filesystem", "EditorIcons"), "Show in FileSystem", 2) + %RightClickMenu.add_icon_item(get_theme_icon("ExternalLink", "EditorIcons"), "Open in External Program", 3) + ################################################################################ ## RESOURCE LIST @@ -52,7 +59,7 @@ func _on_editors_editor_changed(previous:DialogicEditor, current:DialogicEditor) func clean_resource_list(resources_list:Array = []) -> PackedStringArray: - return PackedStringArray(resources_list.filter(func(x): return FileAccess.file_exists(x))) + return PackedStringArray(resources_list.filter(func(x): return ResourceLoader.exists(x))) func update_resource_list(resources_list:PackedStringArray = []) -> void: @@ -112,13 +119,11 @@ func _on_resources_list_item_selected(index:int) -> void: 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) + if mouse_button_index == MOUSE_BUTTON_MIDDLE: + remove_item_from_list(index) + if mouse_button_index == MOUSE_BUTTON_RIGHT: + %RightClickMenu.popup_on_parent(Rect2(get_global_mouse_position(), Vector2())) + %RightClickMenu.set_meta('item_index', index) func _on_search_text_changed(new_text:String) -> void: @@ -157,8 +162,27 @@ func update_content_list(list:PackedStringArray) -> void: var label_directory := DialogicResourceUtil.get_label_cache() if current_resource != null: for i in timeline_directory: - if timeline_directory[i] == current_resource.resource_path: + if timeline_directory[i] == current_resource.resource_path: label_directory[i] = list + # also always store the current timelines labels for easy access + label_directory[""] = list + DialogicResourceUtil.set_label_cache(label_directory) +func remove_item_from_list(index) -> void: + var new_list := [] + for entry in DialogicUtil.get_editor_setting('last_resources', []): + if entry != %ResourcesList.get_item_metadata(index): + new_list.append(entry) + DialogicUtil.set_editor_setting('last_resources', new_list) + %ResourcesList.remove_item(index) + +func _on_right_click_menu_id_pressed(id:int) -> void: + match id: + 1: # REMOVE ITEM FROM LIST + remove_item_from_list(%RightClickMenu.get_meta("item_index")) + 2: # OPEN IN FILESYSTEM + EditorInterface.get_file_system_dock().navigate_to_path(%ResourcesList.get_item_metadata(%RightClickMenu.get_meta("item_index"))) + 3: # OPEN IN EXTERNAL EDITOR + OS.shell_open(ProjectSettings.globalize_path(%ResourcesList.get_item_metadata(%RightClickMenu.get_meta("item_index")))) diff --git a/addons/dialogic/Editor/Common/unique_identifiers_manager.gd b/addons/dialogic/Editor/Common/unique_identifiers_manager.gd index 64368253..28a47354 100644 --- a/addons/dialogic/Editor/Common/unique_identifiers_manager.gd +++ b/addons/dialogic/Editor/Common/unique_identifiers_manager.gd @@ -75,5 +75,21 @@ func _on_identifier_table_button_clicked(item: TreeItem, column: int, id: int, m %IdentifierTable.edit_selected(true) -func _on_help_button_pressed() -> void: - pass # Replace with function body. +func filter_tree(filter:String= "", item:TreeItem = null) -> bool: + if item == null: + item = %IdentifierTable.get_root() + + var any := false + for child in item.get_children(): + if child.get_child_count() > 0: + child.visible = filter_tree(filter, child) + if child.visible: any = true + else: + child.visible = filter.is_empty() or filter.to_lower() in child.get_text(0).to_lower() or filter.to_lower() in child.get_text(1).to_lower() + if child.visible: any = true + + return any + + +func _on_search_text_changed(new_text: String) -> void: + filter_tree(new_text) diff --git a/addons/dialogic/Editor/Common/update_install_window.gd b/addons/dialogic/Editor/Common/update_install_window.gd index 6652e922..7cdda8b1 100644 --- a/addons/dialogic/Editor/Common/update_install_window.gd +++ b/addons/dialogic/Editor/Common/update_install_window.gd @@ -33,20 +33,37 @@ func load_info(info:Dictionary, update_type:int) -> void: %ShortInfo.text = "Huh, what happened here?" %ReadFull.hide() %Install.disabled = true + return + + # If we are up to date (or beyond): + if info.is_empty(): + info['name'] = "You are in the future, Marty!" + info["body"] = "# 😎 You are using the WIP branch!\nSeems like you are using a version that isn't even released yet. Be careful and give us your feedback ;)" + info["published_at"] = "????T" + info["author"] = {'login':"???"} + %State.text = "Where are we Doc?" + %UpdateName.add_theme_color_override("font_color", editor_view.get_theme_color("property_color_z", "Editor")) + %Install.disabled = true + + elif update_type == 0: + %State.text = "Update Available!" + %UpdateName.add_theme_color_override("font_color", editor_view.get_theme_color("warning_color", "Editor")) + %Install.disabled = false else: - %UpdateName.text = info.name - %Content.text = markdown_to_bbcode('#'+info.body.get_slice('#', 1)).strip_edges() - %ShortInfo.text = "Published on "+info.published_at.substr(0, info.published_at.find('T'))+" by "+info.author.login + %State.text = "You are up to date:" + %UpdateName.add_theme_color_override("font_color", editor_view.get_theme_color("success_color", "Editor")) + %Install.disabled = true + + %UpdateName.text = info.name + %Content.text = markdown_to_bbcode('#'+info.body.get_slice('#', 1)).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 %ReadFull.show() - if update_type == 0: - %State.text = "Update Available!" - %UpdateName.add_theme_color_override("font_color", editor_view.get_theme_color("warning_color", "Editor")) - %Install.disabled = false - else: - %State.text = "You are up to date:" - %UpdateName.add_theme_color_override("font_color", editor_view.get_theme_color("success_color", "Editor")) - %Install.disabled = true + else: + %ReadFull.hide() + if info.has('reactions'): + %Reactions.show() var reactions := {"laugh":"😂", "hooray":"🎉", "confused":"😕", "heart":"❤️", "rocket":"🚀", "eyes":"👀"} for i in reactions: %Reactions.get_node(i.capitalize()).visible = info.reactions[i] > 0 @@ -56,7 +73,8 @@ func load_info(info:Dictionary, update_type:int) -> void: %Reactions.get_node("Likes").text = "👍 "+str(info.reactions['+1']+info.reactions['-1']) else: %Reactions.get_node("Likes").visible = false - + else: + %Reactions.hide() func _on_window_close_requested(): get_parent().visible = false @@ -88,12 +106,13 @@ func _on_update_manager_downdload_completed(result:int): func _on_resources_reimported(resources:Array) -> void: - await get_tree().process_frame - get_parent().move_to_foreground() + if is_inside_tree(): + await get_tree().process_frame + get_parent().move_to_foreground() func markdown_to_bbcode(text:String) -> String: - var font_sizes := {1:16, 2:16, 3:16,4:14, 5:14} + var font_sizes := {1:20, 2:16, 3:16,4:14, 5:14} var title_regex := RegEx.create_from_string('(^|\n)((?#+)(?.*))\\n') var res := title_regex.search(text) while res: diff --git a/addons/dialogic/Editor/Common/update_manager.gd b/addons/dialogic/Editor/Common/update_manager.gd index faee9f49..d1296a33 100644 --- a/addons/dialogic/Editor/Common/update_manager.gd +++ b/addons/dialogic/Editor/Common/update_manager.gd @@ -10,7 +10,7 @@ enum UpdateCheckResult {UPDATE_AVAILABLE, UP_TO_DATE, NO_ACCESS} enum DownloadResult {SUCCESS, FAILURE} enum ReleaseState {ALPHA, BETA, STABLE} -const REMOTE_RELEASES_URL := "https://api.github.com/repos/coppolaemilio/dialogic/releases" +const REMOTE_RELEASES_URL := "https://api.github.com/repos/dialogic-godot/dialogic/releases" const TEMP_FILE_NAME = "user://temp.zip" var current_version : String = "" @@ -55,7 +55,7 @@ func _on_UpdateCheck_request_completed(result:int, response_code:int, headers:Pa update_info = versions[0] update_check_completed.emit(UpdateCheckResult.UPDATE_AVAILABLE) else: - update_info = {} + update_info = current_info update_check_completed.emit(UpdateCheckResult.UP_TO_DATE) @@ -70,12 +70,15 @@ func compare_versions(release, current_release_info:Dictionary) -> bool: if checked_release_info.state < current_release_info.state: return false + elif checked_release_info.state == current_release_info.state: if checked_release_info.state_version < current_release_info.state_version: return false + if checked_release_info.state_version == current_release_info.state_version: current_info = release return false + if checked_release_info.state == ReleaseState.STABLE: if checked_release_info.minor == current_release_info.minor: current_info = release diff --git a/addons/dialogic/Editor/Events/BranchEnd.gd b/addons/dialogic/Editor/Events/BranchEnd.gd index 45ba445c..0500cf63 100644 --- a/addons/dialogic/Editor/Events/BranchEnd.gd +++ b/addons/dialogic/Editor/Events/BranchEnd.gd @@ -2,19 +2,21 @@ extends Control ## A scene shown at the end of events that contain other events -var resource : DialogicEndBranchEvent +var resource: DialogicEndBranchEvent # References -var parent_node : Control = null -var end_control :Control = null +var parent_node: Control = null +var end_control: Control = null # Indent -var indent_size := 15 +var indent_size := 22 var current_indent_level := 1 +var selected := false + func _ready() -> void: $Icon.icon = get_theme_icon("GuiSpinboxUpdown", "EditorIcons") - $Spacer.custom_minimum_size.x = 100*DialogicUtil.get_editor_scale() + $Spacer.custom_minimum_size.x = 90*DialogicUtil.get_editor_scale() visual_deselect() parent_node_changed() @@ -22,14 +24,20 @@ func _ready() -> void: ## Called by the visual timeline editor func visual_select() -> void: modulate = get_theme_color("highlighted_font_color", "Editor") + selected = true ## Called by the visual timeline editor func visual_deselect() -> void: if !parent_node:return + selected = false modulate = parent_node.resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.3) +func is_selected() -> bool: + return selected + + ## Called by the visual timeline editor func highlight() -> void: if !parent_node:return @@ -51,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, 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/BranchEnd.tscn b/addons/dialogic/Editor/Events/BranchEnd.tscn index e90f8fc2..9f4147bc 100644 --- a/addons/dialogic/Editor/Events/BranchEnd.tscn +++ b/addons/dialogic/Editor/Events/BranchEnd.tscn @@ -2,23 +2,22 @@ [ext_resource type="Script" path="res://addons/dialogic/Editor/Events/BranchEnd.gd" id="1"] -[sub_resource type="Image" id="Image_8tlok"] +[sub_resource type="Image" id="Image_8jrl8"] 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), +"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_vm1jl"] -image = SubResource("Image_8tlok") +[sub_resource type="ImageTexture" id="ImageTexture_44ap0"] +image = SubResource("Image_8jrl8") [node name="EndBranch" type="HBoxContainer"] -custom_minimum_size = Vector2(0, 40) anchors_preset = 10 anchor_right = 1.0 -offset_bottom = 40.0 +offset_bottom = 24.0 grow_horizontal = 2 mouse_filter = 0 script = ExtResource("1") @@ -28,7 +27,7 @@ layout_mode = 2 size_flags_vertical = 0 [node name="Spacer" type="Control" parent="."] -custom_minimum_size = Vector2(75, 0) +custom_minimum_size = Vector2(90, 0) layout_mode = 2 size_flags_vertical = 0 @@ -40,7 +39,7 @@ size_flags_vertical = 4 tooltip_text = "Click and drag" focus_mode = 0 mouse_filter = 1 -icon = SubResource("ImageTexture_vm1jl") +icon = SubResource("ImageTexture_44ap0") flat = true [node name="HiddenEventsLabel" type="Label" parent="."] diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.gd b/addons/dialogic/Editor/Events/EventBlock/event_block.gd index d34b2229..bce4a146 100644 --- a/addons/dialogic/Editor/Events/EventBlock/event_block.gd +++ b/addons/dialogic/Editor/Events/EventBlock/event_block.gd @@ -3,68 +3,119 @@ extends MarginContainer ## Scene that represents an event in the visual timeline editor. -signal option_action(action_name) signal content_changed() -# Resource +## REFERENCES var resource : DialogicEvent - -var selected : bool = false - -### internal node eferences -@onready var warning := %Warning -@onready var icon_texture := %IconTexture -@onready var header_content_container := %HeaderContent -@onready var body_container := %Body -@onready var body_content_container := %BodyContent - -# is the body visible -var expanded := true - -# was the body content loaded -var body_was_build := false - -# does the body have elements? -var has_any_enabled_body_content := false - -# list that stores visibility conditions -var field_list := [] - +var editor_reference # for choice and condition -var end_node:Node = null: +var end_node: Node = null: get: return end_node set(node): end_node = node %CollapseButton.visible = true if end_node else false + +## FLAGS +var selected := false +# Whether the body is visible +var expanded := true +var body_was_build := false +var has_any_enabled_body_content := false +# Whether contained events (e.g. in choices) are visible var collapsed := false -### extarnal node references -var editor_reference -### Icon size -var icon_size := 28 +## CONSTANTS +const icon_size := 28 +const indent_size := 22 -### the indent size -var indent_size := 22 +## STATE +# List that stores visibility conditions +var field_list := [] var current_indent_level := 1 -# Setting this to true will ignore the event while saving -# Useful for making placeholder events in drag and drop -var ignore_save := false +#region UI AND LOGIC INITIALIZATION +################################################################################ + +func _ready(): + if get_parent() is SubViewport: + return + + if not resource: + printerr("[Dialogic] Event block was added without a resource specified.") + return + + initialize_ui() + initialize_logic() + + +func initialize_ui() -> void: + var _scale := DialogicUtil.get_editor_scale() + + $PanelContainer.self_modulate = get_theme_color("accent_color", "Editor") + + # Warning Icon + %Warning.texture = get_theme_icon("NodeWarning", "EditorIcons") + %Warning.size = Vector2(16 * _scale, 16 * _scale) + %Warning.position = Vector2(-5 * _scale, -10 * _scale) + + # Expand Button + %ExpandButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") + %ExpandButton.modulate = get_theme_color("readonly_color", "Editor") + + # Icon Panel + %IconPanel.tooltip_text = resource.event_name + %IconPanel.self_modulate = resource.event_color + + # Event Icon + %IconTexture.texture = resource._get_icon() -## ***************************************************************************** -## PUBLIC METHODS -## ***************************************************************************** + %IconPanel.custom_minimum_size = Vector2(icon_size, icon_size) * _scale + %IconTexture.custom_minimum_size = %IconPanel.custom_minimum_size + + var custom_style: StyleBoxFlat = %IconPanel.get_theme_stylebox('panel') + custom_style.set_corner_radius_all(5 * _scale) + + # Focus Mode + set_focus_mode(1) # Allowing this node to grab focus + + # Separation on the header + %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() + + %Body.add_theme_constant_override("margin_left", icon_size * _scale) + + visual_deselect() + + +func initialize_logic() -> void: + resized.connect(get_parent().get_parent().queue_redraw) + + resource.ui_update_needed.connect(_on_resource_ui_update_needed) + resource.ui_update_warning.connect(set_warning) + + content_changed.connect(recalculate_field_visibility) + + _on_ExpandButton_toggled(resource.expand_by_default or resource.created_by_button) + +#endregion + + +#region VISUAL METHODS +################################################################################ func visual_select() -> void: $PanelContainer.add_theme_stylebox_override('panel', load("res://addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres")) selected = true %IconPanel.self_modulate = resource.event_color %IconTexture.modulate = get_theme_color("icon_saturation", "Editor") -# %IconTexture.self_modulate.a = 1 func visual_deselect() -> void: @@ -72,66 +123,53 @@ func visual_deselect() -> void: selected = false %IconPanel.self_modulate = resource.event_color.lerp(Color.DARK_SLATE_GRAY, 0.1) %IconTexture.modulate = get_theme_color('font_color', 'Label') -# %IconTexture.self_modulate.a = 0.7 func is_selected() -> bool: return selected -# called by the timeline before adding it to the tree -func load_data(data:DialogicEvent) -> void: - resource = data - func set_warning(text:String= "") -> void: if !text.is_empty(): - warning.show() - warning.tooltip_text = text + %Warning.show() + %Warning.tooltip_text = text else: - warning.hide() + %Warning.hide() func set_indent(indent: int) -> void: - add_theme_constant_override("margin_left", indent_size*indent) + add_theme_constant_override("margin_left", indent_size*indent*DialogicUtil.get_editor_scale()) current_indent_level = indent +#endregion -## ***************************************************************************** -## PRIVATE METHODS -## ***************************************************************************** - -func _set_event_icon(icon: Texture) -> void: - icon_texture.texture = icon - var _scale := DialogicUtil.get_editor_scale() - var ip := %IconPanel - var ipc := icon_texture - - # Resizing the icon acording to the scale - - ip.custom_minimum_size = Vector2(icon_size, icon_size) * _scale - ipc.custom_minimum_size = ip.custom_minimum_size - - # Updating the theme properties to scale - var custom_style :StyleBox = ip.get_theme_stylebox('panel') - custom_style.corner_radius_top_left = 5 * _scale - custom_style.corner_radius_top_right = 5 * _scale - custom_style.corner_radius_bottom_left = 5 * _scale - custom_style.corner_radius_bottom_right = 5 * _scale +#region EVENT FIELDS +################################################################################ -func toggle_collapse(toggled:bool) -> void: - collapsed = toggled - var timeline_editor = find_parent('VisualEditor') - if (timeline_editor != null): - # @todo select item and clear selection is marked as "private" in TimelineEditor.gd - # consider to make it "public" or add a public helper function - timeline_editor.indent_events() - +var FIELD_SCENES := { + DialogicEvent.ValueType.MULTILINE_TEXT: "res://addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn", + DialogicEvent.ValueType.SINGLELINE_TEXT: "res://addons/dialogic/Editor/Events/Fields/field_text_singleline.tscn", + DialogicEvent.ValueType.FILE: "res://addons/dialogic/Editor/Events/Fields/field_file.tscn", + DialogicEvent.ValueType.BOOL: "res://addons/dialogic/Editor/Events/Fields/field_bool_check.tscn", + DialogicEvent.ValueType.BOOL_BUTTON: "res://addons/dialogic/Editor/Events/Fields/field_bool_button.tscn", + DialogicEvent.ValueType.CONDITION: "res://addons/dialogic/Editor/Events/Fields/field_condition.tscn", + DialogicEvent.ValueType.ARRAY: "res://addons/dialogic/Editor/Events/Fields/field_array.tscn", + DialogicEvent.ValueType.DICTIONARY: "res://addons/dialogic/Editor/Events/Fields/field_dictionary.tscn", + DialogicEvent.ValueType.DYNAMIC_OPTIONS: "res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn", + DialogicEvent.ValueType.FIXED_OPTIONS : "res://addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn", + 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" + } func build_editor(build_header:bool = true, build_body:bool = false) -> void: - var current_body_container :HFlowContainer = null + var current_body_container: HFlowContainer = null + + if build_body and body_was_build: + build_body = false - if build_body and body_was_build: build_body = false if build_body: if body_was_build: return @@ -162,84 +200,17 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: %BodyContent.add_child(current_body_container) continue - ### STRINGS - elif p.dialogic_type == resource.ValueType.MULTILINE_TEXT: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/MultilineText.tscn").instantiate() - elif p.dialogic_type == resource.ValueType.SINGLELINE_TEXT: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/SinglelineText.tscn").instantiate() - editor_node.placeholder = p.display_info.get('placeholder', '') - elif p.dialogic_type == resource.ValueType.BOOL: - if p.display_info.has('icon') or p.display_info.has('editor_icon'): - editor_node = load("res://addons/dialogic/Editor/Events/Fields/BoolButton.tscn").instantiate() - if p.display_info.has('editor_icon'): - editor_node.icon = callv('get_theme_icon', p.display_info.editor_icon) - else: - editor_node.icon = p.display_info.get('icon', null) - else: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/Bool.tscn").instantiate() - editor_node.tooltip_text = p.display_info.get('tooltip', "") - elif p.dialogic_type == resource.ValueType.FILE: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn").instantiate() - editor_node.file_filter = p.display_info.get('file_filter', '') - editor_node.placeholder = p.display_info.get('placeholder', '') - editor_node.resource_icon = p.display_info.get('icon', null) - if editor_node.resource_icon == null and p.display_info.has('editor_icon'): - editor_node.resource_icon = callv('get_theme_icon', p.display_info.editor_icon) - - elif p.dialogic_type == resource.ValueType.CONDITION: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn").instantiate() - - ## Complex Picker - elif p.dialogic_type == resource.ValueType.COMPLEX_PICKER: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn").instantiate() - - editor_node.file_extension = p.display_info.get('file_extension', '') - editor_node.collapse_when_empty = p.display_info.get('collapse_when_empty', false) - editor_node.get_suggestions_func = p.display_info.get('suggestions_func', editor_node.get_suggestions_func) - editor_node.empty_text = p.display_info.get('empty_text', '') - editor_node.placeholder_text = p.display_info.get('placeholder', 'Select Resource') - editor_node.resource_icon = p.display_info.get('icon', null) - editor_node.enable_pretty_name = p.display_info.get('enable_pretty_name', false) - if editor_node.resource_icon == null and p.display_info.has('editor_icon'): - editor_node.resource_icon = callv('get_theme_icon', p.display_info.editor_icon) - - ## INTEGERS - elif p.dialogic_type == resource.ValueType.INTEGER: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/Number.tscn").instantiate() - editor_node.use_int_mode() - editor_node.max = p.display_info.get('max', 9999) - editor_node.min = p.display_info.get('min', -9999) - elif p.dialogic_type == resource.ValueType.FLOAT: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/Number.tscn").instantiate() - editor_node.use_float_mode() - editor_node.max = p.display_info.get('max', 9999) - editor_node.min = p.display_info.get('min', 0) - elif p.dialogic_type == resource.ValueType.DECIBEL: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/Number.tscn").instantiate() - editor_node.use_decibel_mode() - elif p.dialogic_type == resource.ValueType.FIXED_OPTION_SELECTOR: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/OptionSelector.tscn").instantiate() - editor_node.options = p.display_info.get('selector_options', []) - editor_node.disabled = p.display_info.get('disabled', false) - editor_node.symbol_only = p.display_info.get('symbol_only', false) - - elif p.dialogic_type == resource.ValueType.VECTOR2: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/Vector2.tscn").instantiate() - - elif p.dialogic_type == resource.ValueType.ARRAY: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/Array.tscn").instantiate() - - elif p.dialogic_type == resource.ValueType.KEY_VALUE_PAIRS: - editor_node = load("res://addons/dialogic/Editor/Events/Fields/KeyValuePairs.tscn").instantiate() - - - elif p.dialogic_type == resource.ValueType.LABEL: + elif p.field_type in FIELD_SCENES: + editor_node = load(FIELD_SCENES[p.field_type]).instantiate() + + elif p.field_type == resource.ValueType.LABEL: editor_node = Label.new() editor_node.text = p.display_info.text editor_node.vertical_alignment = VERTICAL_ALIGNMENT_CENTER editor_node.set('custom_colors/font_color', Color("#7b7b7b")) editor_node.add_theme_color_override('font_color', resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.8)) - elif p.dialogic_type == resource.ValueType.BUTTON: + + elif p.field_type == resource.ValueType.BUTTON: editor_node = Button.new() editor_node.text = p.display_info.text if typeof(p.display_info.icon) == TYPE_ARRAY: @@ -249,8 +220,9 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: editor_node.flat = true editor_node.custom_minimum_size.x = 30*DialogicUtil.get_editor_scale() editor_node.pressed.connect(p.display_info.callable) + ## CUSTOM - elif p.dialogic_type == resource.ValueType.CUSTOM: + elif p.field_type == resource.ValueType.CUSTOM: if p.display_info.has('path'): editor_node = load(p.display_info.path).instantiate() @@ -260,26 +232,46 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: editor_node.text = p.name editor_node.add_theme_color_override('font_color', resource.event_color.lerp(get_theme_color("font_color", "Editor"), 0.8)) + + field_list[-1]['node'] = editor_node ### -------------------------------------------------------------------- - ### 2. ADD IT TO THE RIGHT PLACE (HEADER/BODY) + # Some things need to be called BEFORE the field is added to the tree + if editor_node is DialogicVisualEditorField: + editor_node.event_resource = resource + + editor_node.property_name = p.name + field_list[-1]['property'] = p.name + + editor_node._load_display_info(p.display_info) + var location: Control = %HeaderContent if p.location == 1: location = current_body_container location.add_child(editor_node) - ### -------------------------------------------------------------------- - ### 3. FILL THE NEW NODE WITH INFORMATION AND LISTEN TO CHANGES - field_list[-1]['node'] = editor_node - if "event_resource" in editor_node: - editor_node.event_resource = resource - if 'property_name' in editor_node: - editor_node.property_name = p.name - field_list[-1]['property'] = p.name - if editor_node.has_method('set_value'): - editor_node.set_value(resource.get(p.name)) - if editor_node.has_signal('value_changed'): + # Some things need to be called AFTER the field is added to the tree + if editor_node is DialogicVisualEditorField: + # Only set the value if the field is visible + # + # This prevents events with varied value types (event_setting, event_variable) + # from injecting incorrect types into hidden fields, which then throw errors + # in the console. + if p.has('condition') and not p.condition.is_empty(): + if _evaluate_visibility_condition(p): + editor_node._set_value(resource.get(p.name)) + else: + editor_node._set_value(resource.get(p.name)) + editor_node.value_changed.connect(set_property) - editor_node.tooltip_text = p.display_info.get('tooltip', '') + + editor_node.tooltip_text = p.display_info.get('tooltip', '') + + # Apply autofocus + if resource.created_by_button and p.display_info.get('autofocus', false): + editor_node.call_deferred('take_autofocus') + + ### -------------------------------------------------------------------- + ### 4. ADD LEFT AND RIGHT TEXT var left_label: Label = null var right_label: Label = null if !p.get('left_text', '').is_empty(): @@ -297,6 +289,8 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: location.add_child(right_label) location.move_child(right_label, editor_node.get_index()+1) + ### -------------------------------------------------------------------- + ### 5. REGISTER CONDITION if p.has('condition'): field_list[-1]['condition'] = p.condition if left_label: @@ -304,15 +298,11 @@ func build_editor(build_header:bool = true, build_body:bool = false) -> void: if right_label: field_list.append({'node': right_label, 'condition':p.condition, 'location':p.location}) - ### -------------------------------------------------------------------- - ### 4. GETTING THE PATH OF THE FIELD WE WANT TO FOCUS (in case we want) - if resource.created_by_button and p.display_info.get('autofocus', false) and editor_node.has_method('take_autofocus'): - editor_node.call_deferred('take_autofocus') if build_body: if current_body_container.get_child_count() == 0: expanded = false - body_container.visible = false + %Body.visible = false recalculate_field_visibility() @@ -326,9 +316,7 @@ func recalculate_field_visibility() -> void: if p.location == 1: has_any_enabled_body_content = true else: - var expr := Expression.new() - expr.parse(p.condition) - if expr.execute([], resource): + if _evaluate_visibility_condition(p): if p.node != null: p.node.show() if p.location == 1: @@ -336,8 +324,6 @@ func recalculate_field_visibility() -> void: else: if p.node != null: p.node.hide() - if expr.has_execute_failed(): - printerr("[Dialogic] Failed executing visibility condition for '",p.get('property', 'unnamed'),"': " + expr.get_error_text()) %ExpandButton.visible = has_any_enabled_body_content @@ -348,80 +334,60 @@ func set_property(property_name:String, value:Variant) -> void: end_node.parent_node_changed() +func _evaluate_visibility_condition(p: Dictionary) -> bool: + var expr := Expression.new() + expr.parse(p.condition) + var result: bool + if expr.execute([], resource): + result = true + else: + result = false + if expr.has_execute_failed(): + printerr("[Dialogic] Failed executing visibility condition for '",p.get('property', 'unnamed'),"': " + expr.get_error_text()) + return result + + func _on_resource_ui_update_needed() -> void: for node_info in field_list: if node_info.node and node_info.node.has_method('set_value'): - node_info.node.set_value(resource.get(node_info.property)) + # Only set the value if the field is visible + # + # This prevents events with varied value types (event_setting, event_variable) + # from injecting incorrect types into hidden fields, which then throw errors + # in the console. + if node_info.has('condition') and not node_info.condition.is_empty(): + if _evaluate_visibility_condition(node_info): + node_info.node.set_value(resource.get(node_info.property)) + else: + node_info.node.set_value(resource.get(node_info.property)) recalculate_field_visibility() -func _update_color() -> void: - if resource.dialogic_color_name != '': - %IconPanel.self_modulate = DialogicUtil.get_color(resource.dialogic_color_name) - - -######################## OVERRIDES ############################################# +#region SIGNALS ################################################################################ -func _ready(): - resized.connect(get_parent().get_parent().queue_redraw) - - ## DO SOME STYLING - var _scale := DialogicUtil.get_editor_scale() - $PanelContainer.self_modulate = get_theme_color("accent_color", "Editor") - warning.texture = get_theme_icon("NodeWarning", "EditorIcons") - warning.size = Vector2(16 * _scale, 16 * _scale) - warning.position = Vector2(-5 * _scale, -10 * _scale) - - indent_size = indent_size * DialogicUtil.get_editor_scale() - - %ExpandButton.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") - %ExpandButton.modulate = get_theme_color("readonly_color", "Editor") - - if resource: - if resource.event_name: - %TitleLabel.add_theme_color_override("font_color", resource.event_color.lightened(0.4)) - %TitleLabel.add_theme_font_override("font", get_theme_font("title", "EditorFonts")) - %TitleLabel.text = resource.event_name - %IconPanel.tooltip_text = resource.event_name - if resource._get_icon() != null: - _set_event_icon(resource._get_icon()) - resource.ui_update_needed.connect(_on_resource_ui_update_needed) - resource.ui_update_warning.connect(set_warning) - - %IconPanel.self_modulate = resource.event_color - - _on_ExpandButton_toggled(resource.expand_by_default) - - set_focus_mode(1) # Allowing this node to grab focus - - # signals - # TODO godot4 react to changes of the colors, the signal was removed - #ProjectSettings.project_settings_changed.connect(_update_color) - - # Separation on the header - %Header.add_theme_constant_override("custom_constants/separation", 5 * _scale) - - content_changed.connect(recalculate_field_visibility) +func _on_collapse_toggled(toggled:bool) -> void: + collapsed = toggled + var timeline_editor = find_parent('VisualEditor') + if (timeline_editor != null): + # @todo select item and clear selection is marked as "private" in TimelineEditor.gd + # consider to make it "public" or add a public helper function + timeline_editor.indent_events() -# _on_Indent_visibility_changed() - %CollapseButton.toggled.connect(toggle_collapse) - %CollapseButton.icon = get_theme_icon("Collapse", "EditorIcons") - %CollapseButton.hide() - visual_deselect() func _on_ExpandButton_toggled(button_pressed:bool) -> void: if button_pressed and !body_was_build: build_editor(false, true) %ExpandButton.set_pressed_no_signal(button_pressed) + if button_pressed: %ExpandButton.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons") else: %ExpandButton.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons") + expanded = button_pressed - body_container.visible = button_pressed - body_container.add_theme_constant_override("margin_left", icon_size*DialogicUtil.get_editor_scale()) + %Body.visible = button_pressed if find_parent('VisualEditor') != null: find_parent('VisualEditor').indent_events() @@ -440,6 +406,6 @@ func _on_EventNode_gui_input(event:InputEvent) -> void: popup.current_event = self popup.popup_on_parent(Rect2(get_global_mouse_position(),Vector2())) if resource.help_page_path == "": - popup.set_item_disabled(0, true) + popup.set_item_disabled(2, true) else: - popup.set_item_disabled(0, false) + popup.set_item_disabled(2, false) diff --git a/addons/dialogic/Editor/Events/EventBlock/event_block.tscn b/addons/dialogic/Editor/Events/EventBlock/event_block.tscn index 9d0a1cf7..f0b363c0 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_y3447"] +[sub_resource type="Image" id="Image_ng2y4"] 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", @@ -20,8 +20,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_vg181"] -image = SubResource("Image_y3447") +[sub_resource type="ImageTexture" id="ImageTexture_rc1wh"] +image = SubResource("Image_ng2y4") [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ee4ub"] @@ -80,25 +80,13 @@ stretch_mode = 5 unique_name_in_owner = true visible = false layout_mode = 0 -offset_left = -5.0 -offset_top = -10.0 -offset_right = 11.0 -offset_bottom = 6.0 -texture = SubResource("ImageTexture_vg181") +offset_left = -5.5 +offset_top = -11.0 +offset_right = 12.1 +offset_bottom = 6.6 +texture = SubResource("ImageTexture_rc1wh") stretch_mode = 5 -[node name="TitleLabel" type="Label" parent="PanelContainer/VBoxContainer/Header"] -unique_name_in_owner = true -visible = false -layout_mode = 2 -vertical_alignment = 1 - -[node name="VSeparator" type="VSeparator" parent="PanelContainer/VBoxContainer/Header"] -visible = false -custom_minimum_size = Vector2(0, 20) -layout_mode = 2 -size_flags_vertical = 4 - [node name="HeaderContent" type="HBoxContainer" parent="PanelContainer/VBoxContainer/Header"] unique_name_in_owner = true layout_mode = 2 @@ -111,7 +99,7 @@ size_flags_horizontal = 0 tooltip_text = "Fold/Unfold Settings" theme_override_styles/focus = SubResource("StyleBoxEmpty_ee4ub") toggle_mode = true -icon = SubResource("ImageTexture_vg181") +icon = SubResource("ImageTexture_rc1wh") flat = true [node name="CollapseButton" type="Button" parent="PanelContainer/VBoxContainer/Header"] @@ -121,7 +109,7 @@ layout_mode = 2 size_flags_horizontal = 10 tooltip_text = "Collapse Contained Events" toggle_mode = true -icon = SubResource("ImageTexture_vg181") +icon = SubResource("ImageTexture_rc1wh") flat = true [node name="Body" type="MarginContainer" parent="PanelContainer/VBoxContainer"] 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 6b10fa03..a2b42b9e 100644 --- a/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd +++ b/addons/dialogic/Editor/Events/EventBlock/event_right_click_menu.gd @@ -5,6 +5,8 @@ var current_event : Node = null func _ready(): clear() + add_icon_item(get_theme_icon("Duplicate", "EditorIcons"), "Duplicate") + add_separator() add_icon_item(get_theme_icon("Help", "EditorIcons"), "Documentation") add_icon_item(get_theme_icon("CodeHighlighter", "EditorIcons"), "Open Code") add_separator() @@ -12,7 +14,7 @@ func _ready(): add_icon_item(get_theme_icon("ArrowDown", "EditorIcons"), "Move down") add_separator() add_icon_item(get_theme_icon("Remove", "EditorIcons"), "Delete") - + var menu_background := StyleBoxFlat.new() menu_background.bg_color = get_parent().get_theme_color("base_color", "Editor") add_theme_stylebox_override('panel', menu_background) diff --git a/addons/dialogic/Editor/Events/Fields/Array.tscn b/addons/dialogic/Editor/Events/Fields/Array.tscn deleted file mode 100644 index 53ed4b03..00000000 --- a/addons/dialogic/Editor/Events/Fields/Array.tscn +++ /dev/null @@ -1,12 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://btmy7ageqpyq1"] - -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Array.gd" id="2"] - -[node name="Array" type="HFlowContainer"] -size_flags_horizontal = 3 -script = ExtResource("2") - -[node name="Add" type="Button" parent="."] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "Add another value" diff --git a/addons/dialogic/Editor/Events/Fields/Bool.gd b/addons/dialogic/Editor/Events/Fields/Bool.gd deleted file mode 100644 index 8f352f6e..00000000 --- a/addons/dialogic/Editor/Events/Fields/Bool.gd +++ /dev/null @@ -1,19 +0,0 @@ -@tool -extends CheckButton - -## Event block field for boolean values. - -signal value_changed -var property_name : String - - -func _ready() -> void: - toggled.connect(_on_value_changed) - - -func set_value(value:bool) -> void: - button_pressed = value - - -func _on_value_changed(value:bool) -> void: - value_changed.emit(property_name, value) diff --git a/addons/dialogic/Editor/Events/Fields/BoolButton.tscn b/addons/dialogic/Editor/Events/Fields/BoolButton.tscn deleted file mode 100644 index efad7b8d..00000000 --- a/addons/dialogic/Editor/Events/Fields/BoolButton.tscn +++ /dev/null @@ -1,8 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://iypxcctv080u"] - -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/BoolButton.gd" id="1_5iob7"] - -[node name="Bool" type="Button"] -toggle_mode = true -flat = true -script = ExtResource("1_5iob7") diff --git a/addons/dialogic/Editor/Events/Fields/ComplexPicker.gd b/addons/dialogic/Editor/Events/Fields/ComplexPicker.gd deleted file mode 100644 index 25df4dab..00000000 --- a/addons/dialogic/Editor/Events/Fields/ComplexPicker.gd +++ /dev/null @@ -1,262 +0,0 @@ -@tool -extends Control - -## Event block field for resources/options. - -# this signal is on all event parts and informs the event that a change happened. -signal value_changed(property_name, value) -var property_name : String -var event_resource : DialogicEvent = null - -### SETTINGS FOR THE RESOURCE PICKER -@export var placeholder_text : String = "Select Resource" -var collapse_when_empty := false -var file_extension : String = "" -var get_suggestions_func : Callable = get_default_suggestions -var empty_text : String = "" -@export var enable_pretty_name : bool = false -@export var fit_text_length : bool = true -var force_string := false - -var resource_icon : Texture = null: - get: - return resource_icon - set(new_icon): - resource_icon = new_icon - %Icon.texture = new_icon - -## STORING VALUE AND REFERENCE TO RESOURCE -var current_value :Variant # Dynamic - -var current_selected = 0 - -################################################################################ -## BASIC EVENT PART FUNCTIONS -################################################################################ - -func set_value(value:Variant, text : String = '') -> void: - %Search.show() - if value == null or value.is_empty(): - %Search.text = empty_text - if collapse_when_empty: - %Search.hide() - elif file_extension != "" and file_extension != ".dch" and file_extension != ".dtl": - %Search.text = value.resource_path - %Search.tooltip_text = value.resource_path - elif value: - if enable_pretty_name: - %Search.text = DialogicUtil.pretty_name(value) - else: - %Search.text = value - else: - %Search.text = empty_text - if text: - %Search.text = text - - current_value = value - - -func changed_to_empty() -> void: - if file_extension != "" and file_extension != ".dch" and !force_string: - emit_signal("value_changed", property_name, null) - else: - emit_signal("value_changed", property_name, "") - - -################################################################################ -## BASIC -################################################################################ -func _ready(): - %Focus.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) - %Search.text_changed.connect(_on_Search_text_changed) - %Search.text_submitted.connect(_on_Search_text_entered) - %SelectButton.icon = get_theme_icon("Collapse", "EditorIcons") - %Search.placeholder_text = placeholder_text - %Search.expand_to_text_length = fit_text_length - %Suggestions.add_theme_stylebox_override('bg', load("res://addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres")) - %Suggestions.hide() - %Suggestions.item_selected.connect(suggestion_selected) - %Suggestions.item_clicked.connect(suggestion_selected) - if resource_icon == null: - self.resource_icon = null - - - -func _exit_tree(): - # Explicitly free any open cache resources on close, so we don't get leaked resource errors on shutdown - event_resource = null - - -func take_autofocus(): - %Search.grab_focus() - - -func set_enabled(is_enabled: bool) -> void: - %SelectButton.disabled = !is_enabled - -################################################################################ -## SEARCH & SUGGESTION POPUP -################################################################################ -func _on_Search_text_entered(new_text:String) -> void: - if %Suggestions.get_item_count(): - if %Suggestions.is_anything_selected(): - suggestion_selected(%Suggestions.get_selected_items()[0]) - else: - suggestion_selected(0) - else: - changed_to_empty() - - -func _on_Search_text_changed(new_text:String, just_update:bool = false) -> void: - %Suggestions.clear() - - if new_text == "" and !just_update: - changed_to_empty() - else: - %Search.show() - - var suggestions :Dictionary = get_suggestions_func.call(new_text) - - var line_length:int = 0 - var idx:int = 0 - for element in suggestions: - if new_text.is_empty() or new_text.to_lower() in element.to_lower() or new_text.to_lower() in str(suggestions[element].value).to_lower() or new_text.to_lower() in suggestions[element].get('tooltip', '').to_lower(): - line_length = max(get_theme_font('font', 'Label').get_string_size(element, HORIZONTAL_ALIGNMENT_LEFT, -1, get_theme_font_size("font_size", 'Label')).x+80, line_length) - %Suggestions.add_item(element) - if suggestions[element].has('icon'): - %Suggestions.set_item_icon(idx, suggestions[element].icon) - elif suggestions[element].has('editor_icon'): - %Suggestions.set_item_icon(idx, get_theme_icon(suggestions[element].editor_icon[0],suggestions[element].editor_icon[1])) - - %Suggestions.set_item_tooltip(idx, suggestions[element].get('tooltip', '')) - %Suggestions.set_item_metadata(idx, suggestions[element].value) - idx += 1 - - if not %Suggestions.visible: - %Suggestions.show() - %Suggestions.global_position = $PanelContainer.global_position+Vector2(0,1)*$PanelContainer.size.y - #%Suggestions.position = Vector2() - %Suggestions.size.x = max(%Search.size.x, line_length) - %Suggestions.size.y = min(%Suggestions.get_item_count()*35*DialogicUtil.get_editor_scale(), 200*DialogicUtil.get_editor_scale()) - if %Suggestions.get_item_count(): - %Suggestions.select(0) - current_selected = 0 - else: - current_selected = -1 - %Search.grab_focus() - - -func get_default_suggestions(input:String) -> Dictionary: - if file_extension.is_empty(): - return {'Nothing found!':{'value':''}} - - var suggestions: Dictionary = {} - for resource in DialogicResourceUtil.list_resources_of_type(file_extension): - suggestions[resource] = {'value':resource, 'tooltip':resource} - return suggestions - - -func suggestion_selected(index : int, position:=Vector2(), button_index:=MOUSE_BUTTON_LEFT) -> void: - if button_index != MOUSE_BUTTON_LEFT: - return - if %Suggestions.is_item_disabled(index): - return - - %Search.text = %Suggestions.get_item_text(index) - - if %Suggestions.get_item_metadata(index) == null: - current_value = null - - # if this is a resource, then load it instead of assigning the string: - elif file_extension != "" and file_extension != ".dch" and file_extension != ".dtl": - var file = load(%Suggestions.get_item_metadata(index)) - current_value = file - else: - current_value = %Suggestions.get_item_metadata(index) - - hide_suggestions() - - %Search.grab_focus() - emit_signal("value_changed", property_name, current_value) - -func _input(event:InputEvent): - if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: - if %Suggestions.visible: - if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()) and \ - !%SelectButton.get_global_rect().has_point(get_global_mouse_position()): - hide_suggestions() - - -func hide_suggestions() -> void: - %SelectButton.set_pressed_no_signal(false) - %Suggestions.hide() - if !current_value and collapse_when_empty: - %Search.hide() - - -func _on_SelectButton_toggled(button_pressed:bool) -> void: - if button_pressed: - _on_Search_text_changed('', true) - else: - hide_suggestions() - -func _on_focus_entered(): - %Search.grab_focus() - - -func _on_search_gui_input(event): - if event is InputEventKey and (event.keycode == KEY_DOWN or event.keycode == KEY_UP) and event.pressed: - if !%Suggestions.visible: - _on_Search_text_changed('', true) - current_selected = -1 - if event.keycode == KEY_DOWN: - current_selected = wrapi(current_selected+1, 0, %Suggestions.item_count) - if event.keycode == KEY_UP: - current_selected = wrapi(current_selected-1, 0, %Suggestions.item_count) - %Suggestions.select(current_selected) - %Suggestions.ensure_current_is_visible() - - -func _on_search_focus_entered(): - if %Search.text == "" or current_value == null or (typeof(current_value) == TYPE_STRING and current_value.is_empty()): - _on_Search_text_changed("") - %Search.call_deferred('select_all') - %Focus.show() - - -func _on_search_focus_exited(): - %Focus.hide() - if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()): - hide_suggestions() - -################################################################################ -## DRAG AND DROP -################################################################################ - -func _can_drop_data(position, data) -> bool: - if typeof(data) == TYPE_DICTIONARY and data.has('files') and len(data.files) == 1: - if file_extension: - if data.files[0].ends_with(file_extension): - return true - else: - return false - return false - -func _drop_data(position, data) -> void: - if data.files[0].ends_with('.dch'): - var character_directory := DialogicResourceUtil.get_character_directory() - for character in character_directory: - if character_directory[character] == data.files[0]: - set_value(character) - break - elif data.files[0].ends_with('dtl'): - var timeline_directory := DialogicResourceUtil.get_timeline_directory() - for timeline in timeline_directory: - if timeline_directory[timeline] == data.files[0]: - set_value(timeline) - break - else: - var file := load(data.files[0]) - set_value(file) - value_changed.emit(property_name, file) - diff --git a/addons/dialogic/Editor/Events/Fields/Label.gd b/addons/dialogic/Editor/Events/Fields/Label.gd deleted file mode 100644 index b1625f1d..00000000 --- a/addons/dialogic/Editor/Events/Fields/Label.gd +++ /dev/null @@ -1,11 +0,0 @@ -@tool -extends Control - - -@export var text: String = "Hello World" - - -func _ready(): - $Label.text = text - $Label.set('custom_colors/font_color', Color("#7b7b7b")) - diff --git a/addons/dialogic/Editor/Events/Fields/Label.tscn b/addons/dialogic/Editor/Events/Fields/Label.tscn deleted file mode 100644 index 6eae67bc..00000000 --- a/addons/dialogic/Editor/Events/Fields/Label.tscn +++ /dev/null @@ -1,18 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://addons/dialogic/Editor/Events/Fields/Label.gd" type="Script" id=1] - -[node name="Control" type="CenterContainer"] -anchor_right = 1.0 -anchor_bottom = 1.0 -mouse_filter = 1 -script = ExtResource( 1 ) - -[node name="Label" type="Label" parent="."] -margin_left = 474.0 -margin_top = 293.0 -margin_right = 550.0 -margin_bottom = 307.0 -size_flags_horizontal = 3 -custom_colors/font_color = Color( 0.482353, 0.482353, 0.482353, 1 ) -text = "Hello World" diff --git a/addons/dialogic/Editor/Events/Fields/MultilineText.gd b/addons/dialogic/Editor/Events/Fields/MultilineText.gd deleted file mode 100644 index a609d793..00000000 --- a/addons/dialogic/Editor/Events/Fields/MultilineText.gd +++ /dev/null @@ -1,92 +0,0 @@ -@tool -extends CodeEdit - -## Event block field that allows entering multiline text (mainly text event). - -var property_name : String -signal value_changed - -@onready var code_completion_helper :Node= find_parent('EditorsManager').get_node('CodeCompletionHelper') - -var previous_width := 0 -var height_recalculation_queued := false - -func _ready() -> void: - text_changed.connect(_on_text_changed) - syntax_highlighter = code_completion_helper.text_syntax_highlighter - resized.connect(_resized) - - -func _on_text_changed(value := "") -> void: - emit_signal("value_changed", property_name, text) - request_code_completion(true) - queue_height_recalculation() - - -func _resized() -> void: - if previous_width != size.x: - queue_height_recalculation() - previous_width = size.x - - -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. -## 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(text+' ', HORIZONTAL_ALIGNMENT_LEFT, size.x, get_theme_font_size("font_size")) - custom_minimum_size.y = text_size.y+20+4*(floor(text_size.y/get_theme_font_size("font_size"))) - scroll_vertical = 0 - - -func set_value(value:Variant) -> void: - text = str(value) - queue_height_recalculation() - - -func take_autofocus() -> void: - grab_focus() - - -################################################################################ -## AUTO COMPLETION -################################################################################ - -## Called if something was typed -func _request_code_completion(force:bool): - code_completion_helper.request_code_completion(force, self, 0) - - -## Filters the list of all possible options, depending on what was typed -## Purpose of the different Kinds is explained in [_request_code_completion] -func _filter_code_completion_candidates(candidates:Array) -> Array: - return code_completion_helper.filter_code_completion_candidates(candidates, self) - - -## Called when code completion was activated -## Inserts the selected item -func _confirm_code_completion(replace:bool) -> void: - code_completion_helper.confirm_code_completion(replace, self) - - -################################################################################ -## SYMBOL CLICKING -################################################################################ - -## Performs an action (like opening a link) when a valid symbol was clicked -func _on_symbol_lookup(symbol, line, column): - code_completion_helper.symbol_lookup(symbol, line, column) - - -## Called to test if a symbol can be clicked -func _on_symbol_validate(symbol:String) -> void: - code_completion_helper.symbol_validate(symbol, self) diff --git a/addons/dialogic/Editor/Events/Fields/Number.gd b/addons/dialogic/Editor/Events/Fields/Number.gd deleted file mode 100644 index a24077b5..00000000 --- a/addons/dialogic/Editor/Events/Fields/Number.gd +++ /dev/null @@ -1,77 +0,0 @@ -@tool -extends Control - -## Event block field for integers and floats. Improved version of the native spinbox. - -signal value_changed -var property_name : String - -@export var allow_string :bool = false -@export var step:float = 0.1 -@export var enforce_step:bool = true -@export var min:float = 0 -@export var max:float= 999 -@export var value = 0 -@export var suffix := "" - -func _ready() -> void: - if $Value.text.is_empty(): - set_value(value) - $Spin.icon = get_theme_icon("updown", "SpinBox") - - -func set_value(new_value) -> void: - _on_value_text_submitted(str(new_value), true) - $Value.tooltip_text = tooltip_text - - -func get_value() -> float: - return value - - -func use_float_mode() -> void: - step = 0.1 - suffix = "" - enforce_step = false - - -func use_int_mode() -> void: - step = 1 - suffix = "" - - -func use_decibel_mode() -> void: - max = 6 - suffix = "dB" - min = -80 - - -func _on_spin_gui_input(event:InputEvent) -> void: - if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: - if event.position.y < size.y/2.0: - _on_value_text_submitted(str(value+step)) - else: - _on_value_text_submitted(str(value-step)) - - -func _on_value_text_submitted(new_text:String, no_signal:= false) -> void: - new_text = new_text.trim_suffix(suffix) - if new_text.is_valid_float(): - var temp: float = min(max(new_text.to_float(), min), max) - if !enforce_step or is_equal_approx(temp/step, round(temp/step)): - value = temp - else: - value = snapped(temp, step) - elif allow_string: - value = new_text - $Value.text = str(value)+suffix - if not no_signal: - value_changed.emit(property_name, value) - - -func _on_value_focus_exited() -> void: - _on_value_text_submitted($Value.text) - - -func take_autofocus(): - $Value.grab_focus() diff --git a/addons/dialogic/Editor/Events/Fields/Number.tscn b/addons/dialogic/Editor/Events/Fields/Number.tscn deleted file mode 100644 index e41eceb8..00000000 --- a/addons/dialogic/Editor/Events/Fields/Number.tscn +++ /dev/null @@ -1,45 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://kdpp3mibml33"] - -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Number.gd" id="1"] - -[sub_resource type="Image" id="Image_yitwe"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 76, 255, 255, 255, 75, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 99, 255, 255, 255, 191, 255, 255, 255, 191, 255, 255, 255, 99, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 255, 255, 255, 122, 255, 255, 255, 191, 255, 255, 255, 188, 255, 255, 255, 188, 255, 255, 255, 191, 255, 255, 255, 121, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 12, 255, 255, 255, 142, 255, 255, 255, 191, 255, 255, 255, 181, 255, 255, 255, 53, 255, 255, 255, 54, 255, 255, 255, 181, 255, 255, 255, 191, 255, 255, 255, 142, 255, 255, 255, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 71, 255, 255, 255, 191, 255, 255, 255, 171, 255, 255, 255, 36, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 36, 255, 255, 255, 171, 255, 255, 255, 191, 255, 255, 255, 71, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 86, 255, 255, 255, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 22, 255, 255, 255, 86, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 86, 255, 255, 255, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 22, 255, 255, 255, 86, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 71, 255, 255, 255, 191, 255, 255, 255, 171, 255, 255, 255, 36, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 36, 255, 255, 255, 171, 255, 255, 255, 191, 255, 255, 255, 71, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 12, 255, 255, 255, 142, 255, 255, 255, 191, 255, 255, 255, 181, 255, 255, 255, 54, 255, 255, 255, 54, 255, 255, 255, 182, 255, 255, 255, 191, 255, 255, 255, 142, 255, 255, 255, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 255, 255, 255, 121, 255, 255, 255, 191, 255, 255, 255, 188, 255, 255, 255, 188, 255, 255, 255, 191, 255, 255, 255, 121, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 98, 255, 255, 255, 191, 255, 255, 255, 191, 255, 255, 255, 98, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 75, 255, 255, 255, 75, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_uwu06"] -image = SubResource("Image_yitwe") - -[node name="NumberValue" type="HBoxContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_right = -1036.0 -offset_bottom = -615.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/separation = 0 -script = ExtResource("1") -value = 0.0 - -[node name="Value" type="LineEdit" parent="."] -layout_mode = 2 -theme_type_variation = &"DialogicEventEdit" -theme_override_constants/minimum_character_width = 0 -text = "0" -expand_to_text_length = true - -[node name="Spin" type="Button" parent="."] -layout_mode = 2 -size_flags_vertical = 4 -focus_mode = 0 -icon = SubResource("ImageTexture_uwu06") -flat = true - -[connection signal="focus_exited" from="Value" to="." method="_on_value_focus_exited"] -[connection signal="text_submitted" from="Value" to="." method="_on_value_text_submitted"] -[connection signal="gui_input" from="Spin" to="." method="_on_spin_gui_input"] diff --git a/addons/dialogic/Editor/Events/Fields/OptionSelector.gd b/addons/dialogic/Editor/Events/Fields/OptionSelector.gd deleted file mode 100644 index 3f219553..00000000 --- a/addons/dialogic/Editor/Events/Fields/OptionSelector.gd +++ /dev/null @@ -1,63 +0,0 @@ -@tool -extends MenuButton - -## Event block field for constant options. For varying options use ComplexPicker. - -signal value_changed -var property_name : String - -var options : Array = [] - -## if true, only the symbol will be displayed. In the dropdown text will be visible. -## Useful for making UI simpler -var symbol_only := false: - set(value): - symbol_only = value - if value: text = "" - -var current_value :Variant = -1 - -func _ready() -> void: -# add_theme_stylebox_override("normal", get_theme_stylebox("normal", "LineEdit")) -# add_theme_stylebox_override("hover", get_theme_stylebox("normal", "LineEdit")) - -# add_theme_stylebox_override("focus", get_theme_stylebox("focus", "LineEdit")) -# add_theme_stylebox_override("disabled", get_theme_stylebox("normal", "LineEdit")) - add_theme_color_override("font_disabled_color", get_theme_color("font_color", "MenuButton")) - about_to_popup.connect(insert_options) - get_popup().index_pressed.connect(index_pressed) - - -func set_value(value) -> void: - for option in options: - if option['value'] == value: - if typeof(option.get('icon')) == TYPE_ARRAY: - option.icon = callv('get_theme_icon', option.get('icon')) - if !symbol_only: - text = option['label'] - icon = option.get('icon', null) - current_value = value - - -func get_value() -> Variant: - return current_value - - -func insert_options() -> void: - get_popup().clear() - - var idx := 0 - for option in options: - if typeof(option.get('icon')) == TYPE_ARRAY: - option.icon = callv('get_theme_icon', option.get('icon')) - get_popup().add_icon_item(option.get('icon', null), option['label']) - get_popup().set_item_metadata(idx, option['value']) - idx += 1 - - -func index_pressed(idx:int) -> void: - current_value = idx - if !symbol_only: - text = get_popup().get_item_text(idx) - icon = get_popup().get_item_icon(idx) - value_changed.emit(property_name, get_popup().get_item_metadata(idx)) diff --git a/addons/dialogic/Editor/Events/Fields/SinglelineText.gd b/addons/dialogic/Editor/Events/Fields/SinglelineText.gd deleted file mode 100644 index 0dddfdf4..00000000 --- a/addons/dialogic/Editor/Events/Fields/SinglelineText.gd +++ /dev/null @@ -1,29 +0,0 @@ -@tool -extends LineEdit - -## Event block field for a single line of text. - -signal value_changed -var property_name : String - -var placeholder :String= "": - set(value): - placeholder = value - placeholder_text = placeholder - - -func _ready() -> void: - text_changed.connect(_on_text_changed) -# add_theme_stylebox_override('normal', get_theme_stylebox('normal', 'LineEdit')) -# add_theme_stylebox_override('focus', get_theme_stylebox('focus', 'LineEdit')) - - -func _on_text_changed(value := "") -> void: - value_changed.emit(property_name, text) - - -func set_value(value:String) -> void: - text = str(value) - -func take_autofocus(): - grab_focus() diff --git a/addons/dialogic/Editor/Events/Fields/Vector2.gd b/addons/dialogic/Editor/Events/Fields/Vector2.gd deleted file mode 100644 index 3c02d2e0..00000000 --- a/addons/dialogic/Editor/Events/Fields/Vector2.gd +++ /dev/null @@ -1,26 +0,0 @@ -@tool -extends Control - -## Event block field for a vector. - -signal value_changed -var property_name : String - -var current_value := Vector2() - -func _ready() -> void: - $X.value_changed.connect(_on_value_changed) - $Y.value_changed.connect(_on_value_changed) - - -func _on_value_changed(property:String, value:float) -> void: - current_value = Vector2($X.value, $Y.value) - emit_signal("value_changed", property_name, current_value) - - -func set_value(value:Vector2) -> void: - $X.tooltip_text = tooltip_text - $Y.tooltip_text = tooltip_text - $X.set_value(value.x) - $Y.set_value(value.y) - current_value = value diff --git a/addons/dialogic/Editor/Events/Fields/Vector2.tscn b/addons/dialogic/Editor/Events/Fields/Vector2.tscn deleted file mode 100644 index 6e55d69f..00000000 --- a/addons/dialogic/Editor/Events/Fields/Vector2.tscn +++ /dev/null @@ -1,24 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://dtimnsj014cu"] - -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Vector2.gd" id="1_288li"] -[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/Number.tscn" id="3_l3bum"] - -[node name="Vector2" type="HBoxContainer"] -offset_right = 40.0 -offset_bottom = 40.0 -theme_override_constants/separation = -7 -script = ExtResource("1_288li") - -[node name="X" parent="." instance=ExtResource("3_l3bum")] -layout_mode = 2 -step = 1.0 -min = -9999.0 -max = 9999.0 -suffix = "x" - -[node name="Y" parent="." instance=ExtResource("3_l3bum")] -layout_mode = 2 -step = 1.0 -min = -9999.0 -max = 9999.0 -suffix = "y" diff --git a/addons/dialogic/Editor/Events/Fields/ArrayValue.gd b/addons/dialogic/Editor/Events/Fields/array_part.gd similarity index 99% rename from addons/dialogic/Editor/Events/Fields/ArrayValue.gd rename to addons/dialogic/Editor/Events/Fields/array_part.gd index 6fb2f58d..97352d8a 100644 --- a/addons/dialogic/Editor/Events/Fields/ArrayValue.gd +++ b/addons/dialogic/Editor/Events/Fields/array_part.gd @@ -115,7 +115,7 @@ func change_field_type(type:int) -> void: 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/Number.tscn").instantiate() + value_field = load("res://addons/dialogic/Editor/Events/Fields/field_number.tscn").instantiate() if type == TYPE_FLOAT: value_field.use_float_mode() else: diff --git a/addons/dialogic/Editor/Events/Fields/ArrayValue.tscn b/addons/dialogic/Editor/Events/Fields/array_part.tscn similarity index 98% rename from addons/dialogic/Editor/Events/Fields/ArrayValue.tscn rename to addons/dialogic/Editor/Events/Fields/array_part.tscn index ca2a34e2..72ae7ebd 100644 --- a/addons/dialogic/Editor/Events/Fields/ArrayValue.tscn +++ b/addons/dialogic/Editor/Events/Fields/array_part.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=7 format=3 uid="uid://ch4j2lesn1sis"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/ArrayValue.gd" id="1"] +[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/OptionSelector.tscn" id="3_otpho"] +[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 diff --git a/addons/dialogic/Editor/Events/Fields/KeyValuePairValue.gd b/addons/dialogic/Editor/Events/Fields/dictionary_part.gd similarity index 100% rename from addons/dialogic/Editor/Events/Fields/KeyValuePairValue.gd rename to addons/dialogic/Editor/Events/Fields/dictionary_part.gd diff --git a/addons/dialogic/Editor/Events/Fields/KeyValuePairValue.tscn b/addons/dialogic/Editor/Events/Fields/dictionary_part.tscn similarity index 99% rename from addons/dialogic/Editor/Events/Fields/KeyValuePairValue.tscn rename to addons/dialogic/Editor/Events/Fields/dictionary_part.tscn index 17287fd0..aa012bb6 100644 --- a/addons/dialogic/Editor/Events/Fields/KeyValuePairValue.tscn +++ b/addons/dialogic/Editor/Events/Fields/dictionary_part.tscn @@ -1,7 +1,7 @@ [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/KeyValuePairValue.gd" id="2_q88pg"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/dictionary_part.gd" id="2_q88pg"] [sub_resource type="Image" id="Image_esvau"] data = { diff --git a/addons/dialogic/Editor/Events/Fields/Array.gd b/addons/dialogic/Editor/Events/Fields/field_array.gd similarity index 79% rename from addons/dialogic/Editor/Events/Fields/Array.gd rename to addons/dialogic/Editor/Events/Fields/field_array.gd index 08a1d43b..6e0d59c5 100644 --- a/addons/dialogic/Editor/Events/Fields/Array.gd +++ b/addons/dialogic/Editor/Events/Fields/field_array.gd @@ -1,25 +1,25 @@ @tool -extends HFlowContainer +extends DialogicVisualEditorField ## Event block field for editing arrays. -signal value_changed -var property_name : String -const ArrayValue := "res://addons/dialogic/Editor/Events/Fields/ArrayValue.tscn" +const ArrayValue := "res://addons/dialogic/Editor/Events/Fields/array_part.tscn" + func _ready(): %Add.icon = get_theme_icon("Add", "EditorIcons") %Add.pressed.connect(_on_AddButton_pressed) -func set_value(value:Array) -> void: + +func _set_value(value:Variant) -> void: + value = value as Array for child in get_children(): if child != %Add: child.queue_free() - for item in value: - var x :Node= load(ArrayValue).instantiate() + var x: Node = load(ArrayValue).instantiate() add_child(x) x.set_value(item) x.value_changed.connect(recalculate_values) @@ -27,7 +27,7 @@ func set_value(value:Array) -> void: func _on_value_changed(value:Variant) -> void: - emit_signal("value_changed", property_name, value) + value_changed.emit(property_name, value) func recalculate_values() -> void: diff --git a/addons/dialogic/Editor/Events/Fields/field_array.tscn b/addons/dialogic/Editor/Events/Fields/field_array.tscn new file mode 100644 index 00000000..7cffed15 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_array.tscn @@ -0,0 +1,27 @@ +[gd_scene load_steps=4 format=3 uid="uid://btmy7ageqpyq1"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_array.gd" id="2"] + +[sub_resource type="Image" id="Image_u0aqk"] +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_7iwuk"] +image = SubResource("Image_u0aqk") + +[node name="Field_Array" type="HFlowContainer"] +offset_right = 329.0 +offset_bottom = 256.0 +size_flags_horizontal = 3 +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") diff --git a/addons/dialogic/Editor/Events/Fields/BoolButton.gd b/addons/dialogic/Editor/Events/Fields/field_bool_button.gd similarity index 52% rename from addons/dialogic/Editor/Events/Fields/BoolButton.gd rename to addons/dialogic/Editor/Events/Fields/field_bool_button.gd index dd9bf242..b4373e79 100644 --- a/addons/dialogic/Editor/Events/Fields/BoolButton.gd +++ b/addons/dialogic/Editor/Events/Fields/field_bool_button.gd @@ -1,11 +1,10 @@ @tool -extends Button +extends DialogicVisualEditorField ## Event block field for boolean values. -signal value_changed -var property_name : String - +#region MAIN METHODS +################################################################################ func _ready() -> void: add_theme_color_override("icon_normal_color", get_theme_color("disabled_font_color", "Editor")) @@ -13,12 +12,25 @@ func _ready() -> void: add_theme_color_override("icon_pressed_color", get_theme_color("icon_saturation", "Editor")) add_theme_color_override("icon_hover_pressed_color", get_theme_color("warning_color", "Editor")) add_theme_color_override("icon_focus_color", get_theme_color("disabled_font_color", "Editor")) - toggled.connect(_on_value_changed) + self.toggled.connect(_on_value_changed) + + +func _load_display_info(info:Dictionary) -> void: + if info.has('editor_icon'): + self.icon = callv('get_theme_icon', info.editor_icon) + else: + self.icon = info.get('icon', null) + + +func _set_value(value:Variant) -> void: + self.button_pressed = true if value else false +#endregion -func set_value(value:bool) -> void: - button_pressed = value +#region SIGNAL METHODS +################################################################################ func _on_value_changed(value:bool) -> void: value_changed.emit(property_name, value) +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/field_bool_button.tscn b/addons/dialogic/Editor/Events/Fields/field_bool_button.tscn new file mode 100644 index 00000000..e4f7d0ad --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_bool_button.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://iypxcctv080u"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_bool_button.gd" id="1_t1n1f"] + +[node name="Field_BoolButton" type="Button"] +theme_override_colors/icon_normal_color = Color(0, 0, 0, 1) +theme_override_colors/icon_pressed_color = Color(0, 0, 0, 1) +theme_override_colors/icon_hover_color = Color(0, 0, 0, 1) +theme_override_colors/icon_hover_pressed_color = Color(0, 0, 0, 1) +theme_override_colors/icon_focus_color = Color(0, 0, 0, 1) +toggle_mode = true +flat = true +script = ExtResource("1_t1n1f") diff --git a/addons/dialogic/Editor/Events/Fields/field_bool_check.gd b/addons/dialogic/Editor/Events/Fields/field_bool_check.gd new file mode 100644 index 00000000..d01a1163 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_bool_check.gd @@ -0,0 +1,30 @@ +@tool +extends DialogicVisualEditorField + +## Event block field for boolean values. + +#region MAIN METHODS +################################################################################ +func _ready() -> void: + self.toggled.connect(_on_value_changed) + + +func _load_display_info(info:Dictionary) -> void: + pass + + +func _set_value(value:Variant) -> void: + match DialogicUtil.get_variable_value_type(value): + DialogicUtil.VarTypes.STRING: + self.button_pressed = value and not value.strip_edges() == "false" + _: + self.button_pressed = value and true +#endregion + + +#region SIGNAL METHODS +################################################################################ +func _on_value_changed(value:bool) -> void: + value_changed.emit(property_name, value) + +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/Bool.tscn b/addons/dialogic/Editor/Events/Fields/field_bool_check.tscn similarity index 56% rename from addons/dialogic/Editor/Events/Fields/Bool.tscn rename to addons/dialogic/Editor/Events/Fields/field_bool_check.tscn index c995c1c4..395caf50 100644 --- a/addons/dialogic/Editor/Events/Fields/Bool.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_bool_check.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=2 format=3 uid="uid://dm5hxmhyyxgq"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/Bool.gd" id="1"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_bool_check.gd" id="1_ckmtx"] -[node name="Bool" type="CheckButton"] +[node name="Field_BoolCheck" type="CheckButton"] offset_right = 44.0 offset_bottom = 24.0 -script = ExtResource("1") +script = ExtResource("1_ckmtx") diff --git a/addons/dialogic/Editor/Events/Fields/ConditionPicker.gd b/addons/dialogic/Editor/Events/Fields/field_condition.gd similarity index 62% rename from addons/dialogic/Editor/Events/Fields/ConditionPicker.gd rename to addons/dialogic/Editor/Events/Fields/field_condition.gd index 9f989a68..d7a149fb 100644 --- a/addons/dialogic/Editor/Events/Fields/ConditionPicker.gd +++ b/addons/dialogic/Editor/Events/Fields/field_condition.gd @@ -1,17 +1,32 @@ @tool -extends Control +extends DialogicVisualEditorField -## Event block field for displaying conditions in either a simple or complex way. - -signal value_changed -var property_name : String -var event_resource : DialogicEvent = null +## Event block field for displaying conditions in either a simple or complex way. var _current_value1 :Variant = "" var _current_value2 :Variant = "" +#region MAIN METHODS +################################################################################ + +func _set_value(value:Variant) -> void: + var too_complex := is_too_complex(value) + %ToggleComplex.disabled = too_complex + %ToggleComplex.button_pressed = too_complex + %ComplexEditor.visible = too_complex + %SimpleEditor.visible = !too_complex + %ComplexEditor.text = value + if not too_complex: + load_simple_editor(value) + + + +func _autofocus(): + %Value1Variable.grab_focus() + +#endregion + func _ready() -> void: - for i in [%Value1Type, %Value2Type]: i.options = [{ 'label': 'String', @@ -25,56 +40,45 @@ func _ready() -> void: 'label': 'Variable', 'icon': load("res://addons/dialogic/Editor/Images/Pieces/variable.svg"), 'value': 2 + },{ + 'label': 'Bool', + 'icon': ["bool", "EditorIcons"], + 'value': 3 },{ 'label': 'Expression', 'icon': ["Variant", "EditorIcons"], - 'value': 3 - } -# ,{ -# 'label': 'Random Number', -# 'icon': ["RandomNumberGenerator", "EditorIcons"], -# 'value': 4 -# } - ] + 'value': 4 + }] i.symbol_only = true i.value_changed.connect(value_type_changed.bind(i.name)) i.value_changed.connect(something_changed) i.tooltip_text = "Change type" - - + + for i in [%Value1Variable, %Value2Variable]: i.get_suggestions_func = get_variable_suggestions i.value_changed.connect(something_changed) - + %Value1Number.value_changed.connect(something_changed) %Value2Number.value_changed.connect(something_changed) %Value1Text.value_changed.connect(something_changed) %Value2Text.value_changed.connect(something_changed) - + %Value1Bool.value_changed.connect(something_changed) + %Value2Bool.value_changed.connect(something_changed) + %ToggleComplex.icon = get_theme_icon("Enum", "EditorIcons") - + %Operator.value_changed.connect(something_changed) %Operator.options = [ - {'label': '==', 'value': '=='}, - {'label': '>', 'value': '>'}, - {'label': '<', 'value': '<'}, - {'label': '<=', 'value': '<='}, - {'label': '>=', 'value': '>='}, + {'label': '==', 'value': '=='}, + {'label': '>', 'value': '>'}, + {'label': '<', 'value': '<'}, + {'label': '<=', 'value': '<='}, + {'label': '>=', 'value': '>='}, {'label': '!=', 'value': '!='} ] -func set_value(value:String) -> void: - var too_complex := is_too_complex(value) - %ToggleComplex.disabled = too_complex - %ToggleComplex.button_pressed = too_complex - %ComplexEditor.visible = too_complex - %SimpleEditor.visible = !too_complex - %ComplexEditor.text = value - if not too_complex: - load_simple_editor(value) - - func load_simple_editor(condition_string:String) -> void: var data := complex2simple(condition_string) %Value1Type.set_value(get_value_type(data[0], 2)) @@ -91,6 +95,7 @@ func value_type_changed(property:String, value_type:int, value_name:String) -> v get_node('%'+value_name+'Variable').hide() get_node('%'+value_name+'Text').hide() get_node('%'+value_name+'Number').hide() + get_node('%'+value_name+'Bool').hide() var current_val :Variant = "" if '1' in value_name: current_val = _current_value1 @@ -107,9 +112,11 @@ func value_type_changed(property:String, value_type:int, value_name:String) -> v get_node('%'+value_name+'Variable').show() get_node('%'+value_name+'Variable').set_value(trim_value(current_val, value_type)) 3: + get_node('%'+value_name+'Bool').show() + get_node('%'+value_name+'Bool').set_value(trim_value(current_val, value_type)) + 4: get_node('%'+value_name+'Text').show() get_node('%'+value_name+'Text').set_value(str(current_val)) - func get_value_type(value:String, default:int) -> int: @@ -118,13 +125,15 @@ func get_value_type(value:String, default:int) -> int: return 0 elif value.begins_with('{') and value.ends_with('}') and value.count('{') == 1: return 2 + elif value == "true" or value == "false": + return 3 else: if value.is_empty(): return default if value.is_valid_float(): return 1 else: - return 3 + return 4 func prep_value(value:Variant, value_type:int) -> String: @@ -142,57 +151,65 @@ func trim_value(value:Variant, value_type:int) -> String: match value_type: 0: return value.trim_prefix('"').trim_suffix('"').replace('\\"', '"') 2: return value.trim_prefix('{').trim_suffix('}') + 3: + if value == "true" or (value and (typeof(value) != TYPE_STRING or value != "false")): + return "true" + else: + return "false" _: return value func something_changed(fake_arg1=null, fake_arg2 = null): if %ComplexEditor.visible: value_changed.emit(property_name, %ComplexEditor.text) - - else: - match %Value1Type.current_value: - 0: _current_value1 = prep_value(%Value1Text.text, %Value1Type.current_value) - 1: _current_value1 = str(%Value1Number.get_value()) - 2: _current_value1 = prep_value(%Value1Variable.current_value, %Value1Type.current_value) - _: _current_value1 = prep_value(%Value1Text.text, %Value1Type.current_value) - - match %Value2Type.current_value: - 0: _current_value2 = prep_value(%Value2Text.text, %Value2Type.current_value) - 1: _current_value2 = str(%Value2Number.get_value()) - 2: _current_value2 = prep_value(%Value2Variable.current_value, %Value2Type.current_value) - _: _current_value2 = prep_value(%Value2Text.text, %Value2Type.current_value) - - if event_resource: - if not %Operator.text in ['==', '!='] and get_value_type(_current_value2, 0) == 0: - event_resource.ui_update_warning.emit("This operator doesn't work with strings.") - else: - event_resource.ui_update_warning.emit("") - - value_changed.emit(property_name, get_simple_condition()) + return + + + match %Value1Type.current_value: + 0: _current_value1 = prep_value(%Value1Text.text, %Value1Type.current_value) + 1: _current_value1 = str(%Value1Number.get_value()) + 2: _current_value1 = prep_value(%Value1Variable.current_value, %Value1Type.current_value) + 3: _current_value1 = prep_value(%Value1Bool.button_pressed, %Value1Type.current_value) + _: _current_value1 = prep_value(%Value1Text.text, %Value1Type.current_value) + + match %Value2Type.current_value: + 0: _current_value2 = prep_value(%Value2Text.text, %Value2Type.current_value) + 1: _current_value2 = str(%Value2Number.get_value()) + 2: _current_value2 = prep_value(%Value2Variable.current_value, %Value2Type.current_value) + 3: _current_value2 = prep_value(%Value2Bool.button_pressed, %Value2Type.current_value) + _: _current_value2 = prep_value(%Value2Text.text, %Value2Type.current_value) + + if event_resource: + if not %Operator.text in ['==', '!='] and get_value_type(_current_value2, 0) in [0, 3]: + event_resource.ui_update_warning.emit("This operator doesn't work with strings and booleans.") + else: + event_resource.ui_update_warning.emit("") + + value_changed.emit(property_name, get_simple_condition()) func is_too_complex(condition:String) -> bool: - return !(condition.is_empty() - or ' and ' in condition - or ' or ' in condition - or ' not ' in condition - or condition.count('==') != 1 - or condition.count('>') != 1 - or condition.count('<') != 1 - or condition.count('<=') != 1 - or condition.count('>=') != 1 - or condition.count('!=') != 1) + if condition.strip_edges().is_empty(): + return false + + var comparison_count: int = 0 + for i in ['==', '!=', '<=', '<', '>', '>=']: + comparison_count += condition.count(i) + if comparison_count == 1: + return false + + return true ## Combines the info from the simple editor fields into a string condition func get_simple_condition() -> String: return _current_value1 +" "+ %Operator.text +" "+ _current_value2 - + func complex2simple(condition:String) -> Array: if is_too_complex(condition) or condition.strip_edges().is_empty(): return ['', '==',''] - + for i in ['==', '!=', '<=', '<', '>', '>=']: if i in condition: var cond_split := Array(condition.split(i, false)) @@ -225,3 +242,26 @@ func get_variable_suggestions(filter:String) -> Dictionary: suggestions[var_path] = {'value':var_path, 'editor_icon':["ClassList", "EditorIcons"]} return suggestions + +func _on_value_1_variable_value_changed(property_name: Variant, value: Variant) -> void: + var type := DialogicUtil.get_variable_type(value) + match type: + DialogicUtil.VarTypes.BOOL: + if not %Operator.text in ["==", "!="]: + %Operator.text = "==" + if get_value_type(_current_value2, 3) in [0, 1]: + %Value2Type.insert_options() + %Value2Type.index_pressed(3) + DialogicUtil.VarTypes.STRING: + if not %Operator.text in ["==", "!="]: + %Operator.text = "==" + if get_value_type(_current_value2, 0) in [1, 3]: + %Value2Type.insert_options() + %Value2Type.index_pressed(0) + DialogicUtil.VarTypes.FLOAT, DialogicUtil.VarTypes.INT: + if get_value_type(_current_value2, 1) in [0,3]: + %Value2Type.insert_options() + %Value2Type.index_pressed(1) + + something_changed() + diff --git a/addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn b/addons/dialogic/Editor/Events/Fields/field_condition.tscn similarity index 83% rename from addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn rename to addons/dialogic/Editor/Events/Fields/field_condition.tscn index da4a21d5..ea515f7f 100644 --- a/addons/dialogic/Editor/Events/Fields/ConditionPicker.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_condition.tscn @@ -1,12 +1,13 @@ -[gd_scene load_steps=8 format=3 uid="uid://ir6334lqtuwt"] +[gd_scene load_steps=9 format=3 uid="uid://ir6334lqtuwt"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/ConditionPicker.gd" id="1_q5p62"] -[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="1_rr7mq"] -[ext_resource type="PackedScene" uid="uid://d3bhehatwoio" path="res://addons/dialogic/Editor/Events/Fields/OptionSelector.tscn" id="4_27ir8"] -[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/Number.tscn" id="4_al48y"] -[ext_resource type="PackedScene" uid="uid://c0vkcehgjsjy" path="res://addons/dialogic/Editor/Events/Fields/SinglelineText.tscn" id="4_b5vlr"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_condition.gd" id="1_owjj0"] +[ext_resource type="PackedScene" uid="uid://d3bhehatwoio" path="res://addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn" id="2_f6v80"] +[ext_resource type="PackedScene" uid="uid://c0vkcehgjsjy" path="res://addons/dialogic/Editor/Events/Fields/field_text_singleline.tscn" id="3_3kfwc"] +[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/field_number.tscn" id="4_6q3a6"] +[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_tmsys"] +[sub_resource type="Image" id="Image_cgfp5"] 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,60 +16,68 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_wps7w"] -image = SubResource("Image_tmsys") +[sub_resource type="ImageTexture" id="ImageTexture_4jujf"] +image = SubResource("Image_cgfp5") -[node name="ConditionPicker" type="HBoxContainer"] +[node name="Field_Condition" type="HBoxContainer"] offset_right = 77.0 offset_bottom = 31.0 -script = ExtResource("1_q5p62") +script = ExtResource("1_owjj0") [node name="SimpleEditor" type="HBoxContainer" parent="."] unique_name_in_owner = true layout_mode = 2 -[node name="Value1Type" parent="SimpleEditor" instance=ExtResource("4_27ir8")] +[node name="Value1Type" parent="SimpleEditor" instance=ExtResource("2_f6v80")] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Change type" text = "" -[node name="Value1Text" parent="SimpleEditor" instance=ExtResource("4_b5vlr")] +[node name="Value1Text" parent="SimpleEditor" instance=ExtResource("3_3kfwc")] unique_name_in_owner = true layout_mode = 2 -[node name="Value1Number" parent="SimpleEditor" instance=ExtResource("4_al48y")] +[node name="Value1Number" parent="SimpleEditor" instance=ExtResource("4_6q3a6")] unique_name_in_owner = true layout_mode = 2 -[node name="Value1Variable" parent="SimpleEditor" instance=ExtResource("1_rr7mq")] +[node name="Value1Bool" parent="SimpleEditor" instance=ExtResource("5_1x02a")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Value1Variable" parent="SimpleEditor" instance=ExtResource("6_5a2xd")] unique_name_in_owner = true layout_mode = 2 placeholder_text = "Variable" -[node name="Operator" parent="SimpleEditor" instance=ExtResource("4_27ir8")] +[node name="Operator" parent="SimpleEditor" instance=ExtResource("2_f6v80")] unique_name_in_owner = true layout_mode = 2 -[node name="Value2Type" parent="SimpleEditor" instance=ExtResource("4_27ir8")] +[node name="Value2Type" parent="SimpleEditor" instance=ExtResource("2_f6v80")] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Change type" text = "" -[node name="Value2Text" parent="SimpleEditor" instance=ExtResource("4_b5vlr")] +[node name="Value2Text" parent="SimpleEditor" instance=ExtResource("3_3kfwc")] unique_name_in_owner = true layout_mode = 2 -[node name="Value2Number" parent="SimpleEditor" instance=ExtResource("4_al48y")] +[node name="Value2Number" parent="SimpleEditor" instance=ExtResource("4_6q3a6")] unique_name_in_owner = true layout_mode = 2 -[node name="Value2Variable" parent="SimpleEditor" instance=ExtResource("1_rr7mq")] +[node name="Value2Variable" parent="SimpleEditor" instance=ExtResource("6_5a2xd")] unique_name_in_owner = true layout_mode = 2 placeholder_text = "Variable" +[node name="Value2Bool" parent="SimpleEditor" instance=ExtResource("5_1x02a")] +unique_name_in_owner = true +layout_mode = 2 + [node name="ComplexEditor" type="LineEdit" parent="."] unique_name_in_owner = true visible = false @@ -85,7 +94,8 @@ unique_name_in_owner = true layout_mode = 2 tooltip_text = "Use complex expression" toggle_mode = true -icon = SubResource("ImageTexture_wps7w") +icon = SubResource("ImageTexture_4jujf") +[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"] [connection signal="toggled" from="ToggleComplex" to="." method="_on_toggle_complex_toggled"] diff --git a/addons/dialogic/Editor/Events/Fields/KeyValuePairs.gd b/addons/dialogic/Editor/Events/Fields/field_dictionary.gd similarity index 74% rename from addons/dialogic/Editor/Events/Fields/KeyValuePairs.gd rename to addons/dialogic/Editor/Events/Fields/field_dictionary.gd index e209c848..194f130a 100644 --- a/addons/dialogic/Editor/Events/Fields/KeyValuePairs.gd +++ b/addons/dialogic/Editor/Events/Fields/field_dictionary.gd @@ -1,22 +1,20 @@ @tool -extends VBoxContainer +extends DialogicVisualEditorField -## Event block field for editing arrays. +## Event block field for editing arrays. -signal value_changed -var property_name : String - -const PairValue = "res://addons/dialogic/Editor/Events/Fields/KeyValuePairValue.tscn" +const PairValue = "res://addons/dialogic/Editor/Events/Fields/dictionary_part.tscn" func _ready(): %Add.icon = get_theme_icon("Add", "EditorIcons") -func set_value(value) -> void: + +func _set_value(value:Variant) -> void: for child in %Values.get_children(): child.queue_free() - + var dict : Dictionary - + # attempt to take dictionary values, create a fresh one if not possible if typeof(value) == TYPE_DICTIONARY: dict = value @@ -29,10 +27,10 @@ func set_value(value) -> void: 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) @@ -42,7 +40,7 @@ func set_value(value) -> void: func _on_value_changed(value:Variant) -> void: - emit_signal("value_changed", property_name, value) + value_changed.emit(property_name, value) func recalculate_values() -> void: @@ -60,10 +58,3 @@ func _on_AddButton_pressed() -> void: x.set_value("") x.value_changed.connect(recalculate_values) recalculate_values() - - -## Overridable -func set_left_text(value:String) -> void: - %LeftText.text = str(value) - %LeftText.visible = value.is_empty() - diff --git a/addons/dialogic/Editor/Events/Fields/KeyValuePairs.tscn b/addons/dialogic/Editor/Events/Fields/field_dictionary.tscn similarity index 92% rename from addons/dialogic/Editor/Events/Fields/KeyValuePairs.tscn rename to addons/dialogic/Editor/Events/Fields/field_dictionary.tscn index cee9b757..3bcaa118 100644 --- a/addons/dialogic/Editor/Events/Fields/KeyValuePairs.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_dictionary.tscn @@ -1,9 +1,9 @@ [gd_scene load_steps=5 format=3 uid="uid://c74bnmhefu72w"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/KeyValuePairs.gd" id="1_3mn6b"] -[ext_resource type="PackedScene" uid="uid://b27yweami3mxi" path="res://addons/dialogic/Editor/Events/Fields/KeyValuePairValue.tscn" id="2_g20vj"] +[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_esvau"] +[sub_resource type="Image" id="Image_5s534"] 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,11 +12,11 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_bywig"] -image = SubResource("Image_esvau") +[sub_resource type="ImageTexture" id="ImageTexture_bnwpy"] +image = SubResource("Image_5s534") [node name="Pairs" type="VBoxContainer"] -script = ExtResource("1_3mn6b") +script = ExtResource("1_p4kmu") [node name="Editing" type="HBoxContainer" parent="."] layout_mode = 2 @@ -31,16 +31,16 @@ size_flags_horizontal = 3 [node name="Add" type="Button" parent="Editing"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_bywig") +icon = SubResource("ImageTexture_bnwpy") [node name="Values" type="VBoxContainer" parent="."] unique_name_in_owner = true layout_mode = 2 -[node name="Value" parent="Values" instance=ExtResource("2_g20vj")] +[node name="Value" parent="Values" instance=ExtResource("2_fg1gy")] layout_mode = 2 -[node name="Value2" parent="Values" instance=ExtResource("2_g20vj")] +[node name="Value2" parent="Values" instance=ExtResource("2_fg1gy")] layout_mode = 2 [connection signal="pressed" from="Editing/Add" to="." method="_on_AddButton_pressed"] diff --git a/addons/dialogic/Editor/Events/Fields/FilePicker.gd b/addons/dialogic/Editor/Events/Fields/field_file.gd similarity index 65% rename from addons/dialogic/Editor/Events/Fields/FilePicker.gd rename to addons/dialogic/Editor/Events/Fields/field_file.gd index 625912a5..15d3dd8b 100644 --- a/addons/dialogic/Editor/Events/Fields/FilePicker.gd +++ b/addons/dialogic/Editor/Events/Fields/field_file.gd @@ -1,15 +1,15 @@ @tool -extends Control +extends DialogicVisualEditorField ## Event block field for selecting a file or directory. -signal value_changed(property_name:String, value:String) -var property_name : String +#region VARIABLES +################################################################################ @export var file_filter := "" @export var placeholder := "" @export var file_mode : EditorFileDialog.FileMode = EditorFileDialog.FILE_MODE_OPEN_FILE -@export var resource_icon:Texture = null: +var resource_icon:Texture: get: return resource_icon set(new_icon): @@ -20,29 +20,51 @@ var property_name : String else: %Field.theme_type_variation = "LineEditWithIcon" -var max_text_length := 16 +var max_width := 200 var current_value : String var hide_reset:bool = false +#endregion + + +#region MAIN METHODS +################################################################################ + func _ready() -> void: $FocusStyle.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) + %OpenButton.icon = get_theme_icon("Folder", "EditorIcons") - %ClearButton.icon = get_theme_icon("Reload", "EditorIcons") %OpenButton.button_down.connect(_on_OpenButton_pressed) + + %ClearButton.icon = get_theme_icon("Reload", "EditorIcons") %ClearButton.button_up.connect(clear_path) %ClearButton.visible = !hide_reset + %Field.set_drag_forwarding(Callable(), self._can_drop_data_fw, self._drop_data_fw) %Field.placeholder_text = placeholder -func set_value(value:String) -> void: +func _load_display_info(info:Dictionary) -> void: + file_filter = info.get('file_filter', '') + 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: current_value = value var text := value if file_mode != EditorFileDialog.FILE_MODE_OPEN_DIR: text = value.get_file() %Field.tooltip_text = value - if len(text) > max_text_length: + + if %Field.get_theme_font('font').get_string_size( + text, 0, -1, + %Field.get_theme_font_size('font_size')).x > max_width: %Field.expand_to_text_length = false + %Field.custom_minimum_size.x = max_width %Field.size.x = 0 else: %Field.custom_minimum_size.x = 0 @@ -53,25 +75,31 @@ func set_value(value:String) -> void: %ClearButton.visible = !value.is_empty() and !hide_reset -func set_enabled(is_enabled: bool) -> void: - %Field.editable = is_enabled - %OpenButton.disabled = !is_enabled - %ClearButton.disabled = !is_enabled +#endregion + +#region BUTTONS +################################################################################ func _on_OpenButton_pressed() -> void: find_parent('EditorView').godot_file_dialog(_on_file_dialog_selected, file_filter, file_mode, "Open "+ property_name) func _on_file_dialog_selected(path:String) -> void: - set_value(path) + _set_value(path) emit_signal("value_changed", property_name, path) func clear_path() -> void: - set_value("") + _set_value("") emit_signal("value_changed", property_name, "") +#endregion + + +#region DRAG AND DROP +################################################################################ + 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: @@ -83,6 +111,11 @@ func _can_drop_data_fw(at_position: Vector2, data: Variant) -> bool: func _drop_data_fw(at_position: Vector2, data: Variant) -> void: _on_file_dialog_selected(data.files[0]) +#endregion + + +#region VISUALS FOR FOCUS +################################################################################ func _on_field_focus_entered(): $FocusStyle.show() @@ -90,3 +123,5 @@ func _on_field_focus_entered(): func _on_field_focus_exited(): $FocusStyle.hide() _on_file_dialog_selected(%Field.text) + +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/FilePicker.tscn b/addons/dialogic/Editor/Events/Fields/field_file.tscn similarity index 93% rename from addons/dialogic/Editor/Events/Fields/FilePicker.tscn rename to addons/dialogic/Editor/Events/Fields/field_file.tscn index c472e715..c0e51dac 100644 --- a/addons/dialogic/Editor/Events/Fields/FilePicker.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_file.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=8 format=3 uid="uid://7mvxuaulctcq"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.gd" id="1_838yp"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_file.gd" id="1_0grcf"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tr837"] @@ -8,7 +8,7 @@ [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6b7on"] -[sub_resource type="Image" id="Image_80ipm"] +[sub_resource type="Image" id="Image_kg01j"] 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_u1qpa"] -image = SubResource("Image_80ipm") +[sub_resource type="ImageTexture" id="ImageTexture_j8aof"] +image = SubResource("Image_kg01j") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_p6jdu"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_raavq"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 @@ -33,11 +33,11 @@ border_width_right = 2 border_width_bottom = 2 corner_detail = 1 -[node name="FilePicker" type="MarginContainer"] -offset_right = 160.0 +[node name="Field_File" type="MarginContainer"] +offset_right = 314.0 offset_bottom = 40.0 theme_type_variation = &"DialogicEventEdit" -script = ExtResource("1_838yp") +script = ExtResource("1_0grcf") [node name="BG" type="PanelContainer" parent="."] layout_mode = 2 @@ -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_u1qpa") +icon = SubResource("ImageTexture_j8aof") flat = true [node name="ClearButton" type="Button" parent="BG/HBox"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_u1qpa") +icon = SubResource("ImageTexture_j8aof") flat = true [node name="FocusStyle" type="Panel" parent="."] visible = false layout_mode = 2 mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_p6jdu") +theme_override_styles/panel = SubResource("StyleBoxFlat_raavq") [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_number.gd b/addons/dialogic/Editor/Events/Fields/field_number.gd new file mode 100644 index 00000000..967bdf64 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_number.gd @@ -0,0 +1,198 @@ +@tool +class_name DialogicVisualEditorFieldNumber +extends DialogicVisualEditorField + +## Event block field for integers and floats. Improved version of the native spinbox. + +@export var allow_string : bool = false +@export var step: float = 0.1 +@export var enforce_step: bool = true +@export var min: float = 0 +@export var max: float= 999 +@export var value = 0.0 +@export var prefix: String = "" +@export var suffix: String = "" + +var _is_holding_button: bool = false #For handling incrementing while holding key or click + +#region MAIN METHODS +################################################################################ + +func _ready() -> void: + if %Value.text.is_empty(): + set_value(value) + + 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: + match info.get('mode', 0): + 0: #FLOAT + use_float_mode(info.get('step', 0.1)) + 1: #INT + use_int_mode(info.get('step', 1)) + 2: #DECIBLE: + use_decibel_mode(info.get('step', step)) + + for option in info.keys(): + match option: + 'min': min = info[option] + 'max': max = info[option] + 'prefix': update_prefix(info[option]) + 'suffix': update_suffix(info[option]) + 'step': + enforce_step = true + step = info[option] + 'hide_step_button': %Spin.hide() + + +func _set_value(new_value: Variant) -> void: + _on_value_text_submitted(str(new_value), true) + %Value.tooltip_text = tooltip_text + + +func _autofocus(): + %Value.grab_focus() + + +func get_value() -> float: + return value + + +func use_float_mode(value_step: float = 0.1) -> void: + step = value_step + update_suffix("") + enforce_step = false + + +func use_int_mode(value_step: float = 1) -> void: + step = value_step + update_suffix("") + enforce_step = true + + +func use_decibel_mode(value_step: float = step) -> void: + max = 6 + update_suffix("dB") + min = -80 + +#endregion + +#region UI FUNCTIONALITY +################################################################################ +var _stop_button_holding: Callable = func(button: BaseButton) -> void: + _is_holding_button = false + if button.button_up.get_connections().find(_stop_button_holding): + button.button_up.disconnect(_stop_button_holding) + if button.focus_exited.get_connections().find(_stop_button_holding): + button.focus_exited.disconnect(_stop_button_holding) + if button.mouse_exited.get_connections().find(_stop_button_holding): + button.mouse_exited.disconnect(_stop_button_holding) + + +func _holding_button(value_direction: int, button: BaseButton) -> void: + if _is_holding_button: + return + if _stop_button_holding.get_bound_arguments_count() > 0: + _stop_button_holding.unbind(0) + + _is_holding_button = true + + #Ensure removal of our value changing routine when it shouldn't run anymore + button.button_up.connect(_stop_button_holding.bind(button)) + button.focus_exited.connect(_stop_button_holding.bind(button)) + button.mouse_exited.connect(_stop_button_holding.bind(button)) + + var scene_tree: SceneTree = get_tree() + var delay_timer_ms: int = 600 + + #Instead of awaiting for the duration, await per-frame so we can catch any changes in _is_holding_button and exit completely + while(delay_timer_ms > 0): + if _is_holding_button == false: + return + var pre_time: int = Time.get_ticks_msec() + await scene_tree.process_frame + delay_timer_ms -= Time.get_ticks_msec() - pre_time + + var change_speed: float = 0.25 + + while(_is_holding_button == true): + await scene_tree.create_timer(change_speed).timeout + change_speed = maxf(0.05, change_speed - 0.01) + _on_value_text_submitted(str(value+(step * value_direction))) + + +func update_prefix(to_prefix: String) -> void: + prefix = to_prefix + %Prefix.visible = to_prefix != null and to_prefix != "" + %Prefix.text = prefix + + +func update_suffix(to_suffix: String) -> void: + suffix = to_suffix + %Suffix.visible = to_suffix != null and to_suffix != "" + %Suffix.text = suffix + +#endregion + +#region SIGNAL METHODS +################################################################################ +func _on_gui_input(event: InputEvent) -> void: + if event.is_action('ui_up') and event.get_action_strength('ui_up') > 0.5: + _on_value_text_submitted(str(value+step)) + elif event.is_action('ui_down') and event.get_action_strength('ui_down') > 0.5: + _on_value_text_submitted(str(value-step)) + + +func _on_increment_button_down(button: NodePath) -> void: + _on_value_text_submitted(str(value+step)) + _holding_button(1.0, get_node(button) as BaseButton) + + +func _on_decrement_button_down(button: NodePath) -> void: + _on_value_text_submitted(str(value-step)) + _holding_button(-1.0, get_node(button) as BaseButton) + + +func _on_value_text_submitted(new_text: String, no_signal:= false) -> void: + if new_text.is_valid_float(): + var temp: float = min(max(new_text.to_float(), min), max) + if !enforce_step: + value = temp + else: + value = snapped(temp, step) + elif allow_string: + value = new_text + %Value.text = str(value).pad_decimals(len(str(float(step)-floorf(step)))-2) + if not no_signal: + value_changed.emit(property_name, value) + # Visually disable Up or Down arrow when limit is reached to better indicate a limit has been hit + %Spin/Decrement.disabled = value <= min + %Spin/Increment.disabled = value >= max + + +# If prefix or suffix was clicked, select the actual value box instead and move the caret to the closest side. +func _on_sublabel_clicked(event: InputEvent) -> void: + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + var mousePos: Vector2 = get_global_mouse_position() + mousePos.x -= get_minimum_size().x / 2 + if mousePos.x > global_position.x: + (%Value as LineEdit).caret_column = (%Value as LineEdit).text.length() + else: + (%Value as LineEdit).caret_column = 0 + (%Value as LineEdit).grab_focus() + + +func _on_value_focus_exited() -> void: + _on_value_text_submitted(%Value.text) + $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('panel', 'DialogicEventEdit')) + + +func _on_value_focus_entered() -> void: + $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) + %Value.select_all.call_deferred() + +#endregion + diff --git a/addons/dialogic/Editor/Events/Fields/field_number.tscn b/addons/dialogic/Editor/Events/Fields/field_number.tscn new file mode 100644 index 00000000..bc4a8f23 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_number.tscn @@ -0,0 +1,173 @@ +[gd_scene load_steps=9 format=3 uid="uid://kdpp3mibml33"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_number.gd" id="1_0jdnn"] +[ext_resource type="Texture2D" uid="uid://dh1ycbmw8anqh" path="res://addons/dialogic/Editor/Images/Interactable/increment_icon.svg" id="3_v5cne"] +[ext_resource type="Texture2D" uid="uid://brjikovneb63n" path="res://addons/dialogic/Editor/Images/Interactable/decrement_icon.svg" id="4_ph52o"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_sj3oj"] +content_margin_left = 3.0 +content_margin_right = 1.0 + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8yqsu"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_smq50"] +content_margin_left = 2.0 +content_margin_right = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_increment"] +content_margin_left = 2.0 +content_margin_top = 6.0 +content_margin_right = 2.0 +content_margin_bottom = 2.0 +bg_color = Color(0.94, 0.94, 0.94, 0) +border_color = Color(0, 0, 0, 0) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_decrement"] +content_margin_left = 2.0 +content_margin_top = 2.0 +content_margin_right = 2.0 +content_margin_bottom = 6.0 +bg_color = Color(0.94, 0.94, 0.94, 0) +border_color = Color(0, 0, 0, 0) + +[node name="Field_Number" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1102.0 +offset_bottom = -617.0 +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 + +[node name="Layout" type="HBoxContainer" parent="Value_Panel"] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="Prefix" type="RichTextLabel" parent="Value_Panel/Layout"] +unique_name_in_owner = true +visible = false +clip_contents = false +layout_direction = 2 +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 4 +mouse_filter = 1 +mouse_default_cursor_shape = 1 +theme_override_colors/default_color = Color(0.54099, 0.540991, 0.54099, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_sj3oj") +theme_override_styles/normal = SubResource("StyleBoxEmpty_sj3oj") +bbcode_enabled = true +fit_content = true +scroll_active = false +autowrap_mode = 0 +tab_size = 2 +shortcut_keys_enabled = false +drag_and_drop_selection_enabled = false +text_direction = 1 + +[node name="Value" type="LineEdit" parent="Value_Panel/Layout"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +focus_mode = 1 +theme_override_constants/minimum_character_width = 0 +theme_override_styles/normal = SubResource("StyleBoxEmpty_8yqsu") +theme_override_styles/focus = SubResource("StyleBoxEmpty_8yqsu") +theme_override_styles/read_only = SubResource("StyleBoxEmpty_8yqsu") +text = "0" +alignment = 1 +expand_to_text_length = true +virtual_keyboard_type = 3 + +[node name="Suffix" type="RichTextLabel" parent="Value_Panel/Layout"] +unique_name_in_owner = true +visible = false +clip_contents = false +layout_direction = 2 +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +mouse_default_cursor_shape = 1 +theme_override_colors/default_color = Color(0.435192, 0.435192, 0.435192, 1) +theme_override_styles/focus = SubResource("StyleBoxEmpty_smq50") +theme_override_styles/normal = SubResource("StyleBoxEmpty_smq50") +bbcode_enabled = true +fit_content = true +scroll_active = false +autowrap_mode = 0 +tab_size = 2 +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"] +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"] +layout_mode = 2 +size_flags_vertical = 3 +auto_translate = false +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) +theme_override_colors/icon_focus_color = Color(0.412738, 0.550094, 0.760917, 1) +theme_override_styles/normal = SubResource("StyleBoxFlat_increment") +theme_override_styles/hover = SubResource("StyleBoxFlat_increment") +theme_override_styles/pressed = SubResource("StyleBoxFlat_increment") +theme_override_styles/disabled = SubResource("StyleBoxFlat_increment") +theme_override_styles/focus = SubResource("StyleBoxFlat_increment") +icon = ExtResource("3_v5cne") +flat = true +vertical_icon_alignment = 2 + +[node name="Decrement" type="Button" parent="Value_Panel/Layout/HBoxContainer/Spin"] +layout_mode = 2 +size_flags_vertical = 3 +auto_translate = false +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) +theme_override_colors/icon_focus_color = Color(0.412738, 0.550094, 0.760917, 1) +theme_override_styles/normal = SubResource("StyleBoxFlat_decrement") +theme_override_styles/hover = SubResource("StyleBoxFlat_decrement") +theme_override_styles/pressed = SubResource("StyleBoxFlat_decrement") +theme_override_styles/disabled = SubResource("StyleBoxFlat_decrement") +theme_override_styles/focus = SubResource("StyleBoxFlat_decrement") +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"] diff --git a/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd new file mode 100644 index 00000000..1bb79969 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd @@ -0,0 +1,285 @@ +@tool +extends DialogicVisualEditorField +## Event block field for strings. Options are determined by a function. + + +## SETTINGS +@export var placeholder_text := "Select Resource" +@export var empty_text := "" +enum Modes {PURE_STRING, PRETTY_PATH, IDENTIFIER} +@export var mode := Modes.PURE_STRING +@export var fit_text_length := true +var collapse_when_empty := false +var valid_file_drop_extension := "" +var get_suggestions_func: Callable + +var resource_icon: Texture = null: + get: + return resource_icon + set(new_icon): + resource_icon = new_icon + %Icon.texture = new_icon + +## STATE +var current_value: String +var current_selected := 0 + +## SUGGESTIONS ITEM LIST +var _v_separation := 0 +var _h_separation := 0 +var _icon_margin := 0 +var _line_height := 24 +var _max_height := 200 * DialogicUtil.get_editor_scale() + + +#region FIELD METHODS +################################################################################ + +func _set_value(value:Variant) -> void: + if value == null or value.is_empty(): + %Search.text = empty_text + else: + match mode: + Modes.PRETTY_PATH: + %Search.text = DialogicUtil.pretty_name(value) + Modes.IDENTIFIER when value.begins_with("res://"): + %Search.text = DialogicResourceUtil.get_unique_identifier(value) + _: + %Search.text = str(value) + + %Search.visible = not collapse_when_empty or value + current_value = str(value) + + + +func _load_display_info(info:Dictionary) -> void: + valid_file_drop_extension = info.get('file_extension', '') + collapse_when_empty = info.get('collapse_when_empty', false) + get_suggestions_func = info.get('suggestions_func', get_suggestions_func) + empty_text = info.get('empty_text', '') + placeholder_text = info.get('placeholder', 'Select Resource') + mode = info.get("mode", 0) + 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 _autofocus() -> void: + %Search.grab_focus() + +#endregion + + +#region BASIC +################################################################################ + +func _ready() -> void: + %Focus.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) + + %Search.text_changed.connect(_on_Search_text_changed) + %Search.text_submitted.connect(_on_Search_text_entered) + %Search.placeholder_text = placeholder_text + %Search.expand_to_text_length = fit_text_length + + %SelectButton.icon = get_theme_icon("Collapse", "EditorIcons") + + %Suggestions.add_theme_stylebox_override('bg', load("res://addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres")) + %Suggestions.hide() + %Suggestions.item_selected.connect(suggestion_selected) + %Suggestions.item_clicked.connect(suggestion_selected) + %Suggestions.fixed_icon_size = Vector2i(16, 16) * DialogicUtil.get_editor_scale() + + _v_separation = %Suggestions.get_theme_constant("v_separation") + _h_separation = %Suggestions.get_theme_constant("h_separation") + _icon_margin = %Suggestions.get_theme_constant("icon_margin") + + if resource_icon == null: + self.resource_icon = null + + +func change_to_empty() -> void: + value_changed.emit(property_name, "") + +#endregion + + +#region SEARCH & SUGGESTION POPUP +################################################################################ +func _on_Search_text_entered(new_text:String) -> void: + if %Suggestions.get_item_count(): + if %Suggestions.is_anything_selected(): + suggestion_selected(%Suggestions.get_selected_items()[0]) + else: + suggestion_selected(0) + else: + change_to_empty() + + +func _on_Search_text_changed(new_text:String, just_update:bool = false) -> void: + %Suggestions.clear() + + if new_text == "" and !just_update: + change_to_empty() + else: + %Search.show() + + var suggestions: Dictionary = get_suggestions_func.call(new_text) + + var line_length = 0 + var idx: int = 0 + for element in suggestions: + if new_text.is_empty() or new_text.to_lower() in element.to_lower() or new_text.to_lower() in str(suggestions[element].value).to_lower() or new_text.to_lower() in suggestions[element].get('tooltip', '').to_lower(): + var curr_line_length: int = 0 + curr_line_length = get_theme_font('font', 'Label').get_string_size( + element, HORIZONTAL_ALIGNMENT_LEFT, -1, get_theme_font_size("font_size", 'Label') + ).x + + %Suggestions.add_item(element) + if suggestions[element].has('icon'): + %Suggestions.set_item_icon(idx, suggestions[element].icon) + curr_line_length += %Suggestions.fixed_icon_size.x * %Suggestions.get_icon_scale() + _icon_margin * 2 + _h_separation + elif suggestions[element].has('editor_icon'): + %Suggestions.set_item_icon(idx, get_theme_icon(suggestions[element].editor_icon[0],suggestions[element].editor_icon[1])) + curr_line_length += %Suggestions.fixed_icon_size.x * %Suggestions.get_icon_scale() + _icon_margin * 2 + _h_separation + + line_length = max(line_length, curr_line_length) + + %Suggestions.set_item_tooltip(idx, suggestions[element].get('tooltip', '')) + %Suggestions.set_item_metadata(idx, suggestions[element].value) + idx += 1 + + if not %Suggestions.visible: + %Suggestions.show() + %Suggestions.global_position = $PanelContainer.global_position+Vector2(0,1)*$PanelContainer.size.y + + if %Suggestions.item_count: + %Suggestions.select(0) + current_selected = 0 + else: + current_selected = -1 + %Search.grab_focus() + + var total_height: int = 0 + for item in %Suggestions.item_count: + total_height += _line_height * DialogicUtil.get_editor_scale() + _v_separation + total_height += _v_separation * 2 + if total_height > _max_height: + line_length += %Suggestions.get_v_scroll_bar().get_minimum_size().x + + %Suggestions.size.x = max(%PanelContainer.size.x, line_length) + %Suggestions.size.y = min(total_height, _max_height) + + # Defer setting width to give PanelContainer + # time to update it's size + await get_tree().process_frame + await get_tree().process_frame + + %Suggestions.size.x = max(%PanelContainer.size.x, line_length) + + +func suggestion_selected(index: int, position := Vector2(), button_index := MOUSE_BUTTON_LEFT) -> void: + if button_index != MOUSE_BUTTON_LEFT: + return + if %Suggestions.is_item_disabled(index): + return + + %Search.text = %Suggestions.get_item_text(index) + + if %Suggestions.get_item_metadata(index) == null: + current_value = "" + + else: + current_value = %Suggestions.get_item_metadata(index) + + hide_suggestions() + + grab_focus() + value_changed.emit(property_name, current_value) + + +func _input(event:InputEvent) -> void: + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + if %Suggestions.visible: + if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()) and \ + !%SelectButton.get_global_rect().has_point(get_global_mouse_position()): + hide_suggestions() + + +func hide_suggestions() -> void: + %SelectButton.set_pressed_no_signal(false) + %Suggestions.hide() + if !current_value and collapse_when_empty: + %Search.hide() + + +func _on_SelectButton_toggled(button_pressed:bool) -> void: + if button_pressed: + _on_Search_text_changed('', true) + else: + hide_suggestions() + + +func _on_focus_entered() -> void: + %Search.grab_focus() + + +func _on_search_gui_input(event: InputEvent) -> void: + if event is InputEventKey and (event.keycode == KEY_DOWN or event.keycode == KEY_UP) and event.pressed: + if !%Suggestions.visible: + _on_Search_text_changed('', true) + current_selected = -1 + if event.keycode == KEY_DOWN: + current_selected = wrapi(current_selected+1, 0, %Suggestions.item_count) + if event.keycode == KEY_UP: + current_selected = wrapi(current_selected-1, 0, %Suggestions.item_count) + %Suggestions.select(current_selected) + %Suggestions.ensure_current_is_visible() + + if Input.is_key_pressed(KEY_CTRL): + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + if valid_file_drop_extension in [".dch", ".dtl"] and not current_value.is_empty(): + EditorInterface.edit_resource(DialogicResourceUtil.get_resource_from_identifier(current_value, valid_file_drop_extension)) + + if valid_file_drop_extension in [".dch", ".dtl"] and not current_value.is_empty(): + %Search.mouse_default_cursor_shape = CURSOR_POINTING_HAND + else: + %Search.mouse_default_cursor_shape = CURSOR_IBEAM + + +func _on_search_focus_entered() -> void: + if %Search.text == "": + _on_Search_text_changed("") + %Search.call_deferred('select_all') + %Focus.show() + + +func _on_search_focus_exited() -> void: + %Focus.hide() + if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()): + hide_suggestions() + +#endregion + + +#region DRAG AND DROP +################################################################################ + +func _can_drop_data(position:Vector2, data:Variant) -> bool: + if typeof(data) == TYPE_DICTIONARY and data.has('files') and len(data.files) == 1: + if valid_file_drop_extension: + if data.files[0].ends_with(valid_file_drop_extension): + return true + else: + return false + return false + + +func _drop_data(position:Vector2, data:Variant) -> void: + var path := str(data.files[0]) + if mode == Modes.IDENTIFIER: + path = DialogicResourceUtil.get_unique_identifier(path) + _set_value(path) + value_changed.emit(property_name, path) + +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn similarity index 94% rename from addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn rename to addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn index c05473d4..c4ce234c 100644 --- a/addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn @@ -1,12 +1,12 @@ [gd_scene load_steps=7 format=3 uid="uid://dpwhshre1n4t6"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.gd" id="1"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd" id="1_b07gq"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tmt5n"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_vennf"] -[sub_resource type="Image" id="Image_tmsys"] +[sub_resource type="Image" id="Image_jcy4w"] 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_wps7w"] -image = SubResource("Image_tmsys") +[sub_resource type="ImageTexture" id="ImageTexture_8v6yx"] +image = SubResource("Image_jcy4w") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_j1gic"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2yd2x"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 @@ -31,7 +31,7 @@ border_width_right = 2 border_width_bottom = 2 corner_detail = 1 -[node name="ComplexPicker" type="HBoxContainer"] +[node name="Field_DynamicStringOptions" type="HBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -42,7 +42,7 @@ offset_bottom = -622.0 grow_horizontal = 2 grow_vertical = 2 focus_mode = 2 -script = ExtResource("1") +script = ExtResource("1_b07gq") placeholder_text = "" [node name="PanelContainer" type="MarginContainer" parent="."] @@ -107,6 +107,7 @@ auto_translate = false focus_neighbor_top = NodePath("..") max_text_lines = 3 item_count = 1 +fixed_icon_size = Vector2i(16, 16) item_0/text = "Hello" [node name="SelectButton" type="Button" parent="PanelContainer/MarginContainer/HBoxContainer"] @@ -115,7 +116,7 @@ layout_mode = 2 focus_mode = 0 toggle_mode = true shortcut_in_tooltip = false -icon = SubResource("ImageTexture_wps7w") +icon = SubResource("ImageTexture_8v6yx") flat = true [node name="Focus" type="Panel" parent="PanelContainer"] @@ -123,7 +124,7 @@ unique_name_in_owner = true visible = false layout_mode = 2 mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_j1gic") +theme_override_styles/panel = SubResource("StyleBoxFlat_2yd2x") 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 new file mode 100644 index 00000000..0b379f50 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_options_fixed.gd @@ -0,0 +1,62 @@ +@tool +extends DialogicVisualEditorField + +## Event block field for constant options. For varying options use ComplexPicker. + +var options : Array = [] + +## if true, only the symbol will be displayed. In the dropdown text will be visible. +## Useful for making UI simpler +var symbol_only := false: + set(value): + symbol_only = value + if value: self.text = "" + +var current_value: Variant = -1 + + +func _ready() -> void: + add_theme_color_override("font_disabled_color", get_theme_color("font_color", "MenuButton")) + self.about_to_popup.connect(insert_options) + call("get_popup").index_pressed.connect(index_pressed) + + +func _load_display_info(info:Dictionary) -> void: + options = info.get('options', []) + self.disabled = info.get('disabled', false) + symbol_only = info.get('symbol_only', false) + + +func _set_value(value:Variant) -> void: + for option in options: + if option['value'] == value: + if typeof(option.get('icon')) == TYPE_ARRAY: + option.icon = callv('get_theme_icon', option.get('icon')) + if !symbol_only: + self.text = option['label'] + self.icon = option.get('icon', null) + current_value = value + + +func get_value() -> Variant: + return current_value + + +func insert_options() -> void: + call("get_popup").clear() + + var idx := 0 + for option in options: + if typeof(option.get('icon')) == TYPE_ARRAY: + option.icon = callv('get_theme_icon', option.get('icon')) + call("get_popup").add_icon_item(option.get('icon', null), option['label']) + call("get_popup").set_item_metadata(idx, option['value']) + idx += 1 + + +func index_pressed(idx:int) -> void: + current_value = idx + if !symbol_only: + self.text = call("get_popup").get_item_text(idx) + self.icon =call("get_popup").get_item_icon(idx) + value_changed.emit(property_name, call("get_popup").get_item_metadata(idx)) diff --git a/addons/dialogic/Editor/Events/Fields/OptionSelector.tscn b/addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn similarity index 79% rename from addons/dialogic/Editor/Events/Fields/OptionSelector.tscn rename to addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn index 15c24588..550160c2 100644 --- a/addons/dialogic/Editor/Events/Fields/OptionSelector.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_options_fixed.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=2 format=3 uid="uid://d3bhehatwoio"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/OptionSelector.gd" id="1"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_options_fixed.gd" id="1"] -[node name="OptionSelector" type="MenuButton"] +[node name="Field_FixedOptions" type="MenuButton"] offset_right = 137.0 offset_bottom = 43.0 focus_mode = 2 diff --git a/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd b/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd new file mode 100644 index 00000000..aaeb4c4d --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_text_multiline.gd @@ -0,0 +1,74 @@ +@tool +extends DialogicVisualEditorField + +## Event block field that allows entering multiline text (mainly text event). + +@onready var code_completion_helper: Node = find_parent('EditorsManager').get_node('CodeCompletionHelper') + + +#region MAIN METHODS +################################################################################ + +func _ready() -> void: + self.text_changed.connect(_on_text_changed) + self.syntax_highlighter = code_completion_helper.text_syntax_highlighter + + +func _load_display_info(info:Dictionary) -> void: + pass + + +func _set_value(value:Variant) -> void: + self.text = str(value) + + +func _autofocus() -> void: + grab_focus() + +#endregion + + +#region SIGNAL METHODS +################################################################################ + +func _on_text_changed(value := "") -> void: + value_changed.emit(property_name, self.text) + +#endregion + + +#region AUTO COMPLETION +################################################################################ + +## Called if something was typed +func _request_code_completion(force:bool): + code_completion_helper.request_code_completion(force, self, 0) + + +## Filters the list of all possible options, depending on what was typed +## Purpose of the different Kinds is explained in [_request_code_completion] +func _filter_code_completion_candidates(candidates:Array) -> Array: + return code_completion_helper.filter_code_completion_candidates(candidates, self) + + +## Called when code completion was activated +## Inserts the selected item +func _confirm_code_completion(replace:bool) -> void: + code_completion_helper.confirm_code_completion(replace, self) + +#endregion + + +#region SYMBOL CLICKING +################################################################################ + +## Performs an action (like opening a link) when a valid symbol was clicked +func _on_symbol_lookup(symbol, line, column): + code_completion_helper.symbol_lookup(symbol, line, column) + + +## Called to test if a symbol can be clicked +func _on_symbol_validate(symbol:String) -> void: + code_completion_helper.symbol_validate(symbol, self) + +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/MultilineText.tscn b/addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn similarity index 72% rename from addons/dialogic/Editor/Events/Fields/MultilineText.tscn rename to addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn index 5e2bd3cf..fab44882 100644 --- a/addons/dialogic/Editor/Events/Fields/MultilineText.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_text_multiline.tscn @@ -1,19 +1,20 @@ [gd_scene load_steps=5 format=3 uid="uid://dyp7m2nvab1aj"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/MultilineText.gd" id="1"] -[ext_resource type="StyleBox" uid="uid://cu8otiwksn8ma" path="res://addons/dialogic/Editor/Events/styles/TextBackground.tres" id="1_dkxlh"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd" id="1_wj4ha"] +[ext_resource type="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"] [sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_2q5dk"] -script = ExtResource("1_wj4ha") +script = ExtResource("2_ww6ga") -[node name="MultilineText" type="CodeEdit"] +[node name="Field_Text_Multiline" type="CodeEdit"] offset_right = 413.0 offset_bottom = 15.0 size_flags_horizontal = 3 size_flags_vertical = 3 -theme_override_styles/normal = ExtResource("1_dkxlh") +theme_override_styles/normal = ExtResource("1_xq18n") wrap_mode = 1 +scroll_fit_content_height = true syntax_highlighter = SubResource("SyntaxHighlighter_2q5dk") symbol_lookup_on_click = true delimiter_strings = Array[String]([]) @@ -25,4 +26,4 @@ auto_brace_completion_pairs = { "[": "]", "{": "}" } -script = ExtResource("1") +script = ExtResource("3_q7600") diff --git a/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd b/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd new file mode 100644 index 00000000..c6378eec --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_text_singleline.gd @@ -0,0 +1,40 @@ +@tool +extends DialogicVisualEditorField + +## Event block field for a single line of text. + + +var placeholder :String= "": + set(value): + placeholder = value + self.placeholder_text = placeholder + + +#region MAIN METHODS +################################################################################ + +func _ready() -> void: + self.text_changed.connect(_on_text_changed) + + +func _load_display_info(info:Dictionary) -> void: + self.placeholder = info.get('placeholder', '') + + +func _set_value(value:Variant) -> void: + self.text = str(value) + + +func _autofocus(): + grab_focus() + +#endregion + + +#region SIGNAL METHODS +################################################################################ + +func _on_text_changed(value := "") -> void: + value_changed.emit(property_name, self.text) + +#endregion diff --git a/addons/dialogic/Editor/Events/Fields/SinglelineText.tscn b/addons/dialogic/Editor/Events/Fields/field_text_singleline.tscn similarity index 63% rename from addons/dialogic/Editor/Events/Fields/SinglelineText.tscn rename to addons/dialogic/Editor/Events/Fields/field_text_singleline.tscn index 327083c4..d90d3b05 100644 --- a/addons/dialogic/Editor/Events/Fields/SinglelineText.tscn +++ b/addons/dialogic/Editor/Events/Fields/field_text_singleline.tscn @@ -1,10 +1,10 @@ [gd_scene load_steps=2 format=3 uid="uid://c0vkcehgjsjy"] -[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/SinglelineText.gd" id="1"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_text_singleline.gd" id="1_4vnxv"] -[node name="SingleLineText" type="LineEdit"] +[node name="Field_Text_Singleline" type="LineEdit"] offset_right = 1152.0 offset_bottom = 81.0 theme_type_variation = &"DialogicEventEdit" expand_to_text_length = true -script = ExtResource("1") +script = ExtResource("1_4vnxv") diff --git a/addons/dialogic/Editor/Events/Fields/field_vector2.gd b/addons/dialogic/Editor/Events/Fields/field_vector2.gd new file mode 100644 index 00000000..040abf07 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector2.gd @@ -0,0 +1,26 @@ +@tool +extends DialogicVisualEditorFieldVector +## Event block field for a Vector2. + +var current_value := Vector2() + + +func _set_value(value: Variant) -> void: + current_value = value + super(value) + + +func get_value() -> Vector2: + return current_value + + +func _on_sub_value_changed(sub_component: String, value: float) -> void: + match sub_component: + 'X': current_value.x = value + 'Y': current_value.y = value + _on_value_changed(current_value) + + +func _update_sub_component_text(value: Variant) -> void: + $X._on_value_text_submitted(str(value.x), true) + $Y._on_value_text_submitted(str(value.y), true) diff --git a/addons/dialogic/Editor/Events/Fields/field_vector2.tscn b/addons/dialogic/Editor/Events/Fields/field_vector2.tscn new file mode 100644 index 00000000..bfbc8d63 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector2.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=3 format=3 uid="uid://dtimnsj014cu"] + +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_vector2.gd" id="1_v6lp0"] +[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/field_number.tscn" id="2_a0b6y"] + +[node name="Field_Vector2" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1033.0 +offset_bottom = -617.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 2 +script = ExtResource("1_v6lp0") + +[node name="X" parent="." instance=ExtResource("2_a0b6y")] +layout_mode = 2 +step = 0.001 +min = -9999.0 +max = 9999.0 +prefix = "" + +[node name="Y" parent="." instance=ExtResource("2_a0b6y")] +layout_mode = 2 +step = 0.001 +min = -9999.0 +max = 9999.0 +prefix = "" diff --git a/addons/dialogic/Editor/Events/Fields/field_vector3.gd b/addons/dialogic/Editor/Events/Fields/field_vector3.gd new file mode 100644 index 00000000..c5b9f6de --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector3.gd @@ -0,0 +1,28 @@ +@tool +extends DialogicVisualEditorFieldVector +## Event block field for a Vector3. + +var current_value := Vector3() + + +func _set_value(value: Variant) -> void: + current_value = value + super(value) + + +func get_value() -> Vector3: + return current_value + + +func _on_sub_value_changed(sub_component: String, value: float) -> void: + match sub_component: + 'X': current_value.x = value + 'Y': current_value.y = value + 'Z': current_value.z = value + _on_value_changed(current_value) + + +func _update_sub_component_text(value: Variant) -> void: + $X._on_value_text_submitted(str(value.x), true) + $Y._on_value_text_submitted(str(value.y), true) + $Z._on_value_text_submitted(str(value.z), true) diff --git a/addons/dialogic/Editor/Events/Fields/field_vector3.tscn b/addons/dialogic/Editor/Events/Fields/field_vector3.tscn new file mode 100644 index 00000000..019a26bb --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector3.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=4 format=3 uid="uid://cklkpfrcvopgw"] + +[ext_resource type="PackedScene" uid="uid://dtimnsj014cu" path="res://addons/dialogic/Editor/Events/Fields/field_vector2.tscn" id="1_l3y0o"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_vector3.gd" id="2_gktf1"] +[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/field_number.tscn" id="3_k0u0p"] + +[node name="Field_Vector3" instance=ExtResource("1_l3y0o")] +offset_right = -973.0 +script = ExtResource("2_gktf1") + +[node name="Z" parent="." index="2" instance=ExtResource("3_k0u0p")] +layout_mode = 2 +step = 0.001 +min = -9999.0 +max = 9999.0 +affix = "z:" diff --git a/addons/dialogic/Editor/Events/Fields/field_vector4.gd b/addons/dialogic/Editor/Events/Fields/field_vector4.gd new file mode 100644 index 00000000..3259ce37 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector4.gd @@ -0,0 +1,30 @@ +@tool +extends DialogicVisualEditorFieldVector +## Event block field for a Vector4. + +var current_value := Vector4() + + +func _set_value(value: Variant) -> void: + current_value = value + super(value) + + +func get_value() -> Vector4: + return current_value + + +func _on_sub_value_changed(sub_component: String, value: float) -> void: + match sub_component: + 'X': current_value.x = value + 'Y': current_value.y = value + 'Z': current_value.z = value + 'W': current_value.w = value + _on_value_changed(current_value) + + +func _update_sub_component_text(value: Variant) -> void: + $X._on_value_text_submitted(str(value.x), true) + $Y._on_value_text_submitted(str(value.y), true) + $Z._on_value_text_submitted(str(value.z), true) + $W._on_value_text_submitted(str(value.w), true) diff --git a/addons/dialogic/Editor/Events/Fields/field_vector4.tscn b/addons/dialogic/Editor/Events/Fields/field_vector4.tscn new file mode 100644 index 00000000..182e0f22 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector4.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=4 format=3 uid="uid://dykss037r2rsc"] + +[ext_resource type="PackedScene" uid="uid://cklkpfrcvopgw" path="res://addons/dialogic/Editor/Events/Fields/field_vector3.tscn" id="1_20tvl"] +[ext_resource type="Script" path="res://addons/dialogic/Editor/Events/Fields/field_vector4.gd" id="2_yksrc"] +[ext_resource type="PackedScene" uid="uid://kdpp3mibml33" path="res://addons/dialogic/Editor/Events/Fields/field_number.tscn" id="3_1jogk"] + +[node name="Field_Vector4" instance=ExtResource("1_20tvl")] +offset_right = -908.0 +script = ExtResource("2_yksrc") + +[node name="W" parent="." index="3" instance=ExtResource("3_1jogk")] +layout_mode = 2 +step = 0.001 +min = -9999.0 +max = 9999.0 +affix = "w:" diff --git a/addons/dialogic/Editor/Events/Fields/field_vector_base.gd b/addons/dialogic/Editor/Events/Fields/field_vector_base.gd new file mode 100644 index 00000000..5d5d9e95 --- /dev/null +++ b/addons/dialogic/Editor/Events/Fields/field_vector_base.gd @@ -0,0 +1,38 @@ +class_name DialogicVisualEditorFieldVector +extends DialogicVisualEditorField +## Base type for Vector event blocks + + +func _ready() -> void: + for child in get_children(): + child.tooltip_text = tooltip_text + child.property_name = child.name #to identify the name of the changed sub-component + child.value_changed.connect(_on_sub_value_changed) + + +func _load_display_info(info: Dictionary) -> void: + for child in get_children(): + if child is DialogicVisualEditorFieldNumber: + if info.get('no_prefix', false): + child._load_display_info(info) + else: + var prefixed_info := info.duplicate() + prefixed_info.merge({'prefix':child.name.to_lower()}) + child._load_display_info(prefixed_info) + + +func _set_value(value: Variant) -> void: + _update_sub_component_text(value) + _on_value_changed(value) + + +func _on_value_changed(value: Variant) -> void: + value_changed.emit(property_name, value) + + +func _on_sub_value_changed(sub_component: String, value: float) -> void: + pass + + +func _update_sub_component_text(value: Variant) -> void: + pass diff --git a/addons/dialogic/Editor/Events/event_field.gd b/addons/dialogic/Editor/Events/event_field.gd new file mode 100644 index 00000000..f5db871f --- /dev/null +++ b/addons/dialogic/Editor/Events/event_field.gd @@ -0,0 +1,35 @@ +@tool +class_name DialogicVisualEditorField +extends Control + +signal value_changed(property_name:String, value:Variant) +var property_name := "" + +var event_resource: DialogicEvent = null + +#region OVERWRITES +################################################################################ + +## To be overwritten +func _load_display_info(info:Dictionary) -> void: + pass + + +## To be overwritten +func _set_value(value:Variant) -> void: + pass + + +## To be overwritten +func _autofocus() -> void: + pass + +#endregion + + +func set_value(value:Variant) -> void: + _set_value(value) + + +func take_autofocus() -> void: + _autofocus() diff --git a/addons/dialogic/Editor/Events/styles/SettingsFieldBackground.tres b/addons/dialogic/Editor/Events/styles/SettingsFieldBackground.tres deleted file mode 100644 index 91b4b38e..00000000 --- a/addons/dialogic/Editor/Events/styles/SettingsFieldBackground.tres +++ /dev/null @@ -1,18 +0,0 @@ -[gd_resource type="StyleBoxFlat" format=2] - -[resource] -content_margin_left = 30.0 -content_margin_right = 25.0 -content_margin_top = 5.0 -content_margin_bottom = 5.0 -bg_color = Color( 0.12549, 0.141176, 0.192157, 1 ) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color( 0.0980392, 0.113725, 0.152941, 1 ) -border_blend = true -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 diff --git a/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres b/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres index 603fe134..c9312fb0 100644 --- a/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres +++ b/addons/dialogic/Editor/Events/styles/selected_styleboxflat.tres @@ -1,16 +1,16 @@ [gd_resource type="StyleBoxFlat" format=3 uid="uid://obyrr26pqk2p"] [resource] -content_margin_left = 7.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 +content_margin_left = 3.0 +content_margin_top = 1.0 +content_margin_right = 4.0 +content_margin_bottom = 1.0 bg_color = Color(0.776471, 0.776471, 0.776471, 0.207843) -border_width_left = 3 border_color = Color(1, 1, 1, 1) -corner_radius_top_left = 1 +corner_radius_top_left = 5 corner_radius_top_right = 5 corner_radius_bottom_right = 5 -corner_radius_bottom_left = 1 -expand_margin_top = 3.0 -expand_margin_bottom = 3.0 +corner_radius_bottom_left = 5 +expand_margin_left = 1.0 +expand_margin_top = 1.0 +expand_margin_bottom = 2.0 diff --git a/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres b/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres index f7cf0afe..2fae3b36 100644 --- a/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres +++ b/addons/dialogic/Editor/Events/styles/unselected_stylebox.tres @@ -1,4 +1,7 @@ [gd_resource type="StyleBoxEmpty" format=3 uid="uid://cl75ikyq2is7c"] [resource] -content_margin_left = 7.0 +content_margin_left = 3.0 +content_margin_top = 1.0 +content_margin_right = 4.0 +content_margin_bottom = 1.0 diff --git a/addons/dialogic/Editor/HomePage/home_page.gd b/addons/dialogic/Editor/HomePage/home_page.gd index a2044e5f..c666c328 100644 --- a/addons/dialogic/Editor/HomePage/home_page.gd +++ b/addons/dialogic/Editor/HomePage/home_page.gd @@ -14,7 +14,7 @@ func _get_icon() -> Texture: func _ready(): self_modulate = get_theme_color("font_color", "Editor") self_modulate.a = 0.2 - + var edit_scale := DialogicUtil.get_editor_scale() %HomePageBox.custom_minimum_size = Vector2(600, 350)*edit_scale %TopPanel.custom_minimum_size.y = 100*edit_scale @@ -22,7 +22,7 @@ func _ready(): var plugin_cfg := ConfigFile.new() plugin_cfg.load("res://addons/dialogic/plugin.cfg") %VersionLabel.text = plugin_cfg.get_value('plugin', 'version', 'unknown version') - + %BottomPanel.self_modulate = get_theme_color("dark_color_3", "Editor") %RandomTipLabel.add_theme_color_override("font_color", get_theme_color("property_color_z", "Editor")) @@ -32,7 +32,7 @@ func _ready(): func _register(): editors_manager.register_simple_editor(self) - + self.alternative_text = "Welcome to dialogic!" @@ -42,7 +42,7 @@ func _open(extra_info:Variant="") -> void: var file := FileAccess.open('res://addons/dialogic/Editor/HomePage/tips.txt', FileAccess.READ) tips = file.get_as_text().split('\n') tips = tips.filter(func(item): return !item.is_empty()) - + randomize() var tip :String = tips[randi()%len(tips)] var text := tip.get_slice(';',0).strip_edges() @@ -57,17 +57,17 @@ func show_tip(text:String='', action:String='') -> void: %TipBox.hide() %RandomTipLabel.hide() return - + %TipBox.show() %RandomTipLabel.show() %RandomTip.text = '[i]'+text - + if action.is_empty(): %RandomTipMoreButton.hide() return - + %RandomTipMoreButton.show() - + if %RandomTipMoreButton.pressed.is_connected(_on_tip_action): %RandomTipMoreButton.pressed.disconnect(_on_tip_action) %RandomTipMoreButton.pressed.connect(_on_tip_action.bind(action)) diff --git a/addons/dialogic/Editor/HomePage/home_page.tscn b/addons/dialogic/Editor/HomePage/home_page.tscn index 2aa4d831..a7733f26 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_ybad5"] +[sub_resource type="Image" id="Image_ubn0t"] 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_sioln"] -image = SubResource("Image_ybad5") +[sub_resource type="ImageTexture" id="ImageTexture_jsefb"] +image = SubResource("Image_ubn0t") [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-10 (Godot 4.1.2)" +text = "2.0-Alpha-13 (Godot 4.2+)" horizontal_alignment = 2 [node name="ScrollContainer" type="ScrollContainer" parent="CenterContainer/HomePageBox/BottomPanel"] @@ -261,14 +261,14 @@ layout_mode = 2 theme_type_variation = &"DialogicLink" text = " Wiki" underline = 2 -uri = "https://dialogic-docs.coppolaemilio.com" +uri = "https://docs.dialogic.pro/" [node name="WikiGettingStartedButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] layout_mode = 2 theme_type_variation = &"DialogicLink" text = " Getting Started" underline = 2 -uri = "https://dialogic-docs.coppolaemilio.com/documentation/getting-started/" +uri = "https://docs.dialogic.pro/getting-started.html" [node name="Separator" type="Control" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] custom_minimum_size = Vector2(0, 10) @@ -284,7 +284,7 @@ layout_mode = 2 theme_type_variation = &"DialogicLink" text = " Bug / Request" underline = 2 -uri = "https://github.com/coppolaemilio/dialogic/issues/new/choose" +uri = "https://github.com/dialogic-godot/dialogic/issues/new/choose" [node name="DiscordButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] layout_mode = 2 @@ -293,6 +293,13 @@ text = " Discord" underline = 2 uri = "https://discord.gg/2hHQzkf2pX" +[node name="Website" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] +layout_mode = 2 +theme_type_variation = &"DialogicLink" +text = " Website" +underline = 2 +uri = "https://dialogic.pro/" + [node name="DonateButton" type="LinkButton" parent="CenterContainer/HomePageBox/BottomPanel/ScrollContainer/HBoxContainer/CenterContainer"] layout_mode = 2 theme_type_variation = &"DialogicLink" @@ -362,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_sioln") +icon = SubResource("ImageTexture_jsefb") expand_icon = true diff --git a/addons/dialogic/Editor/HomePage/tips.txt b/addons/dialogic/Editor/HomePage/tips.txt index 2a197985..c8b6f07b 100644 --- a/addons/dialogic/Editor/HomePage/tips.txt +++ b/addons/dialogic/Editor/HomePage/tips.txt @@ -5,7 +5,7 @@ Did you know that dialogic supports translations? It does!; editor://Settings->T 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]<Oh hi/Hello you/Well, well>[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://github.com/coppolaemilio/dialogic/wiki/Tutorial:-Custom-Portraits +You can use scenes as portraits! This gives you basically limiteless 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/Interactable/decrement_icon.svg b/addons/dialogic/Editor/Images/Interactable/decrement_icon.svg new file mode 100644 index 00000000..eb976918 --- /dev/null +++ b/addons/dialogic/Editor/Images/Interactable/decrement_icon.svg @@ -0,0 +1 @@ +<svg width="14" height="6" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m8.046 5.406 4.91-5.134C13.063.16 12.954 0 12.838 0h-2.432L7.303 3.244c-.173.181-.402.189-.582 0L3.618 0H1.166c-.159 0-.208.19-.13.272l4.942 5.134c.54.56 1.538.554 2.068 0z" fill="#e0e0e0" style="fill:#fff;fill-opacity:1;stroke-width:1.05736"/></svg> diff --git a/addons/dialogic/Editor/Images/Interactable/decrement_icon.svg.import b/addons/dialogic/Editor/Images/Interactable/decrement_icon.svg.import new file mode 100644 index 00000000..d9bb5393 --- /dev/null +++ b/addons/dialogic/Editor/Images/Interactable/decrement_icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://brjikovneb63n" +path="res://.godot/imported/decrement_icon.svg-9556cf56db91e200fb946372e010fd5e.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Interactable/decrement_icon.svg" +dest_files=["res://.godot/imported/decrement_icon.svg-9556cf56db91e200fb946372e010fd5e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Interactable/increment_icon.svg b/addons/dialogic/Editor/Images/Interactable/increment_icon.svg new file mode 100644 index 00000000..0b72c076 --- /dev/null +++ b/addons/dialogic/Editor/Images/Interactable/increment_icon.svg @@ -0,0 +1 @@ +<svg width="14" height="6" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m5.954.418-4.91 5.135c-.107.112.002.271.118.271h2.432l3.103-3.243c.173-.182.402-.19.582 0l3.103 3.243h2.452c.159 0 .208-.19.13-.271L8.021.418c-.54-.56-1.538-.554-2.068 0z" fill="#e0e0e0" style="fill:#fff;fill-opacity:1;stroke-width:1.05736"/></svg> diff --git a/addons/dialogic/Editor/Images/Interactable/increment_icon.svg.import b/addons/dialogic/Editor/Images/Interactable/increment_icon.svg.import new file mode 100644 index 00000000..4256591f --- /dev/null +++ b/addons/dialogic/Editor/Images/Interactable/increment_icon.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dh1ycbmw8anqh" +path="res://.godot/imported/increment_icon.svg-081e6509e76349f0628c55a41e85fd65.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/dialogic/Editor/Images/Interactable/increment_icon.svg" +dest_files=["res://.godot/imported/increment_icon.svg-081e6509e76349f0628c55a41e85fd65.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Pieces/warning.svg.import b/addons/dialogic/Editor/Images/Pieces/warning.svg.import index ada59763..b9442db3 100644 --- a/addons/dialogic/Editor/Images/Pieces/warning.svg.import +++ b/addons/dialogic/Editor/Images/Pieces/warning.svg.import @@ -33,6 +33,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=3.0 +svg/scale=1.0 editor/scale_with_editor_scale=true editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Editor/Images/Resources/portrait.svg b/addons/dialogic/Editor/Images/Resources/portrait.svg index 6bee1089..006807ab 100644 --- a/addons/dialogic/Editor/Images/Resources/portrait.svg +++ b/addons/dialogic/Editor/Images/Resources/portrait.svg @@ -1,8 +1,9 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg width="22" height="22" viewBox="0 0 22 22" fill="none" version="1.1" id="svg4" sodipodi:docname="portrait.svg" inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> - <defs id="defs8" /> - <sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#505050" showgrid="true" inkscape:zoom="16" inkscape:cx="24.90625" inkscape:cy="14.0625" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4"> - <inkscape:grid type="xygrid" id="grid6918" originx="0" originy="0" /> - </sodipodi:namedview> - <path fill-rule="evenodd" clip-rule="evenodd" d="m 11.111095,18.651229 c 4.02883,0 7.294832,-3.334617 7.294832,-7.448091 0,-4.1134631 -3.266002,-7.4480904 -7.294832,-7.4480904 -4.0288224,0 -7.2948341,3.3346273 -7.2948341,7.4480904 0,4.113474 3.2660117,7.448091 7.2948341,7.448091 z M 6.5741771,10.203711 C 6.948277,9.821709 7.4556743,9.607119 7.9847375,9.607119 c 0.5290631,0 1.0364603,0.21459 1.4105605,0.596592 l 0.305236,0.31165 0.736935,-0.752417 -0.305237,-0.31165 C 9.562652,8.86979 8.7901913,8.5431061 7.9847375,8.5431061 c -0.805454,0 -1.5779143,0.3266839 -2.1474532,0.9081879 l -0.3052264,0.31165 0.7368825,0.752417 z M 14.237451,9.607119 c -0.529084,0 -1.036491,0.21459 -1.410613,0.596592 l -0.30513,0.31165 -0.736987,-0.752417 0.305235,-0.31165 c 0.569624,-0.581504 1.342041,-0.9081879 2.147495,-0.9081879 0.805454,0 1.577873,0.3266839 2.147496,0.9081879 l 0.305236,0.31165 -0.736986,0.752417 -0.305134,-0.31165 C 15.273943,9.821709 14.766534,9.607119 14.237451,9.607119 Z m -3.126356,6.916084 c -2.0842384,0 -3.6474173,-2.128026 -4.1684767,-3.192038 h 8.3369507 c -0.521059,1.064012 -2.084236,3.192038 -4.168474,3.192038 z" fill="#ffffff" id="path2" style="display:inline;stroke-width:1.05302" inkscape:label="Portrait" /> -</svg> +<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" version="1.1"> + <g> + <title>Layer 1 + + + + + + \ No newline at end of file diff --git a/addons/dialogic/Editor/Settings/csv_file.gd b/addons/dialogic/Editor/Settings/csv_file.gd index 411d5205..3f8fb3a9 100644 --- a/addons/dialogic/Editor/Settings/csv_file.gd +++ b/addons/dialogic/Editor/Settings/csv_file.gd @@ -27,39 +27,40 @@ var updated_rows: int = 0 ## The amount of events that were added to the CSV file. var new_rows: int = 0 +## Whether this CSV handler should add newlines as a separator between sections. +## A section may be a new character, new timeline, or new glossary item inside +## a per-project file. +var add_separator: bool = false -## Stores all character names from the current CSV. -## -## If this is CSV file for timeline events, every appearing speaker will be -## added once to this dictionary by their translation ID. -## If the translation ID does not exist, a new one will be generated. -## -## If this is the character name CSV file, this field captures all characters -## that were added to [member lines] using the -## [method collect_lines_from_characters]. -## -## Key: String, Value: PackedStringArray -var collected_characters: Dictionary = {} +enum PropertyType { + String = 0, + Array = 1, + Other = 2, +} + +## The translation property used for the glossary item translation. +const TRANSLATION_ID := DialogicGlossary.TRANSLATION_PROPERTY ## Attempts to load the CSV file from [param file_path]. ## If the file does not exist, a single entry is added to the [member lines] ## array. -func _init(file_path: String, original_locale: String) -> void: +## The [param separator_enabled] enables adding newlines as a separator to +## per-project files. This is useful for readability. +func _init(file_path: String, original_locale: String, separator_enabled: bool) -> void: used_file_path = file_path + add_separator = separator_enabled # The first entry must be the locale row. # [method collect_lines_from_timeline] will add the other locales, if any. var locale_array_line := PackedStringArray(["keys", original_locale]) lines.append(locale_array_line) - if not 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. # For example: "keys, en" column_count = 2 - - file = FileAccess.open(file_path, FileAccess.WRITE) return file = FileAccess.open(file_path, FileAccess.READ) @@ -74,10 +75,12 @@ func _init(file_path: String, original_locale: String) -> void: ## Private function to read the CSV file into the [member lines] array. +## Cannot be called on a new file. func _read_file_into_lines() -> void: while not file.eof_reached(): var line := file.get_csv_line() var row_key := line[0] + old_lines[row_key] = line @@ -87,17 +90,7 @@ func _read_file_into_lines() -> void: ## If this is the character name CSV file, use this method to ## take previously collected characters from other [class DialogicCsvFile]s. func collect_lines_from_characters(characters: Dictionary) -> void: - for character in characters.values(): - - # Check if the character has a valid translation ID. - if character._translation_id == null or character._translation_id.is_empty(): - character.add_translation_id() - - if character._translation_id in collected_characters: - continue - else: - collected_characters[character._translation_id] = character - + for character: DialogicCharacter in characters.values(): # Add row for display names. var name_property := DialogicCharacter.TranslatedProperties.NAME var display_name_key: String = character.get_property_translation_key(name_property) @@ -105,24 +98,194 @@ func collect_lines_from_characters(characters: Dictionary) -> void: var array_line := PackedStringArray([display_name_key, line_value]) lines.append(array_line) - var character_nicknames: Array = character.nicknames - if character_nicknames.is_empty() or (character_nicknames.size() == 1 and character_nicknames[0].is_empty()): - return + var nicknames: Array = character.nicknames + + if not nicknames.is_empty(): + var nick_name_property := DialogicCharacter.TranslatedProperties.NICKNAMES + var nickname_string: String = ",".join(nicknames) + var nickname_name_line_key: String = character.get_property_translation_key(nick_name_property) + var nick_array_line := PackedStringArray([nickname_name_line_key, nickname_string]) + lines.append(nick_array_line) + + # New character item, if needed, add a separator. + if add_separator: + _append_empty() + + +## Appends an empty line to the [member lines] array. +func _append_empty() -> void: + var empty_line := PackedStringArray(["", ""]) + lines.append(empty_line) + + +## Returns the property type for the given [param key]. +func _get_key_type(key: String) -> PropertyType: + if key.ends_with(DialogicGlossary.NAME_PROPERTY): + return PropertyType.String + + if key.ends_with(DialogicGlossary.ALTERNATIVE_PROPERTY): + return PropertyType.Array + + return PropertyType.Other + + +func _process_line_into_array(csv_values: PackedStringArray, property_type: PropertyType) -> Array[String]: + const KEY_VALUE_INDEX := 0 + var values_as_array: Array[String] = [] + + for i in csv_values.size(): + + if i == KEY_VALUE_INDEX: + continue + + var csv_value := csv_values[i] + + if csv_value.is_empty(): + continue + + match property_type: + PropertyType.String: + values_as_array = [csv_value] + + PropertyType.Array: + var split_values := csv_value.split(",") + + for value in split_values: + values_as_array.append(value) + + return values_as_array + + +func _add_keys_to_glossary(glossary: DialogicGlossary, names: Array) -> void: + var glossary_prefix_key := glossary._get_glossary_translation_id_prefix() + var glossary_translation_id_prefix := _get_glossary_translation_key_prefix(glossary) + + for glossary_line: PackedStringArray in names: + + if glossary_line.is_empty(): + continue + + var csv_key := glossary_line[0] + + # CSV line separators will be empty. + if not csv_key.begins_with(glossary_prefix_key): + continue + + var value_type := _get_key_type(csv_key) + + # String and Array are the only valid types. + if (value_type == PropertyType.Other + or not csv_key.begins_with(glossary_translation_id_prefix)): + continue + + var new_line_to_add := _process_line_into_array(glossary_line, value_type) + + for name_to_add: String in new_line_to_add: + glossary._translation_keys[name_to_add.strip_edges()] = csv_key + + + +## Reads all [member lines] and adds them to the given [param glossary]'s +## internal collection of words-to-translation-key mappings. +## +## Populate the CSV's lines with the method [method collect_lines_from_glossary] +## before. +func add_translation_keys_to_glossary(glossary: DialogicGlossary) -> void: + glossary._translation_keys.clear() + _add_keys_to_glossary(glossary, lines) + _add_keys_to_glossary(glossary, old_lines.values()) + + +## Returns the translation key prefix for the given [param glossary_translation_id]. +## The resulting format will look like this: Glossary/a2/ +## You can use this to find entries in [member lines] that to a glossary. +func _get_glossary_translation_key_prefix(glossary: DialogicGlossary) -> String: + return ( + DialogicGlossary.RESOURCE_NAME + .path_join(glossary._translation_id) + ) + + +## Returns whether [param value_b] is greater than [param value_a]. +## +## This method helps to sort glossary entry properties by their importance +## matching the order in the editor. +## +## TODO: Allow Dialogic users to define their own order. +func _sort_glossary_entry_property_keys(property_key_a: String, property_key_b: String) -> bool: + const GLOSSARY_CSV_LINE_ORDER := { + DialogicGlossary.NAME_PROPERTY: 0, + DialogicGlossary.ALTERNATIVE_PROPERTY: 1, + DialogicGlossary.TEXT_PROPERTY: 2, + DialogicGlossary.EXTRA_PROPERTY: 3, + } + const UNKNOWN_PROPERTY_ORDER := 100 + + var value_a: int = GLOSSARY_CSV_LINE_ORDER.get(property_key_a, UNKNOWN_PROPERTY_ORDER) + var value_b: int = GLOSSARY_CSV_LINE_ORDER.get(property_key_b, UNKNOWN_PROPERTY_ORDER) + + return value_a < value_b + + +## Collects properties from glossary entries from the given [param glossary] and +## adds them to the [member lines]. +func collect_lines_from_glossary(glossary: DialogicGlossary) -> void: + + for glossary_value: Variant in glossary.entries.values(): + + if glossary_value is String: + continue + + var glossary_entry: Dictionary = glossary_value + var glossary_entry_name: String = glossary_entry[DialogicGlossary.NAME_PROPERTY] + + var _glossary_translation_id := glossary.get_set_glossary_translation_id() + var entry_translation_id := glossary.get_set_glossary_entry_translation_id(glossary_entry_name) + + var entry_property_keys := glossary_entry.keys().duplicate() + entry_property_keys.sort_custom(_sort_glossary_entry_property_keys) + + var entry_name_property: String = glossary_entry[DialogicGlossary.NAME_PROPERTY] + + for entry_key: String in entry_property_keys: + # Ignore private keys. + if entry_key.begins_with(DialogicGlossary.PRIVATE_PROPERTY_PREFIX): + continue + + var item_value: Variant = glossary_entry[entry_key] + var item_value_str := "" + + if item_value is Array: + var item_array := item_value as Array + # We use a space after the comma to make it easier to read. + item_value_str = " ,".join(item_array) + + elif not item_value is String or item_value.is_empty(): + continue + + else: + item_value_str = item_value + + var glossary_csv_key := glossary._get_glossary_translation_key(entry_translation_id, entry_key) + + if (entry_key == DialogicGlossary.NAME_PROPERTY + or entry_key == DialogicGlossary.ALTERNATIVE_PROPERTY): + glossary.entries[glossary_csv_key] = entry_name_property + + var glossary_line := PackedStringArray([glossary_csv_key, item_value_str]) + + lines.append(glossary_line) + + # New glossary item, if needed, add a separator. + if add_separator: + _append_empty() - # Add row for nicknames. - var nick_name_property := DialogicCharacter.TranslatedProperties.NICKNAMES - var nickname_string: String = ", ".join(character_nicknames) - var nickname_name_line_key: String = character.get_property_translation_key(nick_name_property) - var nick_array_line := PackedStringArray([nickname_name_line_key, nickname_string]) - lines.append(nick_array_line) ## Collects translatable events from the given [param timeline] and adds ## them to the [member lines]. -## -## If this is a timeline CSV file, func collect_lines_from_timeline(timeline: DialogicTimeline) -> void: - for event in timeline.events: + for event: DialogicEvent in timeline.events: if event.can_be_translated(): @@ -132,27 +295,31 @@ func collect_lines_from_timeline(timeline: DialogicTimeline) -> void: var properties: Array = event._get_translatable_properties() - for property in properties: + for property: String in properties: var line_key: String = event.get_property_translation_key(property) var line_value: String = event._get_property_original_translation(property) var array_line := PackedStringArray([line_key, line_value]) lines.append(array_line) - if not "character" in event: - continue - - var character: DialogicCharacter = event.character - - if (character != null - and not collected_characters.has(character._translation_id)): - collected_characters[character._translation_id] = character + # End of timeline, if needed, add a separator. + if add_separator: + _append_empty() ## Clears the CSV file on disk and writes the current [member lines] array to it. ## Uses the [member old_lines] dictionary to update existing translations. ## If a translation row misses a column, a trailing comma will be added to ## conform to the CSV file format. +## +## If the locale CSV line was collected only, a new file won't be created and +## already existing translations won't be updated. func update_csv_file_on_disk() -> void: + # None or locale row only. + if lines.size() < 2: + print_rich("[color=yellow]No lines for the CSV file, skipping: " + used_file_path) + + return + # Clear the current CSV file. file = FileAccess.open(used_file_path, FileAccess.WRITE) diff --git a/addons/dialogic/Editor/Settings/settings_general.gd b/addons/dialogic/Editor/Settings/settings_general.gd index 1252f57d..540ea3ba 100644 --- a/addons/dialogic/Editor/Settings/settings_general.gd +++ b/addons/dialogic/Editor/Settings/settings_general.gd @@ -41,7 +41,7 @@ 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) @@ -154,6 +154,8 @@ func _on_submit_extension_button_pressed() -> void: return [this_folder.path_join('event_"""+%NameEdit.text.to_snake_case()+""".gd')]\n\n""" file = FileAccess.open(extensions_folder.path_join('event_'+%NameEdit.text.to_snake_case()+'.gd'), FileAccess.WRITE) file.store_string( + +#region EXTENDED EVENT SCRIPT """@tool extends DialogicEvent class_name Dialogic"""+%NameEdit.text.to_pascal_case()+"""Event @@ -165,64 +167,71 @@ func _execute() -> void: finish() # called to continue with the next event +#region INITIALIZE ################################################################################ -## INITIALIZE -################################################################################ - # Set fixed settings of this event func _init() -> void: event_name = \""""+%NameEdit.text.capitalize()+"""\" event_category = "Other" \n -################################################################################ -## SAVING/LOADING +#endregion + +#region SAVING/LOADING ################################################################################ func get_shortcode() -> String: return \""""+%NameEdit.text.to_snake_case()+"""\" func get_shortcode_parameters() -> Dictionary: return { - #param_name : property_info + #param_name : property_info #"my_parameter" : {"property": "property", "default": "Default"}, } # You can alternatively overwrite these 3 functions: to_text(), from_text(), is_valid_event() +#endregion -################################################################################ -## EDITOR REPRESENTATION + +#region EDITOR REPRESENTATION ################################################################################ func build_event_editor() -> void: - pass""") + pass +#endregion +""") + +#endregion if mode != 0: # don't add subsystem in event only mode indexer_content += """func _get_subsystems() -> Array: return [{'name':'"""+%NameEdit.text.to_pascal_case()+"""', 'script':this_folder.path_join('subsystem_"""+%NameEdit.text.to_snake_case()+""".gd')}]""" file = FileAccess.open(extensions_folder.path_join('subsystem_'+%NameEdit.text.to_snake_case()+'.gd'), FileAccess.WRITE) file.store_string( + +# region EXTENDED SUBSYSTEM SCRIPT """extends DialogicSubsystem ## Describe the subsystems purpose here. -#################################################################################################### -## STATE +#region STATE #################################################################################################### -func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR): +func clear_game_state(clear_flag:=Dialogic.ClearFlags.FULL_CLEAR) -> void: pass func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void: pass +#endregion -#################################################################################################### -## MAIN METHODS + +#region MAIN METHODS #################################################################################################### # Add some useful methods here. +#endregion """) file = FileAccess.open(extensions_folder.path_join('index.gd'), FileAccess.WRITE) file.store_string(indexer_content) @@ -233,3 +242,7 @@ func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void: find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources() force_event_button_list_reload() + + +func _on_reload_pressed() -> void: + DialogicUtil._update_autoload_subsystem_access() diff --git a/addons/dialogic/Editor/Settings/settings_general.tscn b/addons/dialogic/Editor/Settings/settings_general.tscn index 51f73205..264427b7 100644 --- a/addons/dialogic/Editor/Settings/settings_general.tscn +++ b/addons/dialogic/Editor/Settings/settings_general.tscn @@ -2,9 +2,9 @@ [ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_general.gd" id="2"] [ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_kqhx5"] -[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="3_i7rug"] +[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="3_i7rug"] -[sub_resource type="Image" id="Image_e2a0f"] +[sub_resource type="Image" id="Image_e1gle"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -13,8 +13,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_wyypx"] -image = SubResource("Image_e2a0f") +[sub_resource type="ImageTexture" id="ImageTexture_4wgbv"] +image = SubResource("Image_e1gle") [node name="General" type="VBoxContainer"] anchors_preset = 15 @@ -35,7 +35,7 @@ text = "Color Palette" [node name="HintTooltip" parent="PaletteTitle" instance=ExtResource("2_kqhx5")] layout_mode = 2 tooltip_text = "These colors are used for the events." -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_4wgbv") hint_text = "These colors are used for the events." [node name="ResetColorsButton" type="Button" parent="PaletteTitle"] @@ -43,7 +43,7 @@ unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 tooltip_text = "Reset Colors to default" -icon = SubResource("ImageTexture_wyypx") +icon = SubResource("ImageTexture_4wgbv") flat = true [node name="ScrollContainer" type="ScrollContainer" parent="."] @@ -72,7 +72,7 @@ tooltip_text = "The layout scene configured in the Layout editor is automaticall instanced when calling Dialogic.start(). Depending on your game, you might want it to be deleted after the dialogue, be hidden (as reinstancing often is wasting resources) or kept visible. " -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_4wgbv") hint_text = "The layout scene configured in the Layout editor is automatically instanced when calling Dialogic.start(). Depending on your game, you might want it to be deleted after the dialogue, be hidden @@ -121,11 +121,16 @@ layout_mode = 2 tooltip_text = "Configure where dialogic looks for custom modules. You will have to restart the project to see the change take action." -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_4wgbv") hint_text = "Configure where dialogic looks for custom modules. You will have to restart the project to see the change take action." +[node name="Reload" type="Button" parent="HBoxContainer6/HBoxContainer4/HBoxContainer5"] +layout_mode = 2 +text = "Reload" +flat = true + [node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer6/HBoxContainer4"] layout_mode = 2 @@ -139,7 +144,7 @@ layout_mode = 2 size_flags_horizontal = 3 placeholder = "res://addons/dialogic_additions/Events" file_mode = 2 -resource_icon = SubResource("ImageTexture_wyypx") +resource_icon = SubResource("ImageTexture_4wgbv") [node name="VSeparator" type="VSeparator" parent="HBoxContainer6"] layout_mode = 2 @@ -164,7 +169,7 @@ text = "Extension Creator " [node name="HintTooltip" parent="HBoxContainer6/ExtensionsPanel/VBox/HBoxContainer6" instance=ExtResource("2_kqhx5")] layout_mode = 2 tooltip_text = "Use the Exension Creator to quickly setup custom modules!" -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_4wgbv") hint_text = "Use the Exension Creator to quickly setup custom modules!" [node name="CreateExtensionButton" type="Button" parent="HBoxContainer6/ExtensionsPanel/VBox"] @@ -228,7 +233,7 @@ text = "Timer processing" [node name="HintTooltip" parent="HBoxContainer7" instance=ExtResource("2_kqhx5")] layout_mode = 2 tooltip_text = "Change whether dialogics timers process in physics_process (frame-rate independent) or process." -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_4wgbv") hint_text = "Change whether dialogics timers process in physics_process (frame-rate independent) or process." [node name="HBoxContainer4" type="HBoxContainer" parent="."] @@ -256,7 +261,7 @@ text = "Section Order" [node name="HintTooltip" parent="HBoxContainer" instance=ExtResource("2_kqhx5")] layout_mode = 2 tooltip_text = "You can change the order of the event sections here. " -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_4wgbv") hint_text = "You can change the order of the event sections here. " [node name="SectionList" type="Tree" parent="."] @@ -272,6 +277,7 @@ hide_root = true drop_mode_flags = 1 [connection signal="item_selected" from="HBoxContainer3/LayoutNodeEndBehaviour" to="." method="_on_layout_node_end_behaviour_item_selected"] +[connection signal="pressed" from="HBoxContainer6/HBoxContainer4/HBoxContainer5/Reload" to="." method="_on_reload_pressed"] [connection signal="pressed" from="HBoxContainer6/ExtensionsPanel/VBox/CreateExtensionButton" to="." method="_on_create_extension_button_pressed"] [connection signal="pressed" from="HBoxContainer6/ExtensionsPanel/VBox/ExtensionCreator/SubmitExtensionButton" to="." method="_on_submit_extension_button_pressed"] [connection signal="button_clicked" from="SectionList" to="." method="_on_section_list_button_clicked"] diff --git a/addons/dialogic/Editor/Settings/settings_modules.gd b/addons/dialogic/Editor/Settings/settings_modules.gd index a870a692..4f44cc3f 100644 --- a/addons/dialogic/Editor/Settings/settings_modules.gd +++ b/addons/dialogic/Editor/Settings/settings_modules.gd @@ -110,7 +110,7 @@ func load_modules_tree() -> void: # 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) event_item.set_icon(0, get_theme_icon("Favorites", "EditorIcons")) diff --git a/addons/dialogic/Editor/Settings/settings_translation.gd b/addons/dialogic/Editor/Settings/settings_translation.gd index 4539e1e4..59419f0b 100644 --- a/addons/dialogic/Editor/Settings/settings_translation.gd +++ b/addons/dialogic/Editor/Settings/settings_translation.gd @@ -17,12 +17,18 @@ const DEFAULT_CHARACTER_CSV_NAME := "dialogic_character_translations.csv" ## Only used when all timelines are supposed to be translated in one file. const DEFAULT_TIMELINE_CSV_NAME := "dialogic_timeline_translations.csv" +const DEFAULT_GLOSSARY_CSV_NAME := "dialogic_glossary_translations.csv" -func _get_icon(): +const _USED_LOCALES_SETTING := "dialogic/translation/locales" + +## Contains translation changes that were made during the last update. + +## Unique locales that will be set after updating the CSV files. +var _unique_locales := [] + +func _get_icon() -> Texture2D: return get_theme_icon("Translation", "EditorIcons") -func _get_info_section() -> Control: - return $InfoSection func _is_feature_tab() -> bool: return true @@ -37,6 +43,7 @@ func _ready() -> void: %TestingLocale.resource_icon = get_theme_icon("Translation", "EditorIcons") %TestingLocale.value_changed.connect(store_changes) %TransFolderPicker.value_changed.connect(store_changes) + %AddSeparatorEnabled.toggled.connect(store_changes) %SaveLocationMode.item_selected.connect(store_changes) %TransMode.item_selected.connect(store_changes) @@ -71,13 +78,14 @@ func _refresh() -> void: %TransMode.select(ProjectSettings.get_setting('dialogic/translation/file_mode', 1)) %TransFolderPicker.set_value(ProjectSettings.get_setting('dialogic/translation/translation_folder', '')) %TestingLocale.set_value(ProjectSettings.get_setting('internationalization/locale/test', '')) + %AddSeparatorEnabled.button_pressed = ProjectSettings.get_setting('dialogic/translation/add_separator', false) _verify_translation_file() loading = false -func store_changes(fake_arg = "", fake_arg2 = "") -> void: +func store_changes(_fake_arg: Variant = null, _fake_arg2: Variant = null) -> void: if loading: return @@ -90,8 +98,10 @@ func store_changes(fake_arg = "", fake_arg2 = "") -> void: ProjectSettings.set_setting('dialogic/translation/translation_folder', %TransFolderPicker.current_value) ProjectSettings.set_setting('internationalization/locale/test', %TestingLocale.current_value) ProjectSettings.set_setting('dialogic/translation/save_mode', %SaveLocationMode.selected) + ProjectSettings.set_setting('dialogic/translation/add_separator', %AddSeparatorEnabled.button_pressed) ProjectSettings.save() + ## Checks whether the translation folder path is required. ## If it is, disables the "Update CSV files" button and shows a warning. ## @@ -125,12 +135,22 @@ func _verify_translation_file() -> void: %StatusMessage.text = status_message -func get_locales(filter:String) -> Dictionary: +func get_locales(_filter: String) -> Dictionary: var suggestions := {} suggestions['Default'] = {'value':'', 'tooltip':"Will use the fallback locale set in the project settings."} suggestions[TranslationServer.get_tool_locale()] = {'value':TranslationServer.get_tool_locale()} - for locale in TranslationServer.get_all_languages(): - suggestions[locale] = {'value':locale, 'tooltip':TranslationServer.get_language_name(locale)} + + var used_locales: Array = ProjectSettings.get_setting(_USED_LOCALES_SETTING, TranslationServer.get_all_languages()) + + for locale: String in used_locales: + var language_name := TranslationServer.get_language_name(locale) + + # Invalid locales return an empty String. + if language_name.is_empty(): + continue + + suggestions[locale] = { 'value': locale, 'tooltip': language_name } + return suggestions @@ -158,11 +178,96 @@ func _delete_and_update() -> void: update_csv_files() +## Creates or updates the glossary CSV files. +func _handle_glossary_translation( + csv_data: CsvUpdateData, + save_location_mode: SaveLocationModes, + translation_mode: TranslationModes, + translation_folder_path: String, + orig_locale: String) -> void: + + var glossary_csv: DialogicCsvFile = null + var glossary_paths: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + var add_separator_lines: bool = ProjectSettings.get_setting('dialogic/translation/add_separator', false) + + for glossary_path: String in glossary_paths: + + if glossary_csv == null: + var csv_name := "" + + # Get glossary CSV file name. + match translation_mode: + TranslationModes.PER_PROJECT: + csv_name = DEFAULT_GLOSSARY_CSV_NAME + + TranslationModes.PER_TIMELINE: + var glossary_name: String = glossary_path.trim_suffix('.tres') + var path_parts := glossary_name.split("/") + var file_name := path_parts[-1] + csv_name = "dialogic_" + file_name + '_translation.csv' + + var glossary_csv_path := "" + # Get glossary CSV file path. + match save_location_mode: + SaveLocationModes.INSIDE_TRANSLATION_FOLDER: + glossary_csv_path = translation_folder_path.path_join(csv_name) + + SaveLocationModes.NEXT_TO_TIMELINE: + glossary_csv_path = glossary_path.get_base_dir().path_join(csv_name) + + # Create or update glossary CSV file. + glossary_csv = DialogicCsvFile.new(glossary_csv_path, orig_locale, add_separator_lines) + + if (glossary_csv.is_new_file): + csv_data.new_glossaries += 1 + else: + csv_data.updated_glossaries += 1 + + var glossary: DialogicGlossary = load(glossary_path) + glossary_csv.collect_lines_from_glossary(glossary) + glossary_csv.add_translation_keys_to_glossary(glossary) + ResourceSaver.save(glossary) + + #If per-file mode is used, save this csv and begin a new one + if translation_mode == TranslationModes.PER_TIMELINE: + glossary_csv.update_csv_file_on_disk() + glossary_csv = null + + # If a Per-Project glossary is still open, we need to save it. + if glossary_csv != null: + glossary_csv.update_csv_file_on_disk() + glossary_csv = null + + +## Keeps information about the amount of new and updated CSV rows and what +## resources were populated with translation IDs. +## The final data can be used to display a status message. +class CsvUpdateData: + var new_events := 0 + var updated_events := 0 + + var new_timelines := 0 + var updated_timelines := 0 + + var new_names := 0 + var updated_names := 0 + + var new_glossaries := 0 + var updated_glossaries := 0 + + var new_glossary_entries := 0 + var updated_glossary_entries := 0 + + func update_csv_files() -> void: + _unique_locales = [] var orig_locale: String = ProjectSettings.get_setting('dialogic/translation/original_locale', '').strip_edges() var save_location_mode: SaveLocationModes = ProjectSettings.get_setting('dialogic/translation/save_mode', SaveLocationModes.NEXT_TO_TIMELINE) var translation_mode: TranslationModes = ProjectSettings.get_setting('dialogic/translation/file_mode', TranslationModes.PER_PROJECT) var translation_folder_path: String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://') + var add_separator_lines: bool = ProjectSettings.get_setting('dialogic/translation/add_separator', false) + + var csv_data := CsvUpdateData.new() if orig_locale.is_empty(): orig_locale = ProjectSettings.get_setting('internationalization/locale/fallback') @@ -171,38 +276,23 @@ func update_csv_files() -> void: ProjectSettings.set_setting('dialogic/translation/intern/file_mode', translation_mode) ProjectSettings.set_setting('dialogic/translation/intern/translation_folder', translation_folder_path) - var new_events := 0 - var new_timelines := 0 - var updated_events := 0 - var updated_timelines := 0 - var new_names := 0 - var updated_names := 0 - var current_timeline := _close_active_timeline() var csv_per_project: DialogicCsvFile = null var per_project_csv_path := translation_folder_path.path_join(DEFAULT_TIMELINE_CSV_NAME) if translation_mode == TranslationModes.PER_PROJECT: - csv_per_project = DialogicCsvFile.new(per_project_csv_path, orig_locale) + csv_per_project = DialogicCsvFile.new(per_project_csv_path, orig_locale, add_separator_lines) if (csv_per_project.is_new_file): - new_timelines += 1 + csv_data.new_timelines += 1 else: - updated_timelines += 1 - - var names_csv_path := translation_folder_path.path_join(DEFAULT_CHARACTER_CSV_NAME) - var character_name_csv: DialogicCsvFile = DialogicCsvFile.new(names_csv_path, orig_locale) - - if (character_name_csv.is_new_file): - new_timelines += 1 - else: - updated_timelines += 1 + csv_data.updated_timelines += 1 # Iterate over all timelines. # Create or update CSV files. # Transform the timeline into translatable lines and collect into the CSV file. - for timeline_path in DialogicResourceUtil.list_resources_of_type('.dtl'): + for timeline_path: String in DialogicResourceUtil.list_resources_of_type('.dtl'): var csv_file: DialogicCsvFile = csv_per_project # Swap the CSV file to the Per Timeline one. @@ -218,17 +308,20 @@ func update_csv_files() -> void: per_timeline_path += '_translation.csv' - csv_file = DialogicCsvFile.new(per_timeline_path, orig_locale) - new_timelines += 1 + csv_file = DialogicCsvFile.new(per_timeline_path, orig_locale, false) + csv_data.new_timelines += 1 # Load and process timeline, turn events into resources. var timeline: DialogicTimeline = load(timeline_path) + + if timeline.events.size() == 0: + print_rich("[color=yellow]Empty timeline, skipping: " + timeline_path + "[/color]") + continue + timeline.process() # Collect timeline into CSV. csv_file.collect_lines_from_timeline(timeline) - var characters := csv_file.collected_characters - character_name_csv.collect_lines_from_characters(characters) # in case new translation_id's were added, we save the timeline again timeline.set_meta("timeline_not_saved", true) @@ -237,41 +330,87 @@ func update_csv_files() -> void: if translation_mode == TranslationModes.PER_TIMELINE: csv_file.update_csv_file_on_disk() - new_events += csv_file.new_rows - updated_events += csv_file.updated_rows + csv_data.new_events += csv_file.new_rows + csv_data.updated_events += csv_file.updated_rows - character_name_csv.update_csv_file_on_disk() + _handle_glossary_translation( + csv_data, + save_location_mode, + translation_mode, + translation_folder_path, + orig_locale + ) + + _handle_character_names( + csv_data, + orig_locale, + translation_folder_path, + add_separator_lines + ) if translation_mode == TranslationModes.PER_PROJECT: csv_per_project.update_csv_file_on_disk() - if character_name_csv.is_new_file: - new_timelines += 1 - else: - updated_timelines += 1 - - new_names += character_name_csv.new_rows - updated_names += character_name_csv.updated_rows - _silently_open_timeline(current_timeline) # Trigger reimport. find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources() - var status_message := "Events created {new_events} updated {updated_events} - Names created {new_names} updated {updated_names} - CSVs created {new_timelines} updated {updated_timelines}" + var status_message := "Events created {new_events} found {updated_events} + Names created {new_names} found {updated_names} + CSVs created {new_timelines} found {updated_timelines} + Glossary created {new_glossaries} found {updated_glossaries} + Entries created {new_glossary_entries} found {updated_glossary_entries}" var status_message_args := { - 'new_events': new_events, - 'updated_events': updated_events, - 'new_timelines': new_timelines, - 'updated_timelines': updated_timelines, - 'new_names': new_names, - 'updated_names': updated_names, + 'new_events': csv_data.new_events, + 'updated_events': csv_data.updated_events, + 'new_timelines': csv_data.new_timelines, + 'updated_timelines': csv_data.updated_timelines, + 'new_glossaries': csv_data.new_glossaries, + 'updated_glossaries': csv_data.updated_glossaries, + 'new_names': csv_data.new_names, + 'updated_names': csv_data.updated_names, + 'new_glossary_entries': csv_data.new_glossary_entries, + 'updated_glossary_entries': csv_data.updated_glossary_entries, } %StatusMessage.text = status_message.format(status_message_args) + ProjectSettings.set_setting(_USED_LOCALES_SETTING, _unique_locales) + + +## Iterates over all character resource files and creates or updates CSV files +## that contain the translations for character properties. +## This will save each character resource file to disk. +func _handle_character_names( + csv_data: CsvUpdateData, + original_locale: String, + translation_folder_path: String, + add_separator_lines: bool) -> void: + var names_csv_path := translation_folder_path.path_join(DEFAULT_CHARACTER_CSV_NAME) + var character_name_csv: DialogicCsvFile = DialogicCsvFile.new(names_csv_path, + original_locale, + add_separator_lines + ) + + var all_characters := {} + + for character_path: String in DialogicResourceUtil.list_resources_of_type('.dch'): + var character: DialogicCharacter = load(character_path) + + if character._translation_id.is_empty(): + csv_data.new_names += 1 + + else: + csv_data.updated_names += 1 + + var translation_id := character.get_set_translation_id() + all_characters[translation_id] = character + + ResourceSaver.save(character) + + character_name_csv.collect_lines_from_characters(all_characters) + character_name_csv.update_csv_file_on_disk() func collect_translations() -> void: @@ -280,9 +419,9 @@ func collect_translations() -> void: if translation_mode == TranslationModes.PER_TIMELINE: - for timeline_path in DialogicResourceUtil.list_resources_of_type('.translation'): + for timeline_path: String in DialogicResourceUtil.list_resources_of_type('.translation'): - for file in DialogicUtil.listdir(timeline_path.get_base_dir()): + for file: String in DialogicUtil.listdir(timeline_path.get_base_dir()): file = timeline_path.get_base_dir().path_join(file) if file.ends_with('.translation'): @@ -293,7 +432,7 @@ func collect_translations() -> void: if translation_mode == TranslationModes.PER_PROJECT: var translation_folder: String = ProjectSettings.get_setting('dialogic/translation/translation_folder', 'res://') - for file in DialogicUtil.listdir(translation_folder): + for file: String in DialogicUtil.listdir(translation_folder): file = translation_folder.path_join(file) if file.ends_with('.translation'): @@ -308,9 +447,9 @@ func collect_translations() -> void: var found_file_paths := [] var removed_translation_files := 0 - for file_path in translation_files: + 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 @@ -319,6 +458,11 @@ 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 locale_part := path_without_suffix.split(".")[-1] + _collect_locale(locale_part) + + var valid_translation_files := PackedStringArray(all_translation_files) ProjectSettings.set_setting('internationalization/locale/translations', valid_translation_files) ProjectSettings.save() @@ -333,26 +477,6 @@ func _on_erase_translations_pressed() -> void: %EraseConfirmationDialog.popup_centered() -## Deletes the Per-Project CSV file and the character name CSV file. -## Returns `true` on success. -func delete_per_project_csv(translation_folder: String) -> bool: - var per_project_csv := translation_folder.path_join(DEFAULT_TIMELINE_CSV_NAME) - - if FileAccess.file_exists(per_project_csv): - - if OK == DirAccess.remove_absolute(per_project_csv): - print_rich("[color=green]Deleted Per-Project timeline CSV file: " + per_project_csv + "[/color]") - - # Delete the timeline CSV import file. - DirAccess.remove_absolute(per_project_csv + '.import') - return true - - else: - print_rich("[color=yellow]Failed to delete Per-Project timeline CSV file: " + per_project_csv + "[/color]") - - return false - - ## Deletes translation files generated by [param csv_name]. ## The [param csv_name] may not contain the file extension (.csv). ## @@ -361,7 +485,7 @@ func delete_per_project_csv(translation_folder: String) -> bool: func delete_translations_files(translation_files: Array, csv_name: String) -> int: var deleted_files := 0 - for file_path in DialogicResourceUtil.list_resources_of_type('.translation'): + for file_path: String in DialogicResourceUtil.list_resources_of_type('.translation'): var base_name: String = file_path.get_basename() var path_parts := base_name.split("/") var translation_name: String = path_parts[-1] @@ -387,18 +511,21 @@ func delete_translations_files(translation_files: Array, csv_name: String) -> in ## translation IDs. ## Deletes the Per-Project CSV file and the character name CSV file. func erase_translations() -> void: - var translation_files := Array(ProjectSettings.get_setting('internationalization/locale/translations', [])) + var files: PackedStringArray = ProjectSettings.get_setting('internationalization/locale/translations', []) + var translation_files := Array(files) + ProjectSettings.set_setting(_USED_LOCALES_SETTING, []) var deleted_csv_files := 0 var deleted_translation_files := 0 var cleaned_timelines := 0 var cleaned_characters := 0 var cleaned_events := 0 + var cleaned_glossaries := 0 var current_timeline := _close_active_timeline() # Delete all Dialogic CSV files and their translation files. - for csv_path in DialogicResourceUtil.list_resources_of_type(".csv"): + for csv_path: String in DialogicResourceUtil.list_resources_of_type(".csv"): var csv_path_parts: PackedStringArray = csv_path.split("/") var csv_name: String = csv_path_parts[-1].trim_suffix(".csv") @@ -416,7 +543,7 @@ func erase_translations() -> void: print_rich("[color=yellow]Failed to delete CSV file: " + csv_path + "[/color]") # Clean timelines. - for timeline_path in DialogicResourceUtil.list_resources_of_type(".dtl"): + for timeline_path: String in DialogicResourceUtil.list_resources_of_type(".dtl"): # Process the timeline. var timeline: DialogicTimeline = load(timeline_path) @@ -424,7 +551,7 @@ func erase_translations() -> void: cleaned_timelines += 1 # Remove event translation IDs. - for event in timeline.events: + for event: DialogicEvent in timeline.events: if event._translation_id and not event._translation_id.is_empty(): event.remove_translation_id() @@ -442,15 +569,19 @@ func erase_translations() -> void: timeline.set_meta("timeline_not_saved", true) ResourceSaver.save(timeline, timeline_path) + _erase_glossary_translation_ids() + _erase_character_name_translation_ids() + ProjectSettings.set_setting('dialogic/translation/id_counter', 16) ProjectSettings.set_setting('internationalization/locale/translations', PackedStringArray(translation_files)) ProjectSettings.save() find_parent('EditorView').plugin_reference.get_editor_interface().get_resource_filesystem().scan_sources() - var status_message := "Timelines found {cleaned_timelines} + var status_message := "Timelines cleaned {cleaned_timelines} Events cleaned {cleaned_events} Characters cleaned {cleaned_characters} + Glossaries cleaned {cleaned_glossaries} CSVs erased {erased_csv_files} Translations erased {erased_translation_files}" @@ -459,6 +590,7 @@ func erase_translations() -> void: 'cleaned_timelines': cleaned_timelines, 'cleaned_characters': cleaned_characters, 'cleaned_events': cleaned_events, + 'cleaned_glossaries': cleaned_glossaries, 'erased_csv_files': deleted_csv_files, 'erased_translation_files': deleted_translation_files, } @@ -477,6 +609,27 @@ func erase_translations() -> void: %StatusMessage.text = status_message.format(status_message_args) +func _erase_glossary_translation_ids() -> void: + # Clean glossary. + var glossary_paths: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + + for glossary_path: String in glossary_paths: + var glossary: DialogicGlossary = load(glossary_path) + glossary.remove_translation_id() + glossary.remove_entry_translation_ids() + glossary.clear_translation_keys() + ResourceSaver.save(glossary, glossary_path) + print_rich("[color=green]Cleaned up glossary file: " + glossary_path + "[/color]") + + +func _erase_character_name_translation_ids() -> void: + for character_path: String in DialogicResourceUtil.list_resources_of_type('.dch'): + var character: DialogicCharacter = load(character_path) + + character.remove_translation_id() + ResourceSaver.save(character) + + ## Closes the current timeline in the Dialogic Editor and returns the timeline ## as a resource. ## If no timeline has been opened, returns null. @@ -496,3 +649,12 @@ func _close_active_timeline() -> Resource: func _silently_open_timeline(timeline_to_open: Resource) -> void: if timeline_to_open != null: settings_editor.editors_manager.edit_resource(timeline_to_open, true, true) + + +## Checks [param locale] for unique locales that have not been added +## to the [_unique_locales] array yet. +func _collect_locale(locale: String) -> void: + if _unique_locales.has(locale): + return + + _unique_locales.append(locale) diff --git a/addons/dialogic/Editor/Settings/settings_translation.tscn b/addons/dialogic/Editor/Settings/settings_translation.tscn index 0da6cbb3..61fca18f 100644 --- a/addons/dialogic/Editor/Settings/settings_translation.tscn +++ b/addons/dialogic/Editor/Settings/settings_translation.tscn @@ -2,10 +2,10 @@ [ext_resource type="Script" path="res://addons/dialogic/Editor/Settings/settings_translation.gd" id="1_dvmyi"] [ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_k2lou"] -[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3_dq4j2"] -[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="4_kvsma"] +[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/field_options_dynamic.tscn" id="3_dq4j2"] +[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/field_file.tscn" id="4_kvsma"] -[sub_resource type="Image" id="Image_e2a0f"] +[sub_resource type="Image" id="Image_g2hic"] data = { "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), "format": "RGBA8", @@ -14,8 +14,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_wyypx"] -image = SubResource("Image_e2a0f") +[sub_resource type="ImageTexture" id="ImageTexture_xbph7"] +image = SubResource("Image_g2hic") [node name="Translations" type="VBoxContainer"] anchors_preset = 15 @@ -73,9 +73,11 @@ text = "Testing locale" layout_mode = 2 tooltip_text = "Change this locale to test your game in a different language (only in-editor). Equivalent of the testing local project setting. " -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_xbph7") hint_text = "Change this locale to test your game in a different language (only in-editor). -Equivalent of the testing local project setting. " +Equivalent of the testing local project setting. + +Update dropdown list via \"Collect Translation\"." [node name="TestingLocale" parent="HBox/Testing/VBox3" instance=ExtResource("3_dq4j2")] unique_name_in_owner = true @@ -111,7 +113,7 @@ text = "Default locale" [node name="HintTooltip" parent="TranslationSettings/VBoxContainer/Grid/VBox" instance=ExtResource("2_k2lou")] layout_mode = 2 tooltip_text = "The locale of the language your timelines are written in." -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_xbph7") hint_text = "The locale of the language your timelines are written in." [node name="OrigLocale" parent="TranslationSettings/VBoxContainer/Grid" instance=ExtResource("3_dq4j2")] @@ -129,7 +131,7 @@ text = "Translation folder" layout_mode = 2 tooltip_text = "Choose a folder to let Dialogic save CSV files in. Also used when saving \"Inside Translation Folder\"" -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_xbph7") hint_text = "Choose a folder to let Dialogic save CSV files in. Also used when saving \"Inside Translation Folder\"" @@ -142,58 +144,68 @@ file_mode = 2 [node name="VBox2" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"] layout_mode = 2 -[node name="Label2" type="Label" parent="TranslationSettings/VBoxContainer/Grid/VBox2"] +[node name="OutputModeLabel" type="Label" parent="TranslationSettings/VBoxContainer/Grid/VBox2"] layout_mode = 2 -text = "Timeline mode" +text = "Output mode" -[node name="HintTooltip2" parent="TranslationSettings/VBoxContainer/Grid/VBox2" instance=ExtResource("2_k2lou")] +[node name="OutputModeTooltip" parent="TranslationSettings/VBoxContainer/Grid/VBox2" instance=ExtResource("2_k2lou")] layout_mode = 2 -tooltip_text = "Decides how many timeline CSV files will be created. +tooltip_text = "Decides how many CSV files will be created. + +• \"Per Type\": Uses one CSV file for each type of resource: Timelines, characters, and glossaries. +For example, 10 timelines will be combined into 1 CSV file. + +• \"Per File\": Uses one CSV file for each resource file. +For example, 10 timelines will result in 10 CSV files. -\"Per Project\": Uses one CSV file for all timelines (e.g. 30 timelines, 1 CSV) -\"Per Timeline\": Uses one CSV file for each timeline (e.g. 30 timelines, 30 CSVs) +The \"Per File\" option utilises \"Output location\", in contrast, the \"Per Type\" will always use the Translation folder." +texture = SubResource("ImageTexture_xbph7") +hint_text = "Decides how many CSV files will be created. -The \"Per Timeline\" option utilises \"Timeline save location\"." -texture = SubResource("ImageTexture_wyypx") -hint_text = "Decides how many timeline CSV files will be created. +• \"Per Type\": Uses one CSV file for each type of resource: Timelines, characters, and glossaries. +For example, 10 timelines will be combined into 1 CSV file. -\"Per Project\": Uses one CSV file for all timelines (e.g. 30 timelines, 1 CSV) -\"Per Timeline\": Uses one CSV file for each timeline (e.g. 30 timelines, 30 CSVs) +• \"Per File\": Uses one CSV file for each resource file. +For example, 10 timelines will result in 10 CSV files. -The \"Per Timeline\" option utilises \"Timeline save location\"." +The \"Per File\" option utilises \"Output location\", in contrast, the \"Per Type\" will always use the Translation folder." [node name="TransMode" type="OptionButton" parent="TranslationSettings/VBoxContainer/Grid"] unique_name_in_owner = true layout_mode = 2 item_count = 2 selected = 0 -popup/item_0/text = "Per Project" +popup/item_0/text = "Per Type" popup/item_0/id = 0 -popup/item_1/text = "Per Timeline" +popup/item_1/text = "Per File" popup/item_1/id = 1 -[node name="SaveLocation" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"] +[node name="OutputLocation" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"] layout_mode = 2 -[node name="Label2" type="Label" parent="TranslationSettings/VBoxContainer/Grid/SaveLocation"] +[node name="OutputLocationLabel" type="Label" parent="TranslationSettings/VBoxContainer/Grid/OutputLocation"] layout_mode = 2 -text = "Timeline location" +text = "Output location" -[node name="HintTooltip2" parent="TranslationSettings/VBoxContainer/Grid/SaveLocation" instance=ExtResource("2_k2lou")] +[node name="OutputLocationTooltip" parent="TranslationSettings/VBoxContainer/Grid/OutputLocation" instance=ExtResource("2_k2lou")] layout_mode = 2 -tooltip_text = "Decides where to save the generated timeline CSV files. +tooltip_text = "Decides where to save the generated CSV files. -- \"Inside Translation Folder\": Uses the \"Translation folder\". -- \"Next To Timeline\": Places them in the timeline's folder. +• \"Inside Translation Folder\": Uses the \"Translation folder\". -This button requires the \"Per Timeline\" timeline mode." -texture = SubResource("ImageTexture_wyypx") -hint_text = "Decides where to save the generated timeline CSV files. +• \"Next To Timeline\": Places them in the resource type's folder. -- \"Inside Translation Folder\": Uses the \"Translation folder\". -- \"Next To Timeline\": Places them in the timeline's folder. +This button requires the \"Per File\" Output mode. +A resource type can be: Timelines, characters, and glossaries." +texture = SubResource("ImageTexture_xbph7") +hint_text = "Decides where to save the generated CSV files. -This button requires the \"Per Timeline\" timeline mode." +• \"Inside Translation Folder\": Uses the \"Translation folder\". + +• \"Next To Timeline\": Places them in the resource type's folder. + +This button requires the \"Per File\" Output mode. +A resource type can be: Timelines, characters, and glossaries." [node name="SaveLocationMode" type="OptionButton" parent="TranslationSettings/VBoxContainer/Grid"] unique_name_in_owner = true @@ -203,10 +215,32 @@ item_count = 2 selected = 0 popup/item_0/text = "Inside Translation Folder" popup/item_0/id = 0 -popup/item_1/text = "Next to Timeline" +popup/item_1/text = "Next to File" popup/item_1/id = 1 [node name="Control" type="Control" parent="TranslationSettings/VBoxContainer/Grid"] +visible = false +layout_mode = 2 + +[node name="AddSeparatorHBox" type="HBoxContainer" parent="TranslationSettings/VBoxContainer/Grid"] +layout_mode = 2 + +[node name="AddSeparatorLabel" type="Label" parent="TranslationSettings/VBoxContainer/Grid/AddSeparatorHBox"] +layout_mode = 2 +text = "Add Separator Lines" + +[node name="HintAddSeparatorEnabled" parent="TranslationSettings/VBoxContainer/Grid/AddSeparatorHBox" instance=ExtResource("2_k2lou")] +layout_mode = 2 +tooltip_text = "Adds an empty line into per-project CSVs to differentiate between sections. + +For example, when a new glossary item or timeline starts, an empty line will be added." +texture = SubResource("ImageTexture_xbph7") +hint_text = "Adds an empty line into per-project CSVs to differentiate between sections. + +For example, when a new glossary item or timeline starts, an empty line will be added." + +[node name="AddSeparatorEnabled" type="CheckBox" parent="TranslationSettings/VBoxContainer/Grid"] +unique_name_in_owner = true layout_mode = 2 [node name="HSeparator6" type="VSeparator" parent="TranslationSettings"] @@ -233,13 +267,16 @@ unique_name_in_owner = true layout_mode = 2 disabled = true text = "Update CSV files" +icon = SubResource("ImageTexture_xbph7") [node name="HintTooltip5" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")] layout_mode = 2 tooltip_text = "This button will scan all timelines and generate or update their CSV files. +A Dialogic CSV file will be prefixed with \"dialogic_\". + This action will be disabled if the \"Translation folder\" is missing or has an invalid path." -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_xbph7") hint_text = "This button will scan all timelines and generate or update their CSV files. A Dialogic CSV file will be prefixed with \"dialogic_\". @@ -250,13 +287,14 @@ This action will be disabled if the \"Translation folder\" is missing or has an unique_name_in_owner = true layout_mode = 2 text = "Collect translations" +icon = SubResource("ImageTexture_xbph7") [node name="HintTooltip6" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")] layout_mode = 2 tooltip_text = "Godot imports CSV files as \".translation\" files. -This buttons adds them to \"Project Settings > Localization\". +This buttons adds them to \"Project Settings -> Localization\". " -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_xbph7") hint_text = "Godot imports CSV files as \".translation\" files. This buttons adds them to \"Project Settings -> Localization\". " @@ -273,14 +311,17 @@ layout_mode = 2 unique_name_in_owner = true layout_mode = 2 text = "Remove translations" +icon = SubResource("ImageTexture_xbph7") [node name="HintTooltip7" parent="TranslationSettings/VBoxContainer2/Actions" instance=ExtResource("2_k2lou")] layout_mode = 2 tooltip_text = "Be very careful with this button! It will try to delete any \".csv\" and \".translation\" files that are related to Dialogic. +CSV and translation files prefixed with \"dialogic_\" are treated as Dialogic-related. + Removes translation IDs (eg. #id:33) from timelines and characters." -texture = SubResource("ImageTexture_wyypx") +texture = SubResource("ImageTexture_xbph7") hint_text = "Be very careful with this button! It will try to delete any \".csv\" and \".translation\" files that are related to Dialogic. @@ -291,62 +332,7 @@ Removes translation IDs (eg. #id:33) from timelines and characters." [node name="StatusMessage" type="Label" parent="TranslationSettings/VBoxContainer2"] unique_name_in_owner = true layout_mode = 2 -text = "⛔ Cannot update CSVs files! - Requires valid translation folder to translate character names and the project CSV file." -autowrap_mode = 3 - -[node name="InfoSection" type="VBoxContainer" parent="."] -layout_mode = 2 - -[node name="Setup" type="VBoxContainer" parent="InfoSection"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="SetupTitle" type="Label" parent="InfoSection/Setup"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"DialogicSection" -text = "Setting up translations" - -[node name="HowToTranslateLabel" type="Label" parent="InfoSection/Setup"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "1. Enable translations -2. Set the language you use in your timelines as \"Default timeline\". -3. Select \"File mode\" and \"Save location\". -4. Hit \"Update CSV files\": This creates new CSV files from your timelines. -5. Wait until reimport is finished. Then click \"Collect translations\"." -autowrap_mode = 3 - -[node name="HSeparator" type="HSeparator" parent="InfoSection"] -layout_mode = 2 - -[node name="Workflow" type="VBoxContainer" parent="InfoSection"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="WorkflowTitle" type="Label" parent="InfoSection/Workflow"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"DialogicSection" -text = "How to work with translations" - -[node name="HowToUseTtranslationLabel" type="Label" parent="InfoSection/Workflow"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Editing: -You can edit the CSV files in most editors (like Google Sheets or LibreOffice Calc). -Create new languages by adding a new column with a valid locale in the first row. - -Updating: -If you made changes to your timelines, hit \"Update CSV files\" again. -If you added a new locale to the CSV, hit \"Update CSV files\" and \"Collect translation\". - -Testing: -You can test languages by selecting the testing locale. -While playing, you can use TranslationServer.set_locale(\"your_locale\") - -" +text = "⛔ Requires valid translation folder to translate character names and the project CSV file." autowrap_mode = 3 [node name="UpdateConfirmationDialog" type="ConfirmationDialog" parent="."] @@ -369,9 +355,9 @@ dialog_text = "You are about to: - Delete all CSVs prefixed with \"dialogic_\". - Delete the related CSV import files. - Delete the related translation files. -- Removes translation IDs from timelines and characters. -- Remove all \"dialogic\" prefixed translations from - \"Project Settings -> Localization\"." +- Remove translation IDs from timelines and characters. +- Remove all \"dialogic\" prefixed translations from \"Project Settings -> Localization\". +- Remove the \"_translation_keys\" and \"entries\" starting with \"Glossary/\"." dialog_autowrap = true [node name="AspectRatioContainer" type="AspectRatioContainer" parent="."] diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd index bf9b2cfb..7d3b92a2 100644 --- a/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/CodeCompletionHelper.gd @@ -3,8 +3,8 @@ extends Node enum Modes {TEXT_EVENT_ONLY, FULL_HIGHLIGHTING} -var syntax_highlighter :SyntaxHighlighter = load("res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd").new() -var text_syntax_highlighter :SyntaxHighlighter = load("res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd").new() +var syntax_highlighter: SyntaxHighlighter = load("res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd").new() +var text_syntax_highlighter: SyntaxHighlighter = load("res://addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd").new() # These RegEx's are used to deduce information from the current line for auto-completion @@ -29,8 +29,7 @@ func _ready(): text_syntax_highlighter.mode = text_syntax_highlighter.Modes.TEXT_EVENT_ONLY -################################################################################ -## AUTO COMPLETION +#region AUTO COMPLETION ################################################################################ # Helper that gets the current line with a special character where the caret is @@ -65,7 +64,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG # make sure shortcode event references are loaded if mode == Modes.FULL_HIGHLIGHTING: - var hidden_events :Array= DialogicUtil.get_editor_setting('hidden_event_buttons', []) + var hidden_events: Array = DialogicUtil.get_editor_setting('hidden_event_buttons', []) if shortcode_events.is_empty(): for event in DialogicResourceUtil.get_event_cache(): if event.get_shortcode() != 'default_shortcode': @@ -84,7 +83,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG var line := get_code_completion_line(text) var word := get_code_completion_word(text) var symbol := get_code_completion_prev_symbol(text) - + var line_part := get_line_untill_caret(line) ## Note on use of KIND types for options. # These types are mostly useless for us. @@ -103,7 +102,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG # The completion will check if the letter is already present and add it otherwise. # Shortcode event suggestions - if line.begins_with('[') and !text_event.text_effects_regex.search(line.get_slice(' ', 0)) and mode == Modes.FULL_HIGHLIGHTING: + if mode == Modes.FULL_HIGHLIGHTING and syntax_highlighter.line_is_shortcode_event(text.get_caret_line()): if symbol == '[': # suggest shortcodes if a shortcode event has just begun var shortcodes := shortcode_events.keys() @@ -116,7 +115,8 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG else: text.add_code_completion_option(CodeEdit.KIND_MEMBER, shortcode, shortcode+" ", shortcode_events[shortcode].event_color.lerp(syntax_highlighter.normal_color, 0.3), shortcode_events[shortcode]._get_icon()) else: - var current_shortcode := completion_shortcode_getter_regex.search(line) + var full_event_text: String = syntax_highlighter.get_full_event(text.get_caret_line()) + var current_shortcode := completion_shortcode_getter_regex.search(full_event_text) if !current_shortcode: text.update_code_completion_options(false) return @@ -127,10 +127,10 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG return # suggest parameters - if symbol == ' ': - var parameters :Array = shortcode_events[code].get_shortcode_parameters().keys() + if symbol == ' ' and line.count('"')%2 == 0: + var parameters: Array = shortcode_events[code].get_shortcode_parameters().keys() for param in parameters: - if !param+'=' in line: + if !param+'=' in full_event_text: 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 @@ -152,7 +152,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG text.update_code_completion_options(true) return - var suggestions : Dictionary= shortcode_events[code].get_shortcode_parameters()[current_parameter]['suggestions'].call() + 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), '" ') @@ -169,7 +169,7 @@ func request_code_completion(force:bool, text:CodeEdit, mode:=Modes.FULL_HIGHLIG if mode == Modes.TEXT_EVENT_ONLY and !event is DialogicTextEvent: continue - if ! ' ' in line: + if ! ' ' in line_part: event._get_start_code_completion(self, text) if event.is_valid_event(line): @@ -199,8 +199,8 @@ func suggest_timelines(text:CodeEdit, type := CodeEdit.KIND_MEMBER, color:=Color func suggest_labels(text:CodeEdit, timeline:String='', end:='', color:=Color()) -> void: - if timeline in Engine.get_main_loop().get_meta('dialogic_label_directory', {}): - for i in Engine.get_main_loop().get_meta('dialogic_label_directory')[timeline]: + if timeline in DialogicResourceUtil.get_label_cache(): + for i in DialogicResourceUtil.get_label_cache()[timeline]: text.add_code_completion_option(CodeEdit.KIND_MEMBER, i, i+end, color, load("res://addons/dialogic/Modules/Jump/icon_label.png")) @@ -265,8 +265,9 @@ func confirm_code_completion(replace:bool, text:CodeEdit) -> void: text.set_caret_column(text.get_caret_column()+1) -################################################################################ -## SYMBOL CLICKING +#endregion + +#region SYMBOL CLICKING ################################################################################ # Performs an action (like opening a link) when a valid symbol was clicked @@ -274,6 +275,10 @@ func symbol_lookup(symbol:String, line:int, column:int) -> void: if symbol in shortcode_events.keys(): if !shortcode_events[symbol].help_page_path.is_empty(): OS.shell_open(shortcode_events[symbol].help_page_path) + if symbol in DialogicResourceUtil.get_character_directory(): + EditorInterface.edit_resource(DialogicResourceUtil.get_resource_from_identifier(symbol, 'dch')) + if symbol in DialogicResourceUtil.get_timeline_directory(): + EditorInterface.edit_resource(DialogicResourceUtil.get_resource_from_identifier(symbol, 'dtl')) # Called to test if a symbol can be clicked @@ -281,3 +286,9 @@ func symbol_validate(symbol:String, text:CodeEdit) -> void: if symbol in shortcode_events.keys(): if !shortcode_events[symbol].help_page_path.is_empty(): text.set_symbol_lookup_word_as_valid(true) + if symbol in DialogicResourceUtil.get_character_directory(): + text.set_symbol_lookup_word_as_valid(true) + if symbol in DialogicResourceUtil.get_timeline_directory(): + text.set_symbol_lookup_word_as_valid(true) + +#endregion diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd index ea13e371..56f6b83b 100644 --- a/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/syntax_highlighter.gd @@ -64,15 +64,19 @@ func _get_line_syntax_highlighting(line:int) -> Dictionary: dict = color_translation_id(dict, str_line) if mode == Modes.FULL_HIGHLIGHTING: - if str_line.strip_edges().begins_with("[") and !text_event.text_effects_regex.search(str_line.get_slice(' ', 0)): - var result:= shortcode_regex.search(str_line) + if line_is_shortcode_event(line): + var full_event := get_full_event(line) + var result := shortcode_regex.search(full_event) if result: if result.get_string('id') in shortcode_events: - dict[result.get_start('id')] = {"color":shortcode_events[result.get_string('id')].event_color.lerp(normal_color, 0.4)} - dict[result.get_end('id')] = {"color":normal_color} - - if result.get_string('args'): - color_shortcode_content(dict, str_line, result.get_start('args'), result.get_end('args'), shortcode_events[result.get_string('id')].event_color) + if full_event.begins_with(str_line): + dict[result.get_start('id')] = {"color":shortcode_events[result.get_string('id')].event_color.lerp(normal_color, 0.4)} + dict[result.get_end('id')] = {"color":normal_color} + + if result.get_string('args'): + color_shortcode_content(dict, str_line, result.get_start('args'), result.get_end('args'), shortcode_events[result.get_string('id')].event_color) + else: + color_shortcode_content(dict, str_line, 0, 0, shortcode_events[result.get_string('id')].event_color) return fix_dict(dict) else: @@ -86,6 +90,39 @@ func _get_line_syntax_highlighting(line:int) -> Dictionary: return fix_dict(dict) +func line_is_shortcode_event(line_idx:int) -> bool: + var str_line := get_text_edit().get_line(line_idx) + if text_event.text_effects_regex.search(str_line.get_slice(' ', 0)): + return false + + if str_line.strip_edges().begins_with("["): + return true + + if line_idx > 0 and get_text_edit().get_line(line_idx-1).ends_with('\\'): + return line_is_shortcode_event(line_idx-1) + + return false + + +func get_full_event(line_idx:int) -> String: + var str_line := get_text_edit().get_line(line_idx) + var offset := 1 + # Add previous lines + while get_text_edit().get_line(line_idx-offset).ends_with('\\'): + str_line = get_text_edit().get_line(line_idx-offset).trim_suffix('\\')+"\n"+str_line + offset += 1 + + # This is commented out, as it is not needed right now. + # However without it, this isn't actually the full event. + # Might need to be included some day. + #offset = 0 + ## Add following lines + #while get_text_edit().get_line(line_idx+offset).ends_with('\\'): + #str_line = str_line.trim_suffix('\\')+"\n"+get_text_edit().get_line(line_idx+offset) + #offset += 1 + + return str_line + func fix_dict(dict:Dictionary) -> Dictionary: var d := {} var k := dict.keys() @@ -151,7 +188,7 @@ func color_region(dict:Dictionary, color:Color, line:String, start:String, end:S func color_shortcode_content(dict:Dictionary, line:String, from:int = 0, to:int = 0, base_color:=normal_color) -> Dictionary: if to <= from: to = len(line)-1 - var args_result:= shortcode_param_regex.search_all(line.substr(from, to-from+2)) + var args_result := shortcode_param_regex.search_all(line.substr(from, to-from+2)) for x in args_result: dict[x.get_start()+from] = {"color":base_color.lerp(normal_color, 0.5)} dict[x.get_start('value')+from-1] = {"color":base_color.lerp(normal_color, 0.7)} diff --git a/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd b/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd index 581f6630..183b68a2 100644 --- a/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd +++ b/addons/dialogic/Editor/TimelineEditor/TextEditor/timeline_editor_text.gd @@ -13,6 +13,7 @@ func _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(): timeline_editor.current_resource_state = DialogicEditor.ResourceStates.UNSAVED request_code_completion(true) @@ -40,7 +41,7 @@ func save_timeline(): 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 @@ -167,12 +168,19 @@ func _can_drop_data(at_position:Vector2, data:Variant) -> bool: return true return false + # Allows dragging files into the editor func _drop_data(at_position:Vector2, data:Variant) -> void: if typeof(data) == TYPE_DICTIONARY and 'files' in data.keys() and len(data.files) == 1: set_caret_column(get_line_column_at_pos(at_position).x) set_caret_line(get_line_column_at_pos(at_position).y) - insert_text_at_caret('"'+data.files[0]+'"') + var result: String = data.files[0] + if get_line(get_caret_line())[get_caret_column()-1] != '"': + result = '"'+result + if get_line(get_caret_line())[get_caret_column()] != '"': + result = result+'"' + + insert_text_at_caret(result) func _on_update_timer_timeout(): diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd index 827195a3..d6041907 100644 --- a/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd +++ b/addons/dialogic/Editor/TimelineEditor/VisualEditor/TimelineArea.gd @@ -26,7 +26,7 @@ func _ready() -> void: %TimelineArea.get_theme_color("background_color", "CodeEdit") -################### EVENT DRAGGING ############################################# +#region EVENT DRAGGING ################################################################################ func start_dragging(type:DragTypes, data:Variant) -> void: @@ -67,74 +67,127 @@ func finish_dragging(): drag_canceled.emit() queue_redraw() +#endregion -##################### LINE DRAWING ############################################# +#region LINE DRAWING ################################################################################ func _draw() -> void: var _scale := DialogicUtil.get_editor_scale() var line_width := 5 * _scale var horizontal_line_length := 100*_scale - var color_multiplier := Color(1,1,1,0.5) + var 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 event : Control = $Timeline.get_child(idx) + var block : Control = $Timeline.get_child(idx) - if not "resource" in event: + if not "resource" in block: continue - if not event.visible: + if not block.visible: continue - if event.resource is DialogicEndBranchEvent: + if block.resource is DialogicEndBranchEvent: continue - if not (event.has_any_enabled_body_content or event.resource.can_contain_events): + if not (block.has_any_enabled_body_content or block.resource.can_contain_events): continue - var icon_panel_height := 32*_scale - var rect_position :Vector2= event.get_node('%IconPanel').global_position+Vector2(0,1)*event.get_node('%IconPanel').size+Vector2(0,-4) - var color :Color= event.resource.event_color - if event.is_selected(): + var icon_panel_height: int = block.get_node('%IconPanel').size.y + var rect_position: Vector2 = block.get_node('%IconPanel').global_position+Vector2(0,1)*block.get_node('%IconPanel').size+Vector2(0,-4) + var color: Color = block.resource.event_color + + if block.is_selected() or block.end_node and block.end_node.is_selected(): color *= selected_color_multiplier else: color *= color_multiplier - if idx < $Timeline.get_child_count()-1 and event.current_indent_level < $Timeline.get_child(idx+1).current_indent_level: - var end_node :Node= event.end_node - var sub_idx := idx + if block.expanded and not block.resource.can_contain_events: + draw_rect(Rect2(rect_position-global_position+Vector2(line_width, 0), Vector2(line_width, block.size.y-block.get_node('%IconPanel').size.y)), color) - if !end_node: # this doesn't have an end node (e.g. text event with choices in it) + ## If the indentation has not changed, nothing else happens + if idx >= $Timeline.get_child_count()-1 or block.current_indent_level >= $Timeline.get_child(idx+1).current_indent_level: + continue + + ## Draw connection between opening and end branch events + if block.resource.can_contain_events: + var end_node: Node = block.end_node + + if end_node != null: + var v_length: float = end_node.global_position.y+end_node.size.y/2-rect_position.y + #var rect_size := Vector2(line_width, ) + var offset := Vector2(line_width, 0) + + # Draw vertical line + draw_rect(Rect2(rect_position-global_position+offset, Vector2(line_width, v_length)), color) + # Draw horizonal line (on END BRANCH event) + draw_rect(Rect2( + rect_position.x+line_width-global_position.x+offset.x, + rect_position.y+v_length-line_width-global_position.y, + horizontal_line_length-offset.x, + line_width), + color) + + if block.resource.wants_to_group: + var group_color: Color = block.resource.event_color*color_multiplier + var group_starter := true + if idx != 0: + var block_above := $Timeline.get_child(idx-1) + if block_above.resource.event_name == block.resource.event_name: + group_starter = false + if block_above.resource is DialogicEndBranchEvent and block_above.parent_node.resource.event_name == block.resource.event_name: + group_starter = false + + ## Draw small horizontal line on any event in group + draw_rect(Rect2( + rect_position.x-global_position.x-line_width, + rect_position.y-global_position.y-icon_panel_height/2, + line_width, + line_width), + group_color) + + if group_starter: + ## Find the last event in the group (or that events END BRANCH) + var sub_idx := idx + var group_end_idx := idx while sub_idx < $Timeline.get_child_count()-1: sub_idx += 1 - if $Timeline.get_child(sub_idx).current_indent_level == event.current_indent_level: - end_node = $Timeline.get_child(sub_idx-1) + if $Timeline.get_child(sub_idx).current_indent_level == block.current_indent_level-1: + group_end_idx = sub_idx-1 break - var rect_size := Vector2() - if end_node != null: - rect_size = Vector2(line_width, end_node.global_position.y+end_node.size.y-rect_position.y) - if end_node.resource is DialogicEndBranchEvent and event.resource.can_contain_events: - rect_size = Vector2(line_width, end_node.global_position.y+end_node.size.y/2-rect_position.y) - else: - rect_size = Vector2(line_width, $Timeline.get_child(-1).global_position.y+$Timeline.get_child(-1).size.y-rect_position.y) - draw_rect(Rect2(rect_position-global_position, rect_size), color) - draw_rect(Rect2(Vector2(event.get_node('%IconPanel').global_position.x+line_width, rect_position.y+rect_size.y-line_width)-global_position, Vector2(horizontal_line_length, line_width)), color) + var end_node := $Timeline.get_child(group_end_idx) + + var offset := Vector2(-2*line_width, -icon_panel_height/2) + var v_length: float = end_node.global_position.y - rect_position.y + icon_panel_height + + ## Draw vertical line + draw_rect(Rect2( + rect_position.x - global_position.x + offset.x, + rect_position.y - global_position.y + offset.y, + line_width, + v_length), + group_color) - elif event.expanded: - draw_rect(Rect2(rect_position-global_position, Vector2(line_width, event.size.y-event.get_node('%IconPanel').size.y+10*_scale)), color.darkened(0.5)) + ## Draw line that indicates the dragging position if dragging and get_global_rect().has_point(get_global_mouse_position()): - var height :int = 0 + var height: int = 0 if drag_to_position == %Timeline.get_child_count(): height = %Timeline.get_child(-1).global_position.y+%Timeline.get_child(-1).size.y-global_position.y-(line_width/2.0) else: height = %Timeline.get_child(drag_to_position).global_position.y-global_position.y-(line_width/2.0) - draw_line(Vector2(0, height), Vector2(size.x*0.9, height), get_theme_color("accent_color", "Editor"), line_width*0.2) + draw_line(Vector2(0, height), Vector2(size.x*0.9, height), get_theme_color("accent_color", "Editor"), line_width*.3) -##################### SPACE BELOW ############################################## +#endregion + + +#region SPACE BELOW ################################################################################ func add_extra_scroll_area_to_timeline(fake_arg:Variant=null) -> void: @@ -143,3 +196,5 @@ func add_extra_scroll_area_to_timeline(fake_arg:Variant=null) -> void: %Timeline.size.y = 0 if %Timeline.size.y + 200 > %TimelineArea.size.y: %Timeline.custom_minimum_size = Vector2(0, %Timeline.size.y + 200) + +#endregion diff --git a/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd b/addons/dialogic/Editor/TimelineEditor/VisualEditor/timeline_editor_visual.gd index d5eaf050..acf136c2 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 drag_allowed := false -##################### CREATE/SAVE/LOAD ######################################### +#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) @@ -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,24 +134,27 @@ 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() +#endregion -##################### SETUP #################################################### +#region SETUP ################################################################################ -func _ready(): +func _ready() -> void: DialogicUtil.get_dialogic_plugin().dialogic_save.connect(save_timeline) event_node = load("res://addons/dialogic/Editor/Events/EventBlock/event_block.tscn") @@ -158,25 +164,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 @@ -191,7 +211,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() @@ -218,40 +238,33 @@ 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) + # Sort event sections var sections_order :Array= DialogicUtil.get_editor_setting('event_section_order', - ['Main', 'Flow', 'Logic', 'Audio', 'Godot','Other', 'Helper']) + ['Main', 'Flow', 'Logic', 'Audio', 'Visual','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)) + 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 - $View.split_offset = -200*_scale _on_right_sidebar_resized() +#endregion -#################### CLEANUP ################################################### -################################################################################ - -func _exit_tree() -> void: - # Explicitly free any open cache resources on close, so we don't get leaked resource errors on shutdown - clear_timeline_nodes() - - -##################### CONTENT LIST ############################################# +#region CONTENT LIST ################################################################################ func _on_content_item_clicked(label:String) -> void: @@ -266,19 +279,28 @@ 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) -################# DRAG & DROP + DRAGGING EVENTS ################################ +#endregion + + +#region DRAG & DROP + DRAGGING EVENTS ################################################################################# # SIGNAL handles input on the events mainly for selection and moving events -func _on_event_block_gui_input(event, item: Node): +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): @@ -288,9 +310,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: + if !%TimelineArea.dragging and !get_viewport().gui_is_dragging() and drag_allowed: sort_selection() %TimelineArea.start_dragging(%TimelineArea.DragTypes.EXISTING_EVENTS, selected_items) @@ -301,14 +325,7 @@ func _on_timeline_area_drag_completed(type:int, index:int, data:Variant) -> void var resource :DialogicEvent = data.duplicate() resource._load_custom_defaults() - TimelineUndoRedo.create_action("[D] Add "+resource.event_name+" event.") - if resource.can_contain_events: - TimelineUndoRedo.add_do_method(add_event_with_end_branch.bind(resource, index, true, true)) - TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(index, 2)) - else: - TimelineUndoRedo.add_do_method(add_event_node.bind(resource, index, true, true)) - TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(index, 1)) - TimelineUndoRedo.commit_action() + add_event_undoable(resource, index) elif type == %TimelineArea.DragTypes.EXISTING_EVENTS: if not (len(data) == 1 and data[0].get_index()+1 == index): @@ -318,11 +335,11 @@ func _on_timeline_area_drag_completed(type:int, index:int, data:Variant) -> void something_changed() scroll_to_piece(index) indent_events() +#endregion -################# CREATING THE TIMELINE ######################################## +#region CREATING THE TIMELINE ################################################################################ - # Adding an event to the timeline func add_event_node(event_resource:DialogicEvent, at_index:int = -1, auto_select: bool = false, indent: bool = false) -> Control: if event_resource is DialogicEndBranchEvent: @@ -332,36 +349,36 @@ func add_event_node(event_resource:DialogicEvent, at_index:int = -1, auto_select if event_resource['event_node_as_text'] != "": event_resource._load_from_string(event_resource['event_node_as_text']) - var piece :Control = event_node.instantiate() - piece.resource = event_resource - event_resource._editor_node = piece + var block: Control = event_node.instantiate() + block.resource = event_resource + event_resource._editor_node = block event_resource._enter_visual_editor(timeline_editor) - piece.content_changed.connect(something_changed) + block.content_changed.connect(something_changed) if event_resource.event_name == "Label": - piece.content_changed.connect(update_content_list) + block.content_changed.connect(update_content_list) if at_index == -1: if len(selected_items) != 0: - selected_items[0].add_sibling(piece) + selected_items[0].add_sibling(block) else: - %Timeline.add_child(piece) + %Timeline.add_child(block) else: - %Timeline.add_child(piece) - %Timeline.move_child(piece, at_index) + %Timeline.add_child(block) + %Timeline.move_child(block, at_index) - piece.gui_input.connect(_on_event_block_gui_input.bind(piece)) + block.gui_input.connect(_on_event_block_gui_input.bind(block)) # Building editing part - piece.build_editor(true, event_resource.expand_by_default) + block.build_editor(true, event_resource.expand_by_default) if auto_select: - select_item(piece, false) + select_item(block, false) # Indent on create if indent: indent_events() - return piece + return block func create_end_branch_event(at_index:int, parent_node:Node) -> Node: @@ -377,13 +394,25 @@ 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) -> 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)) + TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(at_index, 2)) + else: + TimelineUndoRedo.add_do_method(add_event_node.bind(event_resource, at_index, true, true)) + TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(at_index, 1)) + TimelineUndoRedo.commit_action() +#endregion + -#################### DELETING, COPY, PASTE ##################################### +#region DELETING, COPY, PASTE ################################################################################ ## Lists the given events (as text) based on their indexes. @@ -460,6 +489,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 +508,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 +534,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: @@ -554,8 +583,10 @@ func delete_events_at_index(at_index:int, amount:int = 1)-> void: delete_events_indexed(new_indexed_events) indent_events() +#endregion + -#################### BLOCK SELECTION ########################################### +#region BLOCK SELECTION ################################################################################ func _is_item_selected(item: Node) -> bool: @@ -609,6 +640,7 @@ func visual_update_selection() -> void: item.visual_select() if 'end_node' in item and item.end_node != null: item.end_node.highlight() + %TimelineArea.queue_redraw() ## Sorts the selection using 'custom_sort_selection' @@ -631,8 +663,10 @@ func select_all_items() -> void: func deselect_all_items() -> void: selected_items = [] visual_update_selection() +#endregion + -############ CREATING NEW EVENTS USING THE BUTTONS ############################# +#region CREATING NEW EVENTS USING THE BUTTONS ################################################################################ # Event Creation signal for buttons @@ -656,23 +690,17 @@ func _add_event_button_pressed(event_resource:DialogicEvent, force_resource := f resource.created_by_button = true - TimelineUndoRedo.create_action("[D] Add "+event_resource.event_name+" event.") - if event_resource.can_contain_events: - TimelineUndoRedo.add_do_method(add_event_with_end_branch.bind(resource, at_index, true, true)) - TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(at_index, 2)) - else: - TimelineUndoRedo.add_do_method(add_event_node.bind(resource, at_index, true, true)) - TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(at_index, 1)) - TimelineUndoRedo.commit_action() + add_event_undoable(resource, at_index) resource.created_by_button = false something_changed() scroll_to_piece(at_index) indent_events() +#endregion -##################### BLOCK GETTERS ############################################ +#region BLOCK GETTERS ################################################################################ func get_block_above(block:Node) -> Node: @@ -685,9 +713,10 @@ func get_block_below(block:Node) -> Node: if block.get_index() < %Timeline.get_child_count() - 1: return %Timeline.get_child(block.get_index() + 1) return null +#endregion -##################### BLOCK MOVEMENT ########################################### +#region BLOCK MOVEMENT ################################################################################ @@ -701,11 +730,10 @@ func move_blocks_to_index(blocks:Array, index:int): return if "end_node" in event and event.end_node: if !event.end_node in blocks: - if index > event.end_node.get_index(): - if event.end_node.get_index() == event.get_index()+1: - blocks.append(event.end_node) - else: - return + if event.end_node.get_index() == event.get_index()+1: + blocks.append(event.end_node) + else: + return index_shift += int(event.get_index() < index) var do_indexes := {} @@ -802,16 +830,16 @@ func offset_blocks_by_index(blocks:Array, offset:int): TimelineUndoRedo.add_undo_method(move_events_by_indexes.bind(undo_indexes)) TimelineUndoRedo.commit_action() +#endregion - -################### VISIBILITY/VISUALS ######################################### +#region VISIBILITY/VISUALS ################################################################################ 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 @@ -824,132 +852,164 @@ func indent_events() -> void: var currently_hidden := false var hidden_count := 0 - var hidden_until :Control= null + var hidden_until: Control = null # will be applied to the indent after the current event var delayed_indent: int = 0 - for event in event_list: - if (not "resource" in event): + for block in event_list: + if (not "resource" in block): continue - if (not currently_hidden) and event.resource.can_contain_events and event.end_node and event.collapsed: + if (not currently_hidden) and block.resource.can_contain_events and block.end_node and block.collapsed: currently_hidden = true - hidden_until = event.end_node + hidden_until = block.end_node hidden_count = 0 - elif currently_hidden and event == hidden_until: - event.update_hidden_events_indicator(hidden_count) + elif currently_hidden and block == hidden_until: + block.update_hidden_events_indicator(hidden_count) currently_hidden = false hidden_until = null elif currently_hidden: - event.hide() + block.hide() hidden_count += 1 else: - event.show() - if event.resource is DialogicEndBranchEvent: - event.update_hidden_events_indicator(0) + block.show() + if block.resource is DialogicEndBranchEvent: + block.update_hidden_events_indicator(0) delayed_indent = 0 - if event.resource.can_contain_events: + if block.resource.can_contain_events: delayed_indent = 1 - if event.resource.needs_parent_event: - var current_block_above := get_block_above(event) - while current_block_above != null and current_block_above.resource is DialogicEndBranchEvent: - if current_block_above.parent_node == event: - break - current_block_above = get_block_above(current_block_above.parent_node) + if block.resource.wants_to_group: + indent += 1 - if current_block_above != null and event.resource.is_expected_parent_event(current_block_above.resource): - indent += 1 - event.set_warning() - else: - event.set_warning('This event needs a specific parent event!') - - elif event.resource is DialogicEndBranchEvent: - event.parent_node_changed() + elif block.resource is DialogicEndBranchEvent: + block.parent_node_changed() delayed_indent -= 1 - if event.parent_node.resource.needs_parent_event: + if block.parent_node.resource.wants_to_group: delayed_indent -= 1 if indent >= 0: - event.set_indent(indent) + block.set_indent(indent) else: - event.set_indent(0) + block.set_indent(0) indent += delayed_indent + await get_tree().process_frame + await get_tree().process_frame %TimelineArea.queue_redraw() - -################ SPECIAL BLOCK OPERATIONS ###################################### +#region SPECIAL BLOCK OPERATIONS ################################################################################ func _on_event_popup_menu_index_pressed(index:int) -> void: var item :Control = %EventPopupMenu.current_event if index == 0: + if not item in selected_items: + selected_items = [item] + duplicate_selected() + elif index == 2: if not item.resource.help_page_path.is_empty(): OS.shell_open(item.resource.help_page_path) - elif index == 1: + elif index == 3: find_parent('EditorView').plugin_reference.get_editor_interface().set_main_screen_editor('Script') find_parent('EditorView').plugin_reference.get_editor_interface().edit_script(item.resource.get_script(), 1, 1) - elif index == 3 or index == 4: - if index == 3: + elif index == 5 or index == 6: + if index == 5: offset_blocks_by_index(selected_items, -1) else: offset_blocks_by_index(selected_items, +1) - elif index == 6: - var events_indexed := get_events_indexed([item]) + elif index == 8: + var events_indexed : Dictionary + if item in selected_items: + events_indexed = get_events_indexed(selected_items) + else: + events_indexed = get_events_indexed([item]) TimelineUndoRedo.create_action("[D] Deleting 1 event.") TimelineUndoRedo.add_do_method(delete_events_indexed.bind(events_indexed)) TimelineUndoRedo.add_undo_method(add_events_indexed.bind(events_indexed)) TimelineUndoRedo.commit_action() indent_events() - something_changed() -func _on_right_sidebar_resized(): +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 -#################### SHORTCUTS ################################################# + +#region SHORTCUTS ################################################################################ +func duplicate_selected() -> void: + if len(selected_items) > 0: + var events := get_events_indexed(selected_items).values() + 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_undo_method(delete_events_at_index.bind(at_index, len(events))) + 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 @@ -986,7 +1046,8 @@ func _input(event:InputEvent) -> void: get_viewport().set_input_as_handled() ## Some shortcuts should be disabled when writing text. - if get_viewport().gui_get_focus_owner() is TextEdit || get_viewport().gui_get_focus_owner() is LineEdit: + 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 match event.as_text(): @@ -1055,6 +1116,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).") @@ -1064,13 +1126,7 @@ func _input(event:InputEvent) -> void: get_viewport().set_input_as_handled() "Ctrl+D": - if len(selected_items) > 0: - var events := get_events_indexed(selected_items).values() - var at_index :int= selected_items[-1].get_index() - TimelineUndoRedo.create_action("[D] Duplicate "+str(len(events))+" event(s).") - TimelineUndoRedo.add_do_method(add_events_at_index.bind(events, at_index)) - TimelineUndoRedo.add_undo_method(delete_events_at_index.bind(at_index, len(events))) - TimelineUndoRedo.commit_action() + duplicate_selected() get_viewport().set_input_as_handled() "Alt+Up", "Option+Up": @@ -1115,3 +1171,5 @@ func get_previous_character(double_previous := false) -> DialogicCharacter: character = %Timeline.get_child(idx).resource.character break return character + +#endregion diff --git a/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd index 8e5eaf2f..2658995f 100644 --- a/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd +++ b/addons/dialogic/Editor/TimelineEditor/test_timeline_scene.gd @@ -19,13 +19,13 @@ func _ready() -> void: get_tree().quit() DialogicUtil.autoload().start(current_timeline) DialogicUtil.autoload().timeline_ended.connect(get_tree().quit) - DialogicUtil.autoload().signal_event.connect(recieve_event_signal) - DialogicUtil.autoload().text_signal.connect(recieve_text_signal) + DialogicUtil.autoload().signal_event.connect(receive_event_signal) + DialogicUtil.autoload().text_signal.connect(receive_text_signal) -func recieve_event_signal(argument:String) -> void: +func receive_event_signal(argument:String) -> void: print("[Dialogic] Encountered a signal event: ", argument) -func recieve_text_signal(argument:String) -> void: +func receive_text_signal(argument:String) -> void: print("[Dialogic] Encountered a signal in text: ", argument) func _input(event:InputEvent) -> void: @@ -36,7 +36,7 @@ func _input(event:InputEvent) -> void: if (event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_MIDDLE): - var auto_skip: DialogicAutoSkip = DialogicUtil.autoload().Input.auto_skip + var auto_skip: DialogicAutoSkip = DialogicUtil.autoload().Inputs.auto_skip var is_auto_skip_enabled := auto_skip.enabled auto_skip.disable_on_unread_text = false diff --git a/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn b/addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn index a3d0c1b2..799d24d1 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_pnrtc"] +[sub_resource type="Image" id="Image_3cd31"] 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_ajdpw"] -image = SubResource("Image_pnrtc") +[sub_resource type="ImageTexture" id="ImageTexture_wvrw5"] +image = SubResource("Image_3cd31") [sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_7lpql"] script = ExtResource("4_1t6bf") -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dumog"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3migc"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 @@ -66,11 +66,11 @@ tooltip_text = "The name of the timeline is determined from the file name. This is what you should use in a jump event to reference this timeline. Besides the file path, you can also use this name in Dialogic.start()" -texture = SubResource("ImageTexture_ajdpw") -hint_text = "The name of the timeline is determined from the file name. +texture = SubResource("ImageTexture_wvrw5") +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. -Besides the file path, you can also use this name in Dialogic.start()" +You can also use this name in Dialogic.start()." [node name="SwitchEditorMode" type="Button" parent="VBox/HBox"] unique_name_in_owner = true @@ -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_ajdpw") +icon = SubResource("ImageTexture_wvrw5") [node name="VisualEditor" parent="VBox" instance=ExtResource("2_qs7vc")] unique_name_in_owner = true @@ -108,7 +108,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_dumog") +theme_override_styles/panel = SubResource("StyleBoxFlat_3migc") [node name="CenterContainer" type="CenterContainer" parent="NoTimelineScreen"] layout_mode = 2 diff --git a/addons/dialogic/Example Assets/already_read_indicator.gd b/addons/dialogic/Example Assets/already_read_indicator.gd index 29f747dc..8ead3086 100644 --- a/addons/dialogic/Example Assets/already_read_indicator.gd +++ b/addons/dialogic/Example Assets/already_read_indicator.gd @@ -2,10 +2,10 @@ extends Control func _ready(): if DialogicUtil.autoload().has_subsystem('History'): - DialogicUtil.autoload().History.already_read_event_reached.connect(_on_already_read_event) - DialogicUtil.autoload().History.not_read_event_reached.connect(_on_not_read_event) + DialogicUtil.autoload().History.visited_event.connect(_on_visited_event) + DialogicUtil.autoload().History.unvisited_event.connect(_on_not_read_event) -func _on_already_read_event() -> void: +func _on_visited_event() -> void: show() func _on_not_read_event() -> void: diff --git a/addons/dialogic/Example Assets/default_event.gd b/addons/dialogic/Example Assets/default_event.gd index 16af2dda..a2cfa1ed 100644 --- a/addons/dialogic/Example Assets/default_event.gd +++ b/addons/dialogic/Example Assets/default_event.gd @@ -1,17 +1,15 @@ @tool extends DialogicEvent - # DEFINE ALL PROPERTIES OF THE EVENT # var MySetting :String = "" func _execute() -> void: - # I have no idea how this event works + # I have no idea how this event works ;) finish() -################################################################################ -## INITIALIZE +#region INITIALIZE ################################################################################ # SET ALL VALUES THAT SHOULD NEVER CHANGE HERE @@ -21,14 +19,15 @@ func _init() -> void: event_category = "Main" event_sorting_index = 0 +#endregion -################################################################################ -## SAVING/LOADING +#region SAVING/LOADING ################################################################################ func get_shortcode() -> String: return "default_shortcode" + func get_shortcode_parameters() -> Dictionary: return { #param_name : property_name @@ -40,9 +39,13 @@ func get_shortcode_parameters() -> Dictionary: # - from_text(), # - is_valid_event() -################################################################################ -## EDITOR REPRESENTATION +#endregion + + +#region EDITOR REPRESENTATION ################################################################################ func build_event_editor() -> void: pass + +#endregion diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd index c6c0e8a6..e2378dd3 100644 --- a/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_AnimatedSprite.gd @@ -1,11 +1,11 @@ @tool -extends Node2D +extends DialogicPortrait # If the custom portrait accepts a change, then accept it here func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: if passed_portrait == "": passed_portrait = passed_character['default_portrait'] - + if $Sprite.sprite_frames.has_animation(passed_portrait): $Sprite.play(passed_portrait) diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd index 2d6f7c28..acb600aa 100644 --- a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.gd @@ -1,20 +1,19 @@ @tool -extends Node2D +extends DialogicPortrait enum Faces {BASED_ON_PORTRAIT_NAME, NEUTRAL, HAPPY, SAD, JOY, SHOCK, ANGRY} -var portrait - @export var emotion : Faces = Faces.BASED_ON_PORTRAIT_NAME @export var portrait_width: int @export var portrait_height: int -@export var alien = true +@export var alien := true -var does_custom_portrait_change = true +var does_custom_portrait_change := true -func _ready(): +func _ready() -> void: $Alien.hide() + # Function to accept and use the extra data, if the custom portrait wants to accept it func _set_extra_data(data: String) -> void: if data == "alien": @@ -22,14 +21,17 @@ func _set_extra_data(data: String) -> void: elif data == "no_alien": $Alien.hide() + # This function can be overridden. Defaults to true, if not overridden! -func _should_do_portrait_update(character:DialogicCharacter, portrait:String) -> bool: +func _should_do_portrait_update(_character: DialogicCharacter, _portrait:String) -> bool: return true + # If the custom portrait accepts a change, then accept it here -func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String) -> void: +func _update_portrait(_passed_character: DialogicCharacter, passed_portrait: String) -> void: for face in $Faces.get_children(): face.hide() + if emotion == Faces.BASED_ON_PORTRAIT_NAME: if 'happy' in passed_portrait.to_lower(): $Faces/Smile.show() elif 'sad' in passed_portrait.to_lower(): $Faces/Frown.show() @@ -37,6 +39,7 @@ func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String elif 'shock' in passed_portrait.to_lower(): $Faces/Shock.show() elif 'angry' in passed_portrait.to_lower(): $Faces/Anger.show() else: $Faces/Neutral.show() + else: if emotion == Faces.HAPPY: $Faces/Smile.show() elif emotion == Faces.SAD: $Faces/Frown.show() @@ -44,13 +47,24 @@ func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String elif emotion == Faces.SHOCK: $Faces/Shock.show() elif emotion == Faces.ANGRY: $Faces/Anger.show() else: $Faces/Neutral.show() - + $Alien.visible = alien -func _set_mirror(mirror:bool) -> void: - if mirror: scale.x *= -1 -# if implemented, this is used by the editor for the "full view" mode +func _set_mirror(is_mirrored: bool) -> void: + if is_mirrored: + self.scale.x = -1 + + else: + self.scale.x = 1 + + +## If implemented, this is used by the editor for the "full view" mode func _get_covered_rect() -> Rect2: - #return Rect2($Faces/Anger.position+$Faces.position, $Faces/Anger.get_rect().size*$Faces/Anger.scale*$Faces.scale) # will fcus on the face - return Rect2($Body.position, $Body.get_rect().size*$Body.scale) + # This will focus on the face. + # return Rect2($Faces/Anger.position+$Faces.position, $Faces/Anger.get_rect().size*$Faces/Anger.scale*$Faces.scale) + var size: Vector2 = $Body.get_rect().size + var scaled_size: Vector2 = size * $Body.scale + var position: Vector2 = $Body.position + + return Rect2(position, scaled_size) diff --git a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn index 5c22306f..a780a925 100644 --- a/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn +++ b/addons/dialogic/Example Assets/portraits/CustomPortrait_FaceAtlas.tscn @@ -10,7 +10,7 @@ [ext_resource type="Texture2D" uid="uid://c5aku2g01k6c6" path="res://addons/dialogic/Example Assets/portraits/Princess/shock.png" id="7_5xil3"] [ext_resource type="Texture2D" uid="uid://dsid4ye0q74nl" path="res://addons/dialogic/Example Assets/portraits/Princess/smile.png" id="8_7s6tq"] -[node name="CustomPortraitFaceAtlass" type="Node2D"] +[node name="CustomPortraitFaceAtlas" type="Node2D"] position = Vector2(301, 598) script = ExtResource("1_fc12l") diff --git a/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd b/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd index 87ae87ab..64aac03b 100644 --- a/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd +++ b/addons/dialogic/Example Assets/portraits/simple_highlight_portrait.gd @@ -2,7 +2,7 @@ extends DialogicPortrait @export_group('Main') -@export_file var image : String = "" +@export_file var image: String = "" var unhighlighted_color := Color.DARK_GRAY var prev_z_index := 0 @@ -15,13 +15,14 @@ func _update_portrait(passed_character:DialogicCharacter, passed_portrait:String func _ready() -> void: - self.modulate = unhighlighted_color + if not Engine.is_editor_hint(): + self.modulate = unhighlighted_color func _highlight(): create_tween().tween_property(self, 'modulate', Color.WHITE, 0.15) prev_z_index = DialogicUtil.autoload().Portraits.get_character_info(character).get('z_index', 0) - DialogicUtil.autoload().Portraits.change_character_z_index(character, 10) + DialogicUtil.autoload().Portraits.change_character_z_index(character, 99) func _unhighlight(): diff --git a/addons/dialogic/Modules/Audio/event_music.gd b/addons/dialogic/Modules/Audio/event_music.gd index 177289f0..dd5c923d 100644 --- a/addons/dialogic/Modules/Audio/event_music.gd +++ b/addons/dialogic/Modules/Audio/event_music.gd @@ -1,9 +1,9 @@ @tool +## Event that can change the currently playing background music. +## This event won't play new music if it's already playing. class_name DialogicMusicEvent extends DialogicEvent -## Event that can change the currently playing background music. - ### Settings @@ -24,9 +24,10 @@ var loop: bool = true ################################################################################ func _execute() -> void: - dialogic.Audio.update_music(file_path, volume, audio_bus, fade_length, loop) - finish() + if not dialogic.Audio.is_music_playing_resource(file_path): + dialogic.Audio.update_music(file_path, volume, audio_bus, fade_length, loop) + finish() ################################################################################ ## INITIALIZE @@ -66,14 +67,14 @@ 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", 'placeholder' : "No music", 'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]}) - add_body_edit('fade_length', ValueType.FLOAT, {'left_text':'Fade Time:'}) - add_body_edit('volume', ValueType.DECIBEL, {'left_text':'Volume:'}, '!file_path.is_empty()') + add_body_edit('fade_length', ValueType.NUMBER, {'left_text':'Fade Time:'}) + 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()') add_body_edit('loop', ValueType.BOOL, {'left_text':'Loop:'}, '!file_path.is_empty() and not file_path.to_lower().ends_with(".wav")') diff --git a/addons/dialogic/Modules/Audio/event_sound.gd b/addons/dialogic/Modules/Audio/event_sound.gd index 1c1ba7ad..3d8308bc 100644 --- a/addons/dialogic/Modules/Audio/event_sound.gd +++ b/addons/dialogic/Modules/Audio/event_sound.gd @@ -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": "Master", "suggestions": get_bus_suggestions}, "loop" : {"property": "loop", "default": false}, } @@ -65,12 +65,12 @@ func get_shortcode_parameters() -> Dictionary: ################################################################################ func build_event_editor(): - add_header_edit('file_path', ValueType.FILE, + add_header_edit('file_path', ValueType.FILE, {'left_text' : 'Play', - 'file_filter' : '*.mp3, *.ogg, *.wav; Supported Audio Files', - 'placeholder' : "Select file", + 'file_filter' : '*.mp3, *.ogg, *.wav; Supported Audio Files', + 'placeholder' : "Select file", 'editor_icon' : ["AudioStreamPlayer", "EditorIcons"]}) - add_body_edit('volume', ValueType.DECIBEL, {'left_text':'Volume:'}, '!file_path.is_empty()') + 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: diff --git a/addons/dialogic/Modules/Audio/subsystem_audio.gd b/addons/dialogic/Modules/Audio/subsystem_audio.gd index 307d126d..5a22680f 100644 --- a/addons/dialogic/Modules/Audio/subsystem_audio.gd +++ b/addons/dialogic/Modules/Audio/subsystem_audio.gd @@ -1,40 +1,82 @@ extends DialogicSubsystem - -## Subsystem that manages music and sounds. - -signal music_started(info:Dictionary) -signal sound_started(info:Dictionary) - +## Subsystem for managing background music and one-shot sound effects. +## +## This subsystem has many different helper methods for managing audio +## in your timeline. +## For instance, you can listen to music changes via [signal music_started]. + + +## Whenever a new background music is started, this signal is emitted and +## contains a dictionary with the following keys: [br] +## [br] +## Key | Value Type | Value [br] +## ----------- | ------------- | ----- [br] +## `path` | [type String] | The path to the audio resource file. [br] +## `volume` | [type float] | The volume of the audio resource that will be set to the [member base_music_player]. [br] +## `audio_bus` | [type String] | The audio bus name that the [member base_music_player] will use. [br] +## `loop` | [type bool] | Whether the audio resource will loop or not once it finishes playing. [br] +signal music_started(info: Dictionary) + + +## Whenever a new sound effect is set, this signal is emitted and contains a +## dictionary with the following keys: [br] +## [br] +## Key | Value Type | Value [br] +## ----------- | ------------- | ----- [br] +## `path` | [type String] | The path to the audio resource file. [br] +## `volume` | [type float] | The volume of the audio resource that will be set to [member base_sound_player]. [br] +## `audio_bus` | [type String] | The audio bus name that the [member base_sound_player] will use. [br] +## `loop` | [type bool] | Whether the audio resource will loop or not once it finishes playing. [br] +signal sound_started(info: Dictionary) + + +## Audio player base duplicated to play background music. +## +## Background music is long audio. var base_music_player := AudioStreamPlayer.new() +## Audio player base, that will be duplicated to play sound effects. +## +## Sound effects are short audio. var base_sound_player := AudioStreamPlayer.new() -#################################################################################################### -## STATE + +#region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR): +## 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: update_music() stop_all_sounds() -func load_game_state(load_flag:=LoadFlags.FULL_LOAD): + +## Loads the state on this subsystem from the current state info. +func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void: if load_flag == LoadFlags.ONLY_DNODES: return - var info = dialogic.current_state_info.get('music') - if info == null or info.path.is_empty(): + var info: Dictionary = dialogic.current_state_info.get("music", {}) + if info.is_empty() or info.path.is_empty(): update_music() else: update_music(info.path, info.volume, info.audio_bus, 0, info.loop) + +## Pauses playing audio. func pause() -> void: for child in get_children(): child.stream_paused = true + +## Resumes playing audio. func resume() -> void: for child in get_children(): child.stream_paused = false -#################################################################################################### -## MAIN METHODS +#endregion + + +#region MAIN METHODS #################################################################################################### func _ready() -> void: @@ -46,13 +88,13 @@ func _ready() -> void: ## Updates the background music. Will fade out previous music. -func update_music(path:String = '', volume:float = 0.0, audio_bus:String = "Master", fade_time:float = 0.0, loop:bool = true) -> void: +func update_music(path := "", volume := 0.0, audio_bus := "Master", 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(): fader = create_tween() - var prev_node = null + var prev_node: Node = null if base_music_player.playing: prev_node = base_music_player.duplicate() add_child(prev_node) @@ -80,16 +122,17 @@ func update_music(path:String = '', volume:float = 0.0, audio_bus:String = "Mast fader.tween_callback(prev_node.queue_free) +## Whether music is playing. func has_music() -> bool: return !dialogic.current_state_info.get('music', {}).get('path', '').is_empty() ## Plays a given sound file. -func play_sound(path:String, volume:float = 0.0, audio_bus:String = "Master", loop :bool= false) -> void: +func play_sound(path: String, volume := 0.0, audio_bus := "Master", 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.name += "Sound" new_sound_node.stream = load(path) if "loop" in new_sound_node.stream: new_sound_node.stream.loop = loop @@ -105,6 +148,7 @@ func play_sound(path:String, volume:float = 0.0, audio_bus:String = "Master", lo new_sound_node.finished.connect(new_sound_node.queue_free) +## Stops all audio. func stop_all_sounds() -> void: for node in get_children(): if node == base_sound_player: @@ -112,5 +156,19 @@ func stop_all_sounds() -> void: if "Sound" in node.name: node.queue_free() -func interpolate_volume_linearly(value :float, node:Node) -> void: + +## Converts a linear loudness value to decibel and sets that volume to +## the given [param node]. +func interpolate_volume_linearly(value: float, node: Node) -> void: node.volume_db = linear_to_db(value) + + +## Returns whether the currently playing audio resource is the same as this +## event's [param resource_path]. +func is_music_playing_resource(resource_path: String) -> bool: + var is_playing_resource: bool = (base_music_player.is_playing() + and base_music_player.stream.resource_path == resource_path) + + return is_playing_resource + +#endregion 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 f35ce6d3..f5465d5a 100644 --- a/addons/dialogic/Modules/Background/Transitions/class_dialogic_background_transition.gd +++ b/addons/dialogic/Modules/Background/Transitions/class_dialogic_background_transition.gd @@ -51,6 +51,6 @@ func tween_shader_progress(progress_parameter:="progress") -> PropertyTweener: bg_holder.material.set_shader_parameter("progress", 0.0) var tween := create_tween() - var tweener := tween.tween_property(bg_holder, "material:shader_parameter/progress", 1.0, time) + var tweener := tween.tween_property(bg_holder, "material:shader_parameter/progress", 1.0, time).from(0.0) tween.tween_callback(emit_signal.bind('transition_finished')) return tweener diff --git a/addons/dialogic/Modules/Background/event_background.gd b/addons/dialogic/Modules/Background/event_background.gd index 005af8ff..3fac581c 100644 --- a/addons/dialogic/Modules/Background/event_background.gd +++ b/addons/dialogic/Modules/Background/event_background.gd @@ -19,25 +19,49 @@ var fade: float = 0.0 ## Name of the transition to use. var transition: String = "" - -################################################################################ -## EXECUTION +## Helpers for visual editor +enum ArgumentTypes {IMAGE, CUSTOM} +var _arg_type := ArgumentTypes.IMAGE : + get: + if argument.begins_with("res://"): + return ArgumentTypes.IMAGE + else: + return _arg_type + set(value): + if value == ArgumentTypes.CUSTOM: + if argument.begins_with("res://"): + argument = " "+argument + _arg_type = value + +enum SceneTypes {DEFAULT, CUSTOM} +var _scene_type := SceneTypes.DEFAULT : + get: + if scene.is_empty(): + return _scene_type + else: + return SceneTypes.CUSTOM + set(value): + if value == SceneTypes.DEFAULT: + scene = "" + _scene_type = value + +#region EXECUTION ################################################################################ func _execute() -> void: var final_fade_duration := fade - if dialogic.Input.auto_skip.enabled: - var time_per_event: float = dialogic.Input.auto_skip.time_per_event + if dialogic.Inputs.auto_skip.enabled: + var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event final_fade_duration = min(fade, time_per_event) dialogic.Backgrounds.update_background(scene, argument, final_fade_duration, transition) finish() +#endregion -################################################################################ -## INITIALIZE +#region INITIALIZE ################################################################################ func _init() -> void: @@ -46,9 +70,9 @@ func _init() -> void: event_category = "Visuals" event_sorting_index = 0 +#endregion -################################################################################ -## SAVING/LOADING +#region SAVE & LOAD ################################################################################ func get_shortcode() -> String: @@ -66,29 +90,60 @@ func get_shortcode_parameters() -> Dictionary: } -################################################################################ -## EDITOR REPRESENTATION +#endregion + +#region EDITOR REPRESENTATION ################################################################################ func build_event_editor(): - add_header_edit('argument', ValueType.FILE, - {'left_text' : 'Show', - 'file_filter':'*.jpg, *.jpeg, *.png, *.webp, *.tga, *svg, *.bmp, *.dds, *.exr, *.hdr; Supported Image Files', - 'placeholder': "No background", - 'editor_icon':["Image", "EditorIcons"]}, - 'scene == ""') + add_header_edit('_scene_type', ValueType.FIXED_OPTIONS, { + 'left_text' :'Show', + 'options': [ + { + 'label': 'Background', + 'value': SceneTypes.DEFAULT, + 'icon': ["GuiRadioUnchecked", "EditorIcons"] + }, + { + 'label': 'Custom Scene', + 'value': SceneTypes.CUSTOM, + 'icon': ["PackedScene", "EditorIcons"] + } + ]}) + add_header_label("with image", "_scene_type == SceneTypes.DEFAULT") add_header_edit("scene", ValueType.FILE, - {'left_text' :'Scene:', - 'file_filter':'*.tscn, *.scn; Scene Files', - 'placeholder': "Default scene", - 'editor_icon':["PackedScene", "EditorIcons"]}) - add_body_edit('argument', ValueType.SINGLELINE_TEXT, {'left_text':'Argument:'}, 'scene != ""') - add_body_edit("transition", ValueType.COMPLEX_PICKER, + {'file_filter':'*.tscn, *.scn; Scene Files', + 'placeholder': "Custom scene", + 'editor_icon': ["PackedScene", "EditorIcons"], + }, '_scene_type == SceneTypes.CUSTOM') + add_header_edit('_arg_type', ValueType.FIXED_OPTIONS, { + 'left_text' : 'with', + 'options': [ + { + 'label': 'Image', + 'value': ArgumentTypes.IMAGE, + 'icon': ["Image", "EditorIcons"] + }, + { + 'label': 'Custom Argument', + 'value': ArgumentTypes.CUSTOM, + 'icon': ["String", "EditorIcons"] + } + ], "symbol_only": true}, "_scene_type == SceneTypes.CUSTOM") + add_header_edit('argument', ValueType.FILE, + {'file_filter':'*.jpg, *.jpeg, *.png, *.webp, *.tga, *svg, *.bmp, *.dds, *.exr, *.hdr; Supported Image Files', + 'placeholder': "No Image", + 'editor_icon': ["Image", "EditorIcons"], + }, + '_arg_type == ArgumentTypes.IMAGE or _scene_type == SceneTypes.DEFAULT') + add_header_edit('argument', ValueType.SINGLELINE_TEXT, {}, '_arg_type == ArgumentTypes.CUSTOM') + + add_body_edit("transition", ValueType.DYNAMIC_OPTIONS, {'left_text':'Transition:', 'empty_text':'Simple Fade', 'suggestions_func':get_transition_suggestions, 'editor_icon':["PopupMenu", "EditorIcons"]}) - add_body_edit("fade", ValueType.FLOAT, {'left_text':'Fade Time:'}) + add_body_edit("fade", ValueType.NUMBER, {'left_text':'Fade time:'}) func get_transition_suggestions(filter:String="") -> Dictionary: @@ -97,3 +152,5 @@ func get_transition_suggestions(filter:String="") -> Dictionary: for i in transitions: suggestions[DialogicUtil.pretty_name(i)] = {'value': DialogicUtil.pretty_name(i), 'editor_icon': ["PopupMenu", "EditorIcons"]} return suggestions + +#endregion diff --git a/addons/dialogic/Modules/Background/subsystem_backgrounds.gd b/addons/dialogic/Modules/Background/subsystem_backgrounds.gd index a88e09bd..5312334e 100644 --- a/addons/dialogic/Modules/Background/subsystem_backgrounds.gd +++ b/addons/dialogic/Modules/Background/subsystem_backgrounds.gd @@ -1,29 +1,43 @@ extends DialogicSubsystem - ## Subsystem for managing backgrounds. - -signal background_changed(info:Dictionary) - -var _tween: Tween -var _tween_callbacks: Array[Callable] - +## +## This subsystem has many different helper methods for managing backgrounds. +## For instance, you can listen to background changes via +## [signal background_changed]. + + +## Whenever a new background is set, this signal is emitted and contains a +## dictionary with the following keys: [br] +## [br] +## Key | Value Type | Value [br] +## ----------- | ------------- | ----- [br] +## `scene` | [type String] | The scene path of the new background. [br] +## `argument` | [type String] | Information given to the background on its update routine. [br] +## `fade_time` | [type float] | The time the background may take to transition in. [br] +## `same_scene`| [type bool] | If the new background uses the same Godot scene. [br] +signal background_changed(info: Dictionary) + +## The default background scene Dialogic will use. var default_background_scene: PackedScene = load(get_script().resource_path.get_base_dir().path_join('DefaultBackgroundScene/default_background.tscn')) +## The default transition Dialogic will use. var default_transition: String = get_script().resource_path.get_base_dir().path_join("Transitions/Defaults/simple_fade.gd") -#################################################################################################### -## STATE + +#region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR): +## Empties the current background state. +func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: update_background() - -func load_game_state(load_flag:=LoadFlags.FULL_LOAD): +## Loads the background state from the current state info. +func load_game_state(_load_flag := LoadFlags.FULL_LOAD) -> void: update_background(dialogic.current_state_info.get('background_scene', ''), dialogic.current_state_info.get('background_argument', ''), 0.0, default_transition, true) +#endregion -#################################################################################################### -## MAIN METHODS + +#region MAIN METHODS #################################################################################################### ## Method that adds a given scene as child of the DialogicNode_BackgroundHolder. @@ -35,16 +49,18 @@ func load_game_state(load_flag:=LoadFlags.FULL_LOAD): ## and use the same scene. ## To do so implement [_should_do_background_update()] on the custom background scene. ## Then [_update_background()] will be called directly on that previous scene. -func update_background(scene:String = '', argument:String = '', fade_time:float = 0.0, transition_path:=default_transition, force:bool = false) -> void: +func update_background(scene := "", argument := "", fade_time := 0.0, transition_path:=default_transition, force := false) -> void: var background_holder: DialogicNode_BackgroundHolder if dialogic.has_subsystem('Styles'): - background_holder = Dialogic.Styles.get_first_node_in_layout('dialogic_background_holders') + background_holder = dialogic.Styles.get_first_node_in_layout('dialogic_background_holders') else: background_holder = get_tree().get_first_node_in_group('dialogic_background_holders') + + var info := {'scene':scene, 'argument':argument, 'fade_time':fade_time, 'same_scene':false} if background_holder == null: + background_changed.emit(info) return - var info := {'scene':scene, 'argument':argument, 'fade_time':fade_time, 'same_scene':false} var bg_set := false @@ -83,7 +99,7 @@ func update_background(scene:String = '', argument:String = '', fade_time:float 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, default_transition)) var trans_node := Node.new() trans_node.set_script(trans_script) trans_node = (trans_node as DialogicBackgroundTransition) @@ -94,6 +110,9 @@ func update_background(scene:String = '', argument:String = '', fade_time:float trans_node.prev_scene = old_viewport.get_meta('node', null) trans_node.prev_texture = old_viewport.get_child(0).get_texture() old_viewport.get_meta('node')._custom_fade_out(fade_time) + old_viewport.hide() + # TODO We have to call this again here because of https://github.com/godotengine/godot/issues/23729 + old_viewport.get_child(0).render_target_update_mode = SubViewport.UPDATE_ALWAYS trans_node.transition_finished.connect(old_viewport.queue_free) if new_viewport: trans_node.next_scene = new_viewport.get_meta('node', null) @@ -104,12 +123,28 @@ func update_background(scene:String = '', argument:String = '', fade_time:float background_holder.remove_meta('current_viewport') add_child(trans_node) - trans_node._fade() - trans_node.transition_finished.connect(trans_node.queue_free) + if fade_time == 0: + trans_node.transition_finished.emit() + _on_transition_finished(background_holder, trans_node) + else: + trans_node.transition_finished.connect(_on_transition_finished.bind(background_holder, trans_node)) + # We need to break this connection if the background_holder get's removed during the transition + background_holder.tree_exited.connect(trans_node.disconnect.bind("transition_finished", _on_transition_finished)) + trans_node._fade() background_changed.emit(info) +func _on_transition_finished(background_node:DialogicNode_BackgroundHolder, transition_node:DialogicBackgroundTransition) -> void: + if background_node.has_meta("current_viewport"): + if background_node.get_meta("current_viewport").get_meta("node", null) == transition_node.next_scene: + background_node.get_meta("current_viewport").show() + background_node.material = null + 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: var v_con := SubViewportContainer.new() var viewport := SubViewport.new() @@ -122,7 +157,7 @@ func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder return null parent.add_child(v_con) - v_con.visible = false + v_con.hide() v_con.stretch = true v_con.size = parent.size v_con.set_anchors_preset(Control.PRESET_FULL_RECT) @@ -130,6 +165,7 @@ func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder v_con.add_child(viewport) viewport.transparent_bg = true viewport.disable_3d = true + viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS viewport.add_child(b_scene) b_scene.viewport = viewport @@ -140,8 +176,8 @@ func add_background_node(scene:PackedScene, parent:DialogicNode_BackgroundHolder return v_con - +## Whether a background is set. func has_background() -> bool: - return !dialogic.current_state_info['background_scene'].is_empty() or !dialogic.current_state_info['background_argument'].is_empty() - + return !dialogic.current_state_info.get('background_scene', '').is_empty() or !dialogic.current_state_info.get('background_argument','').is_empty() +#endregion diff --git a/addons/dialogic/Modules/Call/event_call.gd b/addons/dialogic/Modules/Call/event_call.gd index 2680a17d..cd9cbaaf 100644 --- a/addons/dialogic/Modules/Call/event_call.gd +++ b/addons/dialogic/Modules/Call/event_call.gd @@ -29,21 +29,33 @@ var _current_method_arg_hints := {'a':null, 'm':null, 'info':{}} ################################################################################ func _execute() -> void: - var autoload = dialogic.get_node('/root/'+autoload_name) - if autoload == null: + var object: Object = null + var obj_path := autoload_name + var autoload: Node = dialogic.get_node('/root/'+obj_path.get_slice('.', 0)) + obj_path = obj_path.trim_prefix(obj_path.get_slice('.', 0)+'.') + object = autoload + if object: + while obj_path: + if obj_path.get_slice(".", 0) in object and object.get(obj_path.get_slice(".", 0)) is Object: + object = object.get(obj_path.get_slice(".", 0)) + else: + break + obj_path = obj_path.trim_prefix(obj_path.get_slice('.', 0)+'.') + + if object == null: printerr("[Dialogic] Call event failed: Unable to find autoload '",autoload_name,"'") finish() return - if autoload.has_method(method): + if object.has_method(method): var args := [] for arg in arguments: if arg is String and arg.begins_with('@'): - args.append(dialogic.Expression.execute_string(arg.trim_prefix('@'))) + args.append(dialogic.Expressions.execute_string(arg.trim_prefix('@'))) else: args.append(arg) dialogic.current_state = dialogic.States.WAITING - await autoload.callv(method, args) + await object.callv(method, args) dialogic.current_state = dialogic.States.IDLE else: printerr("[Dialogic] Call event failed: Autoload doesn't have the method '", method,"'.") @@ -88,7 +100,7 @@ func to_text() -> String: func from_text(string:String) -> void: - var result := RegEx.create_from_string('do (?[^\\.]*)(.(?[^.(]*)(\\((?.*)\\))?)?').search(string.strip_edges()) + var result := RegEx.create_from_string(r"do (?[^\(]*)\.((?[^.(]*)(\((?.*)\))?)?").search(string.strip_edges()) if result: autoload_name = result.get_string('autoload') method = result.get_string('method') @@ -126,11 +138,11 @@ func get_shortcode_parameters() -> Dictionary: ################################################################################ func build_event_editor(): - add_header_edit('autoload_name', ValueType.COMPLEX_PICKER, {'left_text':'On autoload', + add_header_edit('autoload_name', ValueType.DYNAMIC_OPTIONS, {'left_text':'On autoload', 'empty_text':'Autoload', 'suggestions_func':get_autoload_suggestions, 'editor_icon':["Node", "EditorIcons"]}) - add_header_edit('method', ValueType.COMPLEX_PICKER, {'left_text':'call', + add_header_edit('method', ValueType.DYNAMIC_OPTIONS, {'left_text':'call', 'empty_text':'Method', 'suggestions_func':get_method_suggestions, 'editor_icon':["Callable", "EditorIcons"]}, 'autoload_name') @@ -145,27 +157,40 @@ func get_autoload_suggestions(filter:String="") -> Dictionary: if prop.name.begins_with('autoload/'): var autoload: String = prop.name.trim_prefix('autoload/') suggestions[autoload] = {'value': autoload, 'tooltip':autoload, 'editor_icon': ["Node", "EditorIcons"]} + if filter.begins_with(autoload): + suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]} return suggestions func get_method_suggestions(filter:String="", temp_autoload:String = "") -> Dictionary: var suggestions := {} - var script : Script + var script: Script if temp_autoload: script = load(ProjectSettings.get_setting('autoload/'+temp_autoload).trim_prefix('*')) - elif autoload_name: - script = load(ProjectSettings.get_setting('autoload/'+autoload_name).trim_prefix('*')) + + elif autoload_name and ProjectSettings.has_setting('autoload/'+autoload_name): + 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('_'): continue suggestions[method.name] = {'value': method.name, 'tooltip':method.name, 'editor_icon': ["Callable", "EditorIcons"]} + if !filter.is_empty(): + suggestions[filter] = {'value': filter, 'editor_icon':["GuiScrollArrowRight", "EditorIcons"]} return suggestions func update_argument_info() -> void: - if autoload_name and method and not (_current_method_arg_hints.a == autoload_name and _current_method_arg_hints.m == method): + if autoload_name and method and not _current_method_arg_hints.is_empty() and (_current_method_arg_hints.a == autoload_name and _current_method_arg_hints.m == method): if !ResourceLoader.exists(ProjectSettings.get_setting('autoload/'+autoload_name, '').trim_prefix('*')): _current_method_arg_hints = {} return @@ -177,7 +202,7 @@ func update_argument_info() -> void: func check_arguments_and_update_warning(): - if _current_method_arg_hints.info.is_empty(): + if not _current_method_arg_hints.has("info") or _current_method_arg_hints.info.is_empty(): ui_update_warning.emit() return diff --git a/addons/dialogic/Modules/Character/event_character.gd b/addons/dialogic/Modules/Character/event_character.gd index 8fb0c543..775099d5 100644 --- a/addons/dialogic/Modules/Character/event_character.gd +++ b/addons/dialogic/Modules/Character/event_character.gd @@ -72,12 +72,13 @@ func _execute() -> void: Actions.JOIN: if character: if dialogic.has_subsystem('History') and !dialogic.Portraits.is_character_joined(character): - dialogic.History.store_simple_history_entry(character.display_name + " joined", event_name, {'character': character.display_name, 'mode':'Join'}) + 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 dialogic.Input.auto_skip.enabled: - var max_time: float = dialogic.Input.auto_skip.time_per_event + 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( @@ -88,8 +89,8 @@ func _execute() -> void: Actions.LEAVE: var final_animation_length: float = animation_length - if dialogic.Input.auto_skip.enabled: - var max_time: float = dialogic.Input.auto_skip.time_per_event + 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) if character_identifier == '--All--': @@ -105,7 +106,8 @@ func _execute() -> void: elif character: if dialogic.has_subsystem('History') and dialogic.Portraits.is_character_joined(character): - dialogic.History.store_simple_history_entry(character.display_name+" left", event_name, {'character': character.display_name, 'mode':'Leave'}) + 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, @@ -131,8 +133,8 @@ func _execute() -> void: if set_position: var final_position_move_time: float = position_move_time - if dialogic.Input.auto_skip.enabled: - var max_time: float = dialogic.Input.auto_skip.time_per_event + 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) dialogic.Portraits.move_character(character, position, final_position_move_time) @@ -141,8 +143,8 @@ func _execute() -> void: var final_animation_length: float = animation_length var final_animation_repitions: int = animation_repeats - if dialogic.Input.auto_skip.enabled: - var time_per_event: float = dialogic.Input.auto_skip.time_per_event + 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 @@ -335,8 +337,8 @@ func is_valid_event(string:String) -> bool: ################################################################################ func build_event_editor() -> void: - add_header_edit('action', ValueType.FIXED_OPTION_SELECTOR, { - 'selector_options': [ + add_header_edit('action', ValueType.FIXED_OPTIONS, { + 'options': [ { 'label': 'Join', 'value': Actions.JOIN, @@ -354,51 +356,52 @@ func build_event_editor() -> void: } ] }) - add_header_edit('character_identifier', ValueType.COMPLEX_PICKER, + add_header_edit('character_identifier', ValueType.DYNAMIC_OPTIONS, {'placeholder' : 'Character', 'file_extension' : '.dch', + 'mode' : 2, '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, + add_header_edit('set_portrait', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Character/update_portrait.svg"), 'tooltip':'Change Portrait'}, "should_show_portrait_selector() and action == Actions.UPDATE") - add_header_edit('portrait', ValueType.COMPLEX_PICKER, + add_header_edit('portrait', ValueType.DYNAMIC_OPTIONS, {'placeholder' : 'Default', 'collapse_when_empty':true, 'suggestions_func' : get_portrait_suggestions, 'icon' : load("res://addons/dialogic/Editor/Images/Resources/portrait.svg")}, 'should_show_portrait_selector() and (action != Actions.UPDATE or set_portrait)') - add_header_edit('set_position', ValueType.BOOL, + add_header_edit('set_position', 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.INTEGER, {}, + 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) # Body - add_body_edit('animation_name', ValueType.COMPLEX_PICKER, + add_body_edit('animation_name', ValueType.DYNAMIC_OPTIONS, {'left_text' : 'Animation:', 'suggestions_func' : get_animation_suggestions, 'editor_icon' : ["Animation", "EditorIcons"], 'placeholder' : 'Default', 'enable_pretty_name' : true}, 'should_show_animation_options()') - add_body_edit('animation_length', ValueType.FLOAT, {'left_text':'Length:'}, + add_body_edit('animation_length', ValueType.NUMBER, {'left_text':'Length:', 'suffix':'s'}, '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.INTEGER, {'left_text':'Repeat:'}, + add_body_edit('animation_repeats', ValueType.NUMBER, {'left_text':'Repeat:', 'mode':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.FLOAT, {'left_text':'Movement duration:'}, + add_body_edit('position_move_time', ValueType.NUMBER, {'left_text':'Movement duration:'}, 'action == %s and set_position' %Actions.UPDATE) - add_body_edit('set_z_index', ValueType.BOOL, {'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.INTEGER, {'left_text':'Z-index:'}, + 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, {'icon':load("res://addons/dialogic/Modules/Character/update_mirror.svg"), 'tooltip':'Change Mirroring'}, "character != null and action == Actions.UPDATE") + 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) @@ -435,6 +438,8 @@ func get_portrait_suggestions(search_text:String) -> Dictionary: suggestions["Don't Change"] = {'value':'', 'editor_icon':["GuiRadioUnchecked", "EditorIcons"]} if action == Actions.JOIN: suggestions["Default portrait"] = {'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.duplicate()} diff --git a/addons/dialogic/Modules/Character/event_position.gd b/addons/dialogic/Modules/Character/event_position.gd index a0f3fcbd..f46ae9af 100644 --- a/addons/dialogic/Modules/Character/event_position.gd +++ b/addons/dialogic/Modules/Character/event_position.gd @@ -26,8 +26,8 @@ var movement_time: float = 0 func _execute() -> void: var final_movement_time: float = movement_time - if dialogic.Input.auto_skip.enabled: - var time_per_event: float = dialogic.Input.auto_skip.time_per_event + 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: @@ -81,8 +81,8 @@ func get_shortcode_parameters() -> Dictionary: ################################################################################ func build_event_editor(): - add_header_edit('action', ValueType.FIXED_OPTION_SELECTOR, { - 'selector_options': [ + add_header_edit('action', ValueType.FIXED_OPTIONS, { + 'options': [ { 'label': 'Change', 'value': Actions.SET_RELATIVE, @@ -101,10 +101,10 @@ func build_event_editor(): } ] }) - add_header_edit("position", ValueType.INTEGER, {'left_text':"position"}, + 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.FLOAT, {'left_text':"AnimationTime:", "right_text":"(0 for instant)"}) + add_body_edit("movement_time", ValueType.NUMBER, {'left_text':"AnimationTime:", "right_text":"(0 for instant)"}) diff --git a/addons/dialogic/Modules/Character/index.gd b/addons/dialogic/Modules/Character/index.gd index 0e893c49..02fb5a0a 100644 --- a/addons/dialogic/Modules/Character/index.gd +++ b/addons/dialogic/Modules/Character/index.gd @@ -18,4 +18,4 @@ func _get_text_effects() -> Array[Dictionary]: func _get_special_resources() -> Array[Dictionary]: - return list_special_resources('DefaultAnimations', 'PortraitAnimation') + return list_special_resources('DefaultAnimations', &'PortraitAnimation') diff --git a/addons/dialogic/Modules/Character/settings_portraits.gd b/addons/dialogic/Modules/Character/settings_portraits.gd index 702319a1..a637d9f4 100644 --- a/addons/dialogic/Modules/Character/settings_portraits.gd +++ b/addons/dialogic/Modules/Character/settings_portraits.gd @@ -4,9 +4,9 @@ extends DialogicSettingsPage func _ready(): %JoinDefault.get_suggestions_func = get_join_animation_suggestions - %JoinDefault.enable_pretty_name = true + %JoinDefault.mode = 1 %LeaveDefault.get_suggestions_func = get_leave_animation_suggestions - %LeaveDefault.enable_pretty_name = true + %LeaveDefault.mode = 1 func _refresh(): diff --git a/addons/dialogic/Modules/Character/settings_portraits.tscn b/addons/dialogic/Modules/Character/settings_portraits.tscn index 1533c2af..ad39f917 100644 --- a/addons/dialogic/Modules/Character/settings_portraits.tscn +++ b/addons/dialogic/Modules/Character/settings_portraits.tscn @@ -2,10 +2,10 @@ [ext_resource type="Script" path="res://addons/dialogic/Modules/Character/settings_portraits.gd" id="2"] [ext_resource type="PackedScene" uid="uid://dbpkta2tjsqim" path="res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn" id="2_dce78"] -[ext_resource type="PackedScene" uid="uid://dpwhshre1n4t6" path="res://addons/dialogic/Editor/Events/Fields/ComplexPicker.tscn" id="3"] -[ext_resource type="PackedScene" uid="uid://7mvxuaulctcq" path="res://addons/dialogic/Editor/Events/Fields/FilePicker.tscn" id="3_m06d8"] +[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_orkay"] +[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", @@ -14,8 +14,8 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_3he48"] -image = SubResource("Image_orkay") +[sub_resource type="ImageTexture" id="ImageTexture_wre7v"] +image = SubResource("Image_8p738") [node name="Portraits" type="VBoxContainer"] anchors_preset = 15 @@ -37,7 +37,7 @@ text = "Default Portrait Scene [node name="HintTooltip" parent="Animations3" 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_3he48") +texture = SubResource("ImageTexture_wre7v") 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="."] @@ -45,7 +45,7 @@ layout_mode = 2 [node name="Label" type="Label" parent="HBoxContainer"] layout_mode = 2 -text = "Scene:" +text = "Scene" [node name="CustomPortraitScene" parent="HBoxContainer" instance=ExtResource("3_m06d8")] unique_name_in_owner = true @@ -67,7 +67,7 @@ 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_3he48") +texture = SubResource("ImageTexture_wre7v") hint_text = "These settings are used for Leave and Join events if no animation is selected." [node name="GridContainer" type="GridContainer" parent="."] @@ -76,7 +76,7 @@ columns = 2 [node name="Label3" type="Label" parent="GridContainer"] layout_mode = 2 -text = "Join:" +text = "Join" [node name="DefaultIn" type="HBoxContainer" parent="GridContainer"] layout_mode = 2 @@ -84,7 +84,6 @@ layout_mode = 2 [node name="JoinDefault" parent="GridContainer/DefaultIn" instance=ExtResource("3")] unique_name_in_owner = true layout_mode = 2 -enable_pretty_name = true [node name="JoinDefaultLength" type="SpinBox" parent="GridContainer/DefaultIn"] unique_name_in_owner = true @@ -98,7 +97,7 @@ text = "Wait:" [node name="Label4" type="Label" parent="GridContainer"] layout_mode = 2 -text = "Leave:" +text = "Leave" [node name="DefaultOut" type="HBoxContainer" parent="GridContainer"] layout_mode = 2 @@ -106,7 +105,6 @@ layout_mode = 2 [node name="LeaveDefault" parent="GridContainer/DefaultOut" instance=ExtResource("3")] unique_name_in_owner = true layout_mode = 2 -enable_pretty_name = true [node name="LeaveDefaultLength" type="SpinBox" parent="GridContainer/DefaultOut"] unique_name_in_owner = true diff --git a/addons/dialogic/Modules/Character/subsystem_portraits.gd b/addons/dialogic/Modules/Character/subsystem_portraits.gd index 52c5b1f9..b28368db 100644 --- a/addons/dialogic/Modules/Character/subsystem_portraits.gd +++ b/addons/dialogic/Modules/Character/subsystem_portraits.gd @@ -10,35 +10,37 @@ signal position_changed(info:Dictionary) ## The default portrait scene. -var default_portrait_scene :PackedScene = load(get_script().resource_path.get_base_dir().path_join('default_portrait.tscn')) +var default_portrait_scene: PackedScene = load(get_script().resource_path.get_base_dir().path_join('default_portrait.tscn')) -## A reference to the current [DialogicNode_PortraitHolder]. -var _portrait_holder_reference: Node = null -#################################################################################################### -## STATE +#region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR): +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'] = {} - dialogic.current_state_info['speaker_portraits'] = {} -func load_game_state(load_flag:=LoadFlags.FULL_LOAD): - var portraits_info :Dictionary = dialogic.current_state_info.portraits.duplicate() +func load_game_state(load_flag:=LoadFlags.FULL_LOAD) -> void: + if not "portraits" in dialogic.current_state_info: + dialogic.current_state_info["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] + 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) - if dialogic.current_state_info.get('speaker', ''): - change_speaker(load(dialogic.current_state_info['speaker'])) + var speaker: Variant = dialogic.current_state_info.get('speaker', "") + if speaker: + dialogic.current_state_info['speaker'] = "" + change_speaker(load(speaker)) + dialogic.current_state_info['speaker'] = speaker func pause() -> void: @@ -57,9 +59,10 @@ 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 -################### Main Methods ################################################################## +#region MAIN METHODS #################################################################################################### ## The following methods allow manipulating portraits. ## A portrait is made up of a character node [Node2D] that instances the portrait scene as it's child. @@ -83,7 +86,7 @@ func _create_character_node(character:DialogicCharacter, container:DialogicNode_ # Changes the portrait of a specific [character node]. func _change_portrait(character_node:Node2D, portrait:String, update_transform:=true) -> Dictionary: - var character :DialogicCharacter = character_node.get_meta('character') + var character: DialogicCharacter = character_node.get_meta('character') if portrait.is_empty(): portrait = character.default_portrait @@ -94,9 +97,9 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= return info # path to the scene to use - var scene_path :String = character.portraits[portrait].get('scene', '') + var scene_path: String = character.portraits[portrait].get('scene', '') - var portrait_node : Node = null + var portrait_node: Node = null # check if the scene is the same as the currently loaded scene if (character_node.get_child_count() and @@ -113,9 +116,9 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= character_node.remove_child(character_node.get_child(0)) if ResourceLoader.exists(scene_path): - var p: PackedScene = load(scene_path) - if p: - portrait_node = p.instantiate() + var packed_scene: PackedScene = load(scene_path) + if packed_scene: + portrait_node = packed_scene.instantiate() else: push_error('[Dialogic] Portrait node "' + str(scene_path) + '" for character [' + character.display_name + '] could not be loaded. Your portrait might not show up on the screen. Confirm the path is correct.') @@ -146,7 +149,7 @@ func _change_portrait(character_node:Node2D, portrait:String, update_transform:= ## portrait mirror and portrait position mirror settings. func _change_portrait_mirror(character_node:Node2D, mirrored:=false, force:=false) -> void: if character_node.get_child(0).has_method('_set_mirror'): - var character :DialogicCharacter= character_node.get_meta('character') + var 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))) @@ -157,18 +160,18 @@ func _change_portrait_extradata(character_node:Node2D, extra_data:="") -> void: func _update_portrait_transform(character_node:Node2D, time:float = 0.0) -> void: - var character :DialogicCharacter= character_node.get_meta('character') + 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'), {}) + var portrait_node: Node = character_node.get_child(0) + var portrait_info: Dictionary = character.portraits.get(character_node.get_meta('portrait'), {}) # ignore the character scale on custom portraits that have 'ignore_char_scale' set to true - var apply_character_scale :bool= !portrait_info.get('ignore_char_scale', false) - var transform :Rect2 = character_node.get_parent().get_local_portrait_transform( + var apply_character_scale: bool= !portrait_info.get('ignore_char_scale', false) + var transform: Rect2 = character_node.get_parent().get_local_portrait_transform( portrait_node._get_covered_rect(), (character.scale * portrait_info.get('scale', 1))*int(apply_character_scale)+portrait_info.get('scale', 1)*int(!apply_character_scale)) - var tween : Tween + 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() @@ -188,11 +191,11 @@ func _update_portrait_transform(character_node:Node2D, time:float = 0.0) -> void ## 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: +func _animate_portrait(character_node:Node2D, animation_path:String, length:float, repeats := 1) -> DialogicAnimation: if character_node.has_meta('animation_node') and is_instance_valid(character_node.get_meta('animation_node')): character_node.get_meta('animation_node').queue_free() - var anim_script :Script = load(animation_path) + var anim_script: Script = load(animation_path) var anim_node := Node.new() anim_node.set_script(anim_script) anim_node = (anim_node as DialogicAnimation) @@ -209,7 +212,7 @@ func _animate_portrait(character_node:Node2D, animation_path:String, length:floa ## Moves the given portrait to the given container. -func _move_portrait(character_node:Node2D, portrait_container:DialogicNode_PortraitContainer, time:float = 0.0) -> void: +func _move_portrait(character_node:Node2D, portrait_container:DialogicNode_PortraitContainer, time:= 0.0) -> void: var global_pos := character_node.global_position if character_node.get_parent(): character_node.get_parent().remove_child(character_node) @@ -225,12 +228,10 @@ func _change_portrait_z_index(character_node:Node2D, z_index:int, update_zindex: if update_zindex: character_node.get_parent().set_meta('z_index', z_index) - var portait_siblings := character_node.get_parent().get_parent().get_children() - var portraits: = portait_siblings.filter( - func(portrait): return portrait is DialogicNode_PortraitContainer) - portraits.sort_custom(z_sort_portrait_containers) + var sorted_children := character_node.get_parent().get_parent().get_children() + sorted_children.sort_custom(z_sort_portrait_containers) var idx := 0 - for con in portraits: + for con in sorted_children: con.get_parent().move_child(con, idx) idx += 1 @@ -245,28 +246,55 @@ func _remove_portrait(character_node:Node2D) -> void: character_node.get_parent().remove_child(character_node) character_node.queue_free() + ## Gets the default animation length for joining characters ## If Auto-Skip is enabled, limits the time. func _get_join_default_length() -> float: - var default_time = ProjectSettings.get_setting('dialogic/animations/join_default_length', 0.5) + var default_time: float = ProjectSettings.get_setting('dialogic/animations/join_default_length', 0.5) - if dialogic.Input.auto_skip.enabled: - default_time = min(default_time, dialogic.Input.auto_skip.time_per_event) + if dialogic.Inputs.auto_skip.enabled: + default_time = min(default_time, dialogic.Inputs.auto_skip.time_per_event) return default_time + ## Gets the default animation length for leaving characters ## If Auto-Skip is enabled, limits the time. func _get_leave_default_length() -> float: - var default_time = ProjectSettings.get_setting('dialogic/animations/leave_default_length', 0.5) + var default_time: float = ProjectSettings.get_setting('dialogic/animations/leave_default_length', 0.5) - if dialogic.Input.auto_skip.enabled: - default_time = min(default_time, dialogic.Input.auto_skip.time_per_event) + if dialogic.Inputs.auto_skip.enabled: + default_time = min(default_time, dialogic.Inputs.auto_skip.time_per_event) return default_time -################### Character Methods ############################################################# +## 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 := dialogic.Expressions.execute_string(portrait) + if test: + portrait = str(test) + + if not portrait in character.portraits: + portrait = character.default_portrait + printerr('[Dialogic] Tried to use nonexistant portrait "', portrait, '" on character "', DialogicResourceUtil.get_unique_identifier(character.resource_path), '". Using default portrait instead.') + dialogic.print_debug_moment() + + if portrait.is_empty(): + portrait = character.default_portrait + + return portrait + +#endregion + + +#region Character Methods #################################################################################################### ## The following methods are used to manage character portraits with the following rules: ## - a character can only be present once with these methods. @@ -275,7 +303,7 @@ 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: bool = false, z_index: int = 0, extra_data:String = "", animation_name:String = "", animation_length:float = 0, animation_wait := false) -> Node: +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: if is_character_joined(character): change_character_portrait(character, portrait) if animation_name.is_empty(): @@ -310,7 +338,7 @@ func join_character(character:DialogicCharacter, portrait:String, position_idx: animation_name = DialogicResourceUtil.guess_special_resource("PortraitAnimation", animation_name, "") if animation_name and animation_length > 0: - var anim:DialogicAnimation = _animate_portrait(character_node, animation_name, animation_length) + var anim: DialogicAnimation = _animate_portrait(character_node, animation_name, animation_length) if animation_wait: dialogic.current_state = DialogicGameHandler.States.ANIMATING @@ -325,24 +353,19 @@ func add_character(character:DialogicCharacter, portrait:String, position_idx:i 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 + var character_node: Node = null for portrait_position in get_tree().get_nodes_in_group('dialogic_portrait_con_position'): - if portrait_position.is_inside_tree() and portrait_position.position_index == position_idx: + if portrait_position.is_visible_in_tree() and portrait_position.position_index == position_idx: character_node = _create_character_node(character, portrait_position) break @@ -362,6 +385,8 @@ func change_character_portrait(character:DialogicCharacter, portrait:String, upd if !is_character_joined(character): return + portrait = get_valid_portrait(character, portrait) + if dialogic.current_state_info.portraits[character.resource_path].portrait == portrait: return @@ -401,7 +426,7 @@ 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: +func animate_character(character:DialogicCharacter, animation_path:String, length:float, repeats := 1) -> DialogicAnimation: if !is_character_joined(character): return null @@ -411,7 +436,7 @@ func animate_character(character:DialogicCharacter, animation_path:String, lengt ## Moves the given character to the given position. Only works with joined characters -func move_character(character:DialogicCharacter, position_idx:int, time:float = 0.0) -> void: +func move_character(character:DialogicCharacter, position_idx:int, time:= 0.0) -> void: if !is_character_joined(character): return @@ -429,7 +454,7 @@ func move_character(character:DialogicCharacter, position_idx:int, time:float = ## Removes a character with a given animation or the default animation. -func leave_character(character:DialogicCharacter, animation_name :String = "", animation_length:float = 0, animation_wait := false) -> void: +func leave_character(character:DialogicCharacter, animation_name:= "", animation_length:= 0.0, animation_wait := false) -> void: if !is_character_joined(character): return @@ -455,7 +480,7 @@ func leave_character(character:DialogicCharacter, animation_name :String = "", a ## Removes all joined characters with a given animation or the default animation. -func leave_all_characters(animation_name:String="", animation_length:float= 0, animation_wait:= false) -> void: +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) @@ -482,7 +507,7 @@ func remove_character(character:DialogicCharacter) -> void: ## Returns true if the given character is currently joined. func is_character_joined(character:DialogicCharacter) -> bool: - if !character.resource_path in dialogic.current_state_info['portraits']: + if not character or !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): @@ -492,7 +517,7 @@ func is_character_joined(character:DialogicCharacter) -> bool: ## Returns a list of the joined charcters (as resources) func get_joined_characters() -> Array[DialogicCharacter]: - var chars :Array[DialogicCharacter]= [] + var chars: Array[DialogicCharacter]= [] for char_path in dialogic.current_state_info.get('portraits', {}).keys(): chars.append(load(char_path)) return chars @@ -503,15 +528,16 @@ func get_joined_characters() -> Array[DialogicCharacter]: ## Only joined is included (and false) for not joined characters func get_character_info(character:DialogicCharacter) -> Dictionary: if is_character_joined(character): - var info :Dictionary = dialogic.current_state_info['portraits'][character.resource_path] + var info: Dictionary = dialogic.current_state_info['portraits'][character.resource_path] info['joined'] = true return info else: return {'joined':false} +#endregion -################### Positions ##################################################################### +#region Positions #################################################################################################### func get_portrait_container(postion_index:int) -> DialogicNode_PortraitContainer: @@ -537,7 +563,7 @@ func add_portrait_position(position_index: int, position:Vector2) -> void: position_changed.emit({'change':'added', 'container_node':new_position, 'position_index':position_index}) -func move_portrait_position(position_index: int, vector:Vector2, relative:bool = false, time:float = 0.0) -> void: +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'): @@ -555,26 +581,27 @@ func move_portrait_position(position_index: int, vector:Vector2, relative:bool = add_portrait_position(position_index, vector) -func reset_all_portrait_positions(time:float = 0.0) -> void: +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:float = 0.0) -> void: +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 -################## SPEAKER PORTRAIT CONTAINERS ##################################################### +#region SPEAKER PORTRAIT CONTAINERS #################################################################################################### ## Updates all portrait containers set to SPEAKER. -func change_speaker(speaker:DialogicCharacter= null, portrait:= ""): +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: @@ -601,7 +628,6 @@ func change_speaker(speaker:DialogicCharacter= null, portrait:= ""): _change_portrait_mirror(con.get_child(0)) - 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'])): @@ -611,7 +637,10 @@ func change_speaker(speaker:DialogicCharacter= null, portrait:= ""): 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() -################### TEXT EFFECTS ################################################################### +#endregion + + +#region TEXT EFFECTS #################################################################################################### ## Called from the [portrait=something] text effect. @@ -620,3 +649,4 @@ func text_effect_portrait(text_node:Control, skipped:bool, argument:String) -> v if dialogic.current_state_info.get('speaker', null): change_character_portrait(load(dialogic.current_state_info.speaker), argument) change_speaker(load(dialogic.current_state_info.speaker), argument) +#endregion diff --git a/addons/dialogic/Modules/Character/update_mirror.svg.import b/addons/dialogic/Modules/Character/update_mirror.svg.import index ec935973..74a5f39e 100644 --- a/addons/dialogic/Modules/Character/update_mirror.svg.import +++ b/addons/dialogic/Modules/Character/update_mirror.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://c735ss4h37y8i" path="res://.godot/imported/update_mirror.svg-d6fa4832a7758cad02a7f32282433230.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Character/update_portrait.svg.import b/addons/dialogic/Modules/Character/update_portrait.svg.import index 3302870f..9ceb2abb 100644 --- a/addons/dialogic/Modules/Character/update_portrait.svg.import +++ b/addons/dialogic/Modules/Character/update_portrait.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://qx5bntelnslj" path="res://.godot/imported/update_portrait.svg-b90fa6163d3720d34df8578ce2aa35e1.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Character/update_position.svg.2023_09_23_08_37_47.0.svg.import b/addons/dialogic/Modules/Character/update_position.svg.2023_09_23_08_37_47.0.svg.import index 97c5fb3d..1496c178 100644 --- a/addons/dialogic/Modules/Character/update_position.svg.2023_09_23_08_37_47.0.svg.import +++ b/addons/dialogic/Modules/Character/update_position.svg.2023_09_23_08_37_47.0.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://deu1h3rsp3p8y" path="res://.godot/imported/update_position.svg.2023_09_23_08_37_47.0.svg-d66e70a05e9de92a595b70fa88fca0d4.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Character/update_position.svg.import b/addons/dialogic/Modules/Character/update_position.svg.import index b6799988..0d90b2fb 100644 --- a/addons/dialogic/Modules/Character/update_position.svg.import +++ b/addons/dialogic/Modules/Character/update_position.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://rslog4jar4gu" path="res://.godot/imported/update_position.svg-4be50608dc0768e715ff659ca62cf187.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Character/update_z_index.svg.import b/addons/dialogic/Modules/Character/update_z_index.svg.import index 7ae30996..706b609f 100644 --- a/addons/dialogic/Modules/Character/update_z_index.svg.import +++ b/addons/dialogic/Modules/Character/update_z_index.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://c5xa3i541igyk" path="res://.godot/imported/update_z_index.svg-204064db9241de79bf8dfd23af57c6c6.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Choice/event_choice.gd b/addons/dialogic/Modules/Choice/event_choice.gd index 7abc045a..1eef08c1 100644 --- a/addons/dialogic/Modules/Choice/event_choice.gd +++ b/addons/dialogic/Modules/Choice/event_choice.gd @@ -4,7 +4,7 @@ extends DialogicEvent ## Event that allows adding choices. Needs to go after a text event (or another choices EndBranch). -enum ElseActions {HIDE, DISABLE, DEFAULT} +enum ElseActions {HIDE=0, DISABLE=1, DEFAULT=2} ### Settings @@ -18,31 +18,22 @@ var else_action: = ElseActions.DEFAULT ## If empty [text] will be used for disabled button as well. var disabled_text: String = "" +#endregion -################################################################################ -## EXECUTION + +#region EXECUTION ################################################################################ func _execute() -> void: - # This event is mostly a placeholder that's used to indicate a position. - # Only the selected choice is reached. - # However mainly the Choices Subsystem queries the events - # to find the choices that belong to the question. - if !dialogic.Choices.last_question_info.has('choices'): - finish() - return - if dialogic.has_subsystem('History'): - var all_choices : Array = dialogic.Choices.last_question_info['choices'] - if dialogic.has_subsystem('VAR'): - dialogic.History.store_simple_history_entry(dialogic.VAR.parse_variables(text), event_name, {'all_choices': all_choices}) - else: - dialogic.History.store_simple_history_entry(text, event_name, {'all_choices': all_choices}) - finish() + if dialogic.Choices.is_question(dialogic.current_event_idx): + dialogic.Choices.show_current_choices(false) + dialogic.current_state = dialogic.States.AWAITING_CHOICE +#endregion -################################################################################ -## INITIALIZE + +#region INITIALIZE ################################################################################ func _init() -> void: @@ -51,20 +42,16 @@ func _init() -> void: event_category = "Flow" event_sorting_index = 0 can_contain_events = true - needs_parent_event = true - - -# if needs_parent_event is true, this needs to return true if the event is that event -func is_expected_parent_event(event:DialogicEvent) -> bool: - return event is DialogicTextEvent + wants_to_group = true # return a control node that should show on the END BRANCH node func get_end_branch_control() -> Control: return load(get_script().resource_path.get_base_dir().path_join('ui_choice_end.tscn')).instantiate() +#endregion -################################################################################ -## SAVING/LOADING + +#region SAVING/LOADING ################################################################################ func to_text() -> String: @@ -112,9 +99,9 @@ func is_valid_event(string:String) -> bool: return true return false +#endregion -################################################################################ -## TRANSLATIONS +#region TRANSLATIONS ################################################################################ func _get_translatable_properties() -> Array: @@ -128,17 +115,17 @@ func _get_property_original_translation(property:String) -> String: 'disabled_text': return disabled_text return '' +#endregion -################################################################################ -## EDITOR REPRESENTATION +#region EDITOR REPRESENTATION ################################################################################ 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_OPTION_SELECTOR, {'left_text':'else ', - 'selector_options': [ + add_body_edit("else_action", ValueType.FIXED_OPTIONS, {'left_text':'else ', + 'options': [ { 'label': 'Default', 'value': ElseActions.DEFAULT, @@ -162,9 +149,10 @@ func allow_alt_text() -> bool: else_action == ElseActions.DISABLE or (else_action == ElseActions.DEFAULT and ProjectSettings.get_setting("dialogic/choices/def_false_behaviour", 0) == 1)) +#endregion -####################### CODE COMPLETION ######################################## +#region CODE COMPLETION ################################################################################ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:String, word:String, symbol:String) -> void: @@ -189,9 +177,10 @@ func _get_code_completion(CodeCompletionHelper:Node, TextNode:TextEdit, line:Str TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'default', "default", event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5), null, '"') TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'hide', "hide", event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5), null, '"') TextNode.add_code_completion_option(CodeEdit.KIND_MEMBER, 'disable', "disable", event_color.lerp(TextNode.syntax_highlighter.normal_color, 0.5), null, '"') +#endregion -#################### SYNTAX HIGHLIGHTING ####################################### +#region SYNTAX HIGHLIGHTING ################################################################################ func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, line:String) -> Dictionary: @@ -204,3 +193,5 @@ func _get_syntax_highlighting(Highlighter:SyntaxHighlighter, dict:Dictionary, li dict = Highlighter.color_condition(dict, line, from, line.find(']', from)) dict = Highlighter.color_shortcode_content(dict, line, line.find(']', from), 0,event_color) return dict +#endregion + diff --git a/addons/dialogic/Modules/Choice/icon.svg.import b/addons/dialogic/Modules/Choice/icon.svg.import index 39536fe0..2198f10a 100644 --- a/addons/dialogic/Modules/Choice/icon.svg.import +++ b/addons/dialogic/Modules/Choice/icon.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://wd4fnxjfmj5m" path="res://.godot/imported/icon.svg-372a9bfce8bea67fff1988d7acf09a9a.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Choice/node_choice_button.gd b/addons/dialogic/Modules/Choice/node_choice_button.gd index ae5de2a9..1b0dfe8a 100644 --- a/addons/dialogic/Modules/Choice/node_choice_button.gd +++ b/addons/dialogic/Modules/Choice/node_choice_button.gd @@ -1,10 +1,20 @@ class_name DialogicNode_ChoiceButton extends Button +## The button allows the player to make a choice in the Dialogic system. +## +## This class is used in the Choice Layer. [br] +## You may change the [member text_node] to any [class Node] that has a +## `text` property. [br] +## If you don't set the [member text_node], the text will be set on this +## button instead. +## +## Using a different node may allow using rich text effects; they are +## not supported on buttons at this point. + -## Dialogic Node that displays choices. ## Used to identify what choices to put on. If you leave it at -1, choices will be distributed automatically. -@export var choice_index:int = -1 +@export var choice_index: int = -1 ## Can be set to play this sound when pressed. Requires a sibling DialogicNode_ButtonSound node. @export var sound_pressed: AudioStream @@ -12,9 +22,20 @@ extends Button @export var sound_hover: AudioStream ## Can be set to play this sound when focused. Requires a sibling DialogicNode_ButtonSound node. @export var sound_focus: AudioStream +## If set, the text will be set on this node's `text` property instead. +@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(): +func _ready() -> void: add_to_group('dialogic_choice_button') shortcut_in_tooltip = false hide() diff --git a/addons/dialogic/Modules/Choice/settings_choices.gd b/addons/dialogic/Modules/Choice/settings_choices.gd index b40fae5b..cfd736d8 100644 --- a/addons/dialogic/Modules/Choice/settings_choices.gd +++ b/addons/dialogic/Modules/Choice/settings_choices.gd @@ -6,7 +6,7 @@ func _refresh() -> void: %Delay.value = ProjectSettings.get_setting('dialogic/choices/delay', 0.2) %FalseBehaviour.select(ProjectSettings.get_setting('dialogic/choices/def_false_behaviour', 0)) %HotkeyType.select(ProjectSettings.get_setting('dialogic/choices/hotkey_behaviour', 0)) - + var reveal_delay :float = ProjectSettings.get_setting('dialogic/choices/reveal_delay', 0) var reveal_by_input :bool = ProjectSettings.get_setting('dialogic/choices/reveal_by_input', false) if not reveal_by_input and reveal_delay == 0: @@ -17,7 +17,7 @@ func _refresh() -> void: _on_appear_mode_item_selected(2) if reveal_by_input and reveal_delay != 0: _on_appear_mode_item_selected(3) - + %RevealDelay.value = reveal_delay func _on_Autofocus_toggled(button_pressed: bool) -> void: diff --git a/addons/dialogic/Modules/Choice/subsystem_choices.gd b/addons/dialogic/Modules/Choice/subsystem_choices.gd index bbd27421..3ee43f83 100644 --- a/addons/dialogic/Modules/Choice/subsystem_choices.gd +++ b/addons/dialogic/Modules/Choice/subsystem_choices.gd @@ -2,31 +2,61 @@ extends DialogicSubsystem ## Subsystem that manages showing and activating of choices. +## Emitted when a choice button was pressed. Info includes the keys 'button_index', 'text', 'event_index'. 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) +## Contains information on the latest question. +var last_question_info := {} -## Used to block choices from being clicked for a couple of seconds (if delay is set in settings). -var choice_blocker := Timer.new() +## The delay between the text finishing revealing and the choices appearing +var reveal_delay: float = 0.0 +## If true the player has to click to reveal choices when they are reached +var reveal_by_input: bool = false +## The delay between the choices becoming visible and being clickable. Can prevent accidental selection. +var block_delay: float = 0.2 +## If true, the first (top-most) choice will be focused +var autofocus_first_choice: bool = true -var last_question_info := {} +enum FalseBehaviour {HIDE=0, DISABLE=1} +## The behaviour of choices with a false condition and else_action set to DEFAULT. +var default_false_behaviour := FalseBehaviour.HIDE -func _ready(): - choice_blocker.one_shot = true - DialogicUtil.update_timer_process_callback(choice_blocker) - add_child(choice_blocker) +enum HotkeyBehaviour {NONE, NUMBERS} +## Will add some hotkeys to the choices if different then HotkeyBehaviour.NONE. +var hotkey_behaviour := HotkeyBehaviour.NONE -#################################################################################################### -## STATE +### INTERNALS + +## Used to block choices from being clicked for a couple of seconds (if delay is set in settings). +var _choice_blocker := Timer.new() + +#region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR): +func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: hide_all_choices() -#################################################################################################### -## MAIN METHODS +func _ready() -> void: + _choice_blocker.one_shot = true + DialogicUtil.update_timer_process_callback(_choice_blocker) + add_child(_choice_blocker) + + reveal_delay = float(ProjectSettings.get_setting('dialogic/choices/reveal_delay', reveal_delay)) + reveal_by_input = ProjectSettings.get_setting('dialogic/choices/reveal_by_input', reveal_by_input) + block_delay = ProjectSettings.get_setting('dialogic/choices/delay', block_delay) + autofocus_first_choice = ProjectSettings.get_setting('dialogic/choices/autofocus_first', autofocus_first_choice) + hotkey_behaviour = ProjectSettings.get_setting('dialogic/choices/hotkey_behaviour', hotkey_behaviour) + default_false_behaviour = ProjectSettings.get_setting('dialogic/choices/def_false_behaviour', default_false_behaviour) + +#endregion + + +#region MAIN METHODS #################################################################################################### ## Hides all choice buttons. @@ -40,23 +70,20 @@ func hide_all_choices() -> void: ## Lists all current choices and shows buttons. func show_current_choices(instant:=true) -> void: hide_all_choices() - choice_blocker.stop() - - var reveal_delay := float(ProjectSettings.get_setting('dialogic/choices/reveal_delay', 0.0)) - var reveal_by_input :bool = ProjectSettings.get_setting('dialogic/choices/reveal_by_input', false) + _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.start(reveal_delay) + _choice_blocker.timeout.connect(show_current_choices) if reveal_by_input: - dialogic.Input.dialogic_action.connect(show_current_choices) + dialogic.Inputs.dialogic_action.connect(show_current_choices) return - if choice_blocker.timeout.is_connected(show_current_choices): - choice_blocker.timeout.disconnect(show_current_choices) - if dialogic.Input.dialogic_action.is_connected(show_current_choices): - dialogic.Input.dialogic_action.disconnect(show_current_choices) + 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) var button_idx := 1 @@ -64,9 +91,9 @@ func show_current_choices(instant:=true) -> void: for choice_index in get_current_choice_indexes(): var choice_event: DialogicEvent = dialogic.current_timeline_events[choice_index] # check if condition is false - if not choice_event.condition.is_empty() and not dialogic.Expression.execute_condition(choice_event.condition): + if 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 = ProjectSettings.get_setting('dialogic/choices/def_false_behaviour', 0) + choice_event.else_action = default_false_behaviour # check what to do in this case if choice_event.else_action == DialogicChoiceEvent.ElseActions.DISABLE: @@ -83,43 +110,43 @@ func show_current_choices(instant:=true) -> void: last_question_info['choices'].append(choice_event.get_property_translated('text')) button_idx += 1 choices_shown.emit(last_question_info) - choice_blocker.start(float(ProjectSettings.get_setting('dialogic/choices/delay', 0.2))) + _choice_blocker.start(block_delay) ## 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 in get_tree().get_nodes_in_group('dialogic_choice_button'): - # FIXME: Godot 4.2 can replace this with a typed for loop. - var choice_button := node as DialogicNode_ChoiceButton - + 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 (idx == button_index and node.choice_index == -1): node.show() + + if dialogic.has_subsystem('Text'): - node.text = dialogic.Text.parse_text(text, true, true, false, true, false, false) - else: - node.text = text + text = dialogic.Text.parse_text(text, true, true, false, true, false, false) + + node._set_text_changed(text) - if idx == 1 and ProjectSettings.get_setting('dialogic/choices/autofocus_first', true): + if idx == 1 and autofocus_first_choice: node.grab_focus() ## Add 1 to 9 as shortcuts if it's enabled - if ProjectSettings.get_setting('dialogic/choices/hotkey_behaviour', 0): - if idx > 0 and 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 + 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 @@ -137,22 +164,30 @@ func show_choice(button_index:int, text:String, enabled:bool, event_index:int) - 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.") + func _on_ChoiceButton_choice_selected(event_index:int, choice_info:={}) -> void: - if dialogic.paused or not choice_blocker.is_stopped(): + 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) + 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}) + func get_current_choice_indexes() -> Array: var choices := [] - var evt_idx :int= dialogic.current_event_idx + var evt_idx := dialogic.current_event_idx var ignore := 0 while true: - - evt_idx += 1 if evt_idx >= len(dialogic.current_timeline_events): break if dialogic.current_timeline_events[evt_idx] is DialogicChoiceEvent: @@ -167,10 +202,13 @@ func get_current_choice_indexes() -> Array: if dialogic.current_timeline_events[evt_idx] is DialogicEndBranchEvent: ignore -= 1 + evt_idx += 1 return choices -#################################################################################################### -## HELPERS +#endregion + + +#region HELPERS #################################################################################################### func is_question(index:int) -> bool: @@ -178,5 +216,15 @@ func is_question(index:int) -> bool: if len(dialogic.current_timeline_events)-1 != index: if dialogic.current_timeline_events[index+1] is DialogicChoiceEvent: return true + + 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: + return false + else: + return true + else: + return true return false +#endregion diff --git a/addons/dialogic/Modules/Choice/ui_choice_end.gd b/addons/dialogic/Modules/Choice/ui_choice_end.gd index 00b82a86..8bfb2187 100644 --- a/addons/dialogic/Modules/Choice/ui_choice_end.gd +++ b/addons/dialogic/Modules/Choice/ui_choice_end.gd @@ -21,5 +21,8 @@ func _on_add_choice_pressed() -> void: if timeline: var resource = DialogicChoiceEvent.new() resource.created_by_button = true - timeline.add_event_with_end_branch(resource, get_parent().get_index()+1) + timeline.add_event_undoable(resource, get_parent().get_index()+1) timeline.indent_events() + timeline.something_changed() + # Prevent focusing on future redos + resource.created_by_button = false diff --git a/addons/dialogic/Modules/Clear/clear_background.svg.import b/addons/dialogic/Modules/Clear/clear_background.svg.import index 2a24e847..fc09019d 100644 --- a/addons/dialogic/Modules/Clear/clear_background.svg.import +++ b/addons/dialogic/Modules/Clear/clear_background.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://brc74uf1yvlfr" path="res://.godot/imported/clear_background.svg-32f657ae646e5867a8ceb0543ae207de.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Clear/clear_characters.svg.import b/addons/dialogic/Modules/Clear/clear_characters.svg.import index 7bade64b..bd18d6cd 100644 --- a/addons/dialogic/Modules/Clear/clear_characters.svg.import +++ b/addons/dialogic/Modules/Clear/clear_characters.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://d3b85xt58e8ej" path="res://.godot/imported/clear_characters.svg-58481eec0869d46b2d90f2102edd1687.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Clear/clear_music.svg.import b/addons/dialogic/Modules/Clear/clear_music.svg.import index 6f540ab0..bbf00a85 100644 --- a/addons/dialogic/Modules/Clear/clear_music.svg.import +++ b/addons/dialogic/Modules/Clear/clear_music.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://c70ten8d456b5" path="res://.godot/imported/clear_music.svg-ab19a5f3dc85de8ba17b7c720b838a73.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Clear/clear_positions.svg.import b/addons/dialogic/Modules/Clear/clear_positions.svg.import index 8ed5e545..d779f5ea 100644 --- a/addons/dialogic/Modules/Clear/clear_positions.svg.import +++ b/addons/dialogic/Modules/Clear/clear_positions.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://rs76jb858p8e" path="res://.godot/imported/clear_positions.svg-9c6ef9c2e28a63b87a3c7d0ed63620d8.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Clear/clear_style.svg.import b/addons/dialogic/Modules/Clear/clear_style.svg.import index 118b379c..7ae2d623 100644 --- a/addons/dialogic/Modules/Clear/clear_style.svg.import +++ b/addons/dialogic/Modules/Clear/clear_style.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://clpxmppelspva" path="res://.godot/imported/clear_style.svg-ab3288e6e47f45c4e12ce1862403a99c.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Clear/clear_textbox.svg.import b/addons/dialogic/Modules/Clear/clear_textbox.svg.import index 61d2808d..d5ecd585 100644 --- a/addons/dialogic/Modules/Clear/clear_textbox.svg.import +++ b/addons/dialogic/Modules/Clear/clear_textbox.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://co8t6o06m76ed" path="res://.godot/imported/clear_textbox.svg-04935e3d82350d24a197adcb47df6f57.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Clear/event_clear.gd b/addons/dialogic/Modules/Clear/event_clear.gd index 567d48a0..c69acaa9 100644 --- a/addons/dialogic/Modules/Clear/event_clear.gd +++ b/addons/dialogic/Modules/Clear/event_clear.gd @@ -22,13 +22,13 @@ var clear_background := true func _execute() -> void: var final_time := time - if dialogic.Input.auto_skip.enabled: - var time_per_event: float = dialogic.Input.auto_skip.time_per_event + if dialogic.Inputs.auto_skip.enabled: + var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event final_time = min(time, time_per_event) if clear_textbox and dialogic.has_subsystem("Text"): dialogic.Text.update_dialog_text('') - dialogic.Text.hide_text_boxes() + dialogic.Text.hide_textbox() dialogic.current_state = dialogic.States.IDLE if step_by_step: await dialogic.get_tree().create_timer(final_time).timeout @@ -96,14 +96,14 @@ func get_shortcode_parameters() -> Dictionary: func build_event_editor(): add_header_label('Clear') - add_body_edit('time', ValueType.FLOAT, {'left_text':'Time:'}) + add_body_edit('time', ValueType.NUMBER, {'left_text':'Time:'}) add_body_edit('step_by_step', ValueType.BOOL, {'left_text':'Step by Step:'}, 'time > 0') add_body_line_break() - add_body_edit('clear_textbox', ValueType.BOOL, {'left_text':'Clear:', 'icon':load("res://addons/dialogic/Modules/Clear/clear_textbox.svg"), 'tooltip':'Clear Textbox'}) - add_body_edit('clear_portraits', ValueType.BOOL, {'icon':load("res://addons/dialogic/Modules/Clear/clear_characters.svg"), 'tooltip':'Clear Portraits'}) - add_body_edit('clear_background', ValueType.BOOL, {'icon':load("res://addons/dialogic/Modules/Clear/clear_background.svg"), 'tooltip':'Clear Background'}) - add_body_edit('clear_music', ValueType.BOOL, {'icon':load("res://addons/dialogic/Modules/Clear/clear_music.svg"), 'tooltip':'Clear Music'}) - add_body_edit('clear_style', ValueType.BOOL, {'icon':load("res://addons/dialogic/Modules/Clear/clear_style.svg"), 'tooltip':'Clear Style'}) - add_body_edit('clear_portrait_positions', ValueType.BOOL, {'icon':load("res://addons/dialogic/Modules/Clear/clear_positions.svg"), 'tooltip':'Clear Portrait Positions'}) + add_body_edit('clear_textbox', ValueType.BOOL_BUTTON, {'left_text':'Clear:', 'icon':load("res://addons/dialogic/Modules/Clear/clear_textbox.svg"), 'tooltip':'Clear Textbox'}) + add_body_edit('clear_portraits', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_characters.svg"), 'tooltip':'Clear Portraits'}) + add_body_edit('clear_background', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_background.svg"), 'tooltip':'Clear Background'}) + add_body_edit('clear_music', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_music.svg"), 'tooltip':'Clear Music'}) + add_body_edit('clear_style', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_style.svg"), 'tooltip':'Clear Style'}) + add_body_edit('clear_portrait_positions', ValueType.BOOL_BUTTON, {'icon':load("res://addons/dialogic/Modules/Clear/clear_positions.svg"), 'tooltip':'Clear Portrait Positions'}) diff --git a/addons/dialogic/Modules/Condition/event_condition.gd b/addons/dialogic/Modules/Condition/event_condition.gd index 2a0a59fa..3902a894 100644 --- a/addons/dialogic/Modules/Condition/event_condition.gd +++ b/addons/dialogic/Modules/Condition/event_condition.gd @@ -21,10 +21,10 @@ func _execute() -> void: if condition_type == ConditionTypes.ELSE: finish() return - + if condition.is_empty(): condition = "true" - - var result :bool= dialogic.Expression.execute_condition(condition) + + var result :bool= dialogic.Expressions.execute_condition(condition) if not result: var idx :int= dialogic.current_event_idx var ignore := 1 @@ -36,7 +36,7 @@ func _execute() -> void: ignore += 1 elif dialogic.current_timeline.get_event(idx) is DialogicEndBranchEvent: ignore -= 1 - + dialogic.current_event_idx = idx-1 finish() @@ -69,7 +69,7 @@ func get_end_branch_control() -> Control: func to_text() -> String: var result_string := "" - + match condition_type: ConditionTypes.IF: result_string = 'if '+condition+':' @@ -77,7 +77,7 @@ func to_text() -> String: result_string = 'elif '+condition+':' ConditionTypes.ELSE: result_string = 'else:' - + return result_string @@ -104,8 +104,8 @@ func is_valid_event(string:String) -> bool: ################################################################################ func build_event_editor(): - add_header_edit('condition_type', ValueType.FIXED_OPTION_SELECTOR, { - 'selector_options': [ + add_header_edit('condition_type', ValueType.FIXED_OPTIONS, { + 'options': [ { 'label': 'IF', 'value': ConditionTypes.IF, diff --git a/addons/dialogic/Modules/Condition/icon.svg.import b/addons/dialogic/Modules/Condition/icon.svg.import index b2b8d62b..1e6a663e 100644 --- a/addons/dialogic/Modules/Condition/icon.svg.import +++ b/addons/dialogic/Modules/Condition/icon.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://bco4t1ey0fkpm" path="res://.godot/imported/icon.svg-502d45c064cc30f576b7b105a01357ce.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/Condition/ui_condition_end.gd b/addons/dialogic/Modules/Condition/ui_condition_end.gd index 661c66ec..747955ce 100644 --- a/addons/dialogic/Modules/Condition/ui_condition_end.gd +++ b/addons/dialogic/Modules/Condition/ui_condition_end.gd @@ -33,13 +33,15 @@ func add_elif(): if timeline: var resource = DialogicConditionEvent.new() resource.condition_type = DialogicConditionEvent.ConditionTypes.ELIF - timeline.add_event_with_end_branch(resource, get_parent().get_index()+1) + timeline.add_event_undoable(resource, get_parent().get_index()+1) timeline.indent_events() + timeline.something_changed() func add_else(): var timeline = find_parent('VisualEditor') if timeline: var resource = DialogicConditionEvent.new() resource.condition_type = DialogicConditionEvent.ConditionTypes.ELSE - timeline.add_event_with_end_branch(resource, get_parent().get_index()+1) + timeline.add_event_undoable(resource, get_parent().get_index()+1) timeline.indent_events() + timeline.something_changed() diff --git a/addons/dialogic/Modules/Converter/custom_event_converter.gd b/addons/dialogic/Modules/Converter/custom_event_converter.gd deleted file mode 100644 index c3395f1e..00000000 --- a/addons/dialogic/Modules/Converter/custom_event_converter.gd +++ /dev/null @@ -1,18 +0,0 @@ -extends Node -class_name CustomEventConverter - -# This is an extension to the Converter to allow for converting custom scenes -# This will take a 1.x event node with a custom scene, and let you convert it to the new format -# One type is provided for example. If you wish to convert them into a new format, add it here as another Match case -# Otherwise, it will just create it as a commment with the parameters in it for you to do in-editor - - -static func convertCustomEvent(event): - var returnString = "" - - match event['event_id']: - "comment_001": - # Example node, a custom event to simply store comments, as there wasn't a built-in one in 1.x - returnString += "# " + event['comment_text'] - - return returnString diff --git a/addons/dialogic/Modules/Converter/index.gd b/addons/dialogic/Modules/Converter/index.gd deleted file mode 100644 index 208bd71c..00000000 --- a/addons/dialogic/Modules/Converter/index.gd +++ /dev/null @@ -1,5 +0,0 @@ -@tool -extends DialogicIndexer - -func _get_settings_pages() -> Array: - return [this_folder.path_join('settings_converter.tscn')] diff --git a/addons/dialogic/Modules/Converter/settings_converter.gd b/addons/dialogic/Modules/Converter/settings_converter.gd deleted file mode 100644 index caee0bf8..00000000 --- a/addons/dialogic/Modules/Converter/settings_converter.gd +++ /dev/null @@ -1,1041 +0,0 @@ -@tool -extends DialogicSettingsPage - -var folderStructure - -var timelineFolderBreakdown:Dictionary = {} -var characterFolderBreakdown:Dictionary = {} -var definitionFolderBreakdown:Dictionary = {} -var themeFolderBreakdown:Dictionary = {} -var definitionsFile = {} -var flatDefinitionsFile = {} - -var conversionRootFolder = "res://converted-dialogic" - -var contents - -var conversionReady = false - -var varSubsystemInstalled = false -var anchorNames = {} -var prefixCharacters = false - - -func _get_title(): - return 'Converter' - -func _refresh(): - pass - -func _is_feature_tab() -> bool: - return true - -func _on_verify_pressed(): - - %OutputLog.text = "" - - if FileAccess.file_exists("res://dialogic/settings.cfg"): - %OutputLog.text += "[√] Dialogic 1.x settings data [color=green]found![/color]\r\n" - else: - %OutputLog.text += "[X] Dialogic 1.x settings data [color=red]not found![/color]\r\n" - %OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n" - return - - if FileAccess.file_exists("res://dialogic/definitions.json"): - %OutputLog.text += "[√] Dialogic 1.x definitions [color=green]found![/color]\r\n" - else: - %OutputLog.text += "[X] Dialogic 1.x definitions [color=red]not found![/color]\r\n" - %OutputLog.text += "Please copy the res://dialogic folder from your Dialogic 1.x project into this project and try again.\r\n" - return - - %OutputLog.text += "\r\n" - - %OutputLog.text += "Verifying data:\r\n" - var file := FileAccess.open("res://dialogic/folder_structure.json", FileAccess.READ) - var fileContent := file.get_as_text() - var json_object := JSON.new() - - var error := json_object.parse(fileContent) - - if error == OK: - folderStructure = json_object.get_data() - else: - print("JSON Parse Error: ", json_object.get_error_message(), " in ", error, " at line ", json_object.get_error_line()) - %OutputLog.text += "Dialogic 1.x folder structure [color=red]could not[/color] be read!\r\n" - %OutputLog.text += "Please check the output log for the error the JSON parser encountered.\r\n" - return - - %OutputLog.text += "Dialogic 1.x folder structure read successfully!\r\n" - - recursive_search("Timeline", folderStructure["folders"]["Timelines"], "/") - recursive_search("Character", folderStructure["folders"]["Characters"], "/") - recursive_search("Definition", folderStructure["folders"]["Definitions"], "/") - recursive_search("Theme", folderStructure["folders"]["Themes"], "/") - - - %OutputLog.text += "Timelines found: " + str(timelineFolderBreakdown.size()) + "\r\n" - %OutputLog.text += "Characters found: " + str(characterFolderBreakdown.size()) + "\r\n" - %OutputLog.text += "Definitions found: " + str(definitionFolderBreakdown.size()) + "\r\n" - %OutputLog.text += "Themes found: " + str(themeFolderBreakdown.size()) + "\r\n" - - %OutputLog.text += "\r\n" - %OutputLog.text += "Verifying count of JSON files for match with folder structure:\r\n" - - var timelinesDirectory = list_files_in_directory("res://dialogic/timelines") - if timelinesDirectory.size() == timelineFolderBreakdown.size(): - %OutputLog.text += "Timeline files found: [color=green]" + str(timelinesDirectory.size()) + "[/color]\r\n" - else: - %OutputLog.text += "Timeline files found: [color=red]" + str(timelinesDirectory.size()) + "[/color]\r\n" - %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" - - var characterDirectory = list_files_in_directory("res://dialogic/characters") - if characterDirectory.size() == characterFolderBreakdown.size(): - %OutputLog.text += "Character files found: [color=green]" + str(characterDirectory.size()) + "[/color]\r\n" - else: - %OutputLog.text += "Character files found: [color=red]" + str(characterDirectory.size()) + "[/color]\r\n" - %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" - - - - file = FileAccess.open("res://dialogic/definitions.json",FileAccess.READ) - fileContent = file.get_as_text() - json_object = JSON.new() - - error = json_object.parse(fileContent) - - if error == OK: - definitionsFile = json_object.get_data() - #print(folderStructure) - elif definitionFolderBreakdown.size() == 0: - %OutputLog.text += "[i]No definitions could be loaded, but that is probably correct as no definitions seem to exist.[/i]\n" - else: - print("JSON Parse Error: ", json_object.get_error_message(), " in ", error, " at line ", json_object.get_error_line()) - %OutputLog.text += "Dialogic 1.x definitions file [color=red]could not[/color] be read!\r\n" - %OutputLog.text += "Please check the output log for the error the JSON parser encountered.\r\n" - return - - if definitionsFile: - for variable in definitionsFile["variables"]: - var varPath = definitionFolderBreakdown[variable["id"]] - var variableInfo = {} - variableInfo["type"] = "variable" - variableInfo["path"] = varPath - variableInfo["name"] = variable["name"] - variableInfo["value"] = variable["value"] - definitionFolderBreakdown[variable["id"]] = variableInfo - - for variable in definitionsFile["glossary"]: - var varPath = definitionFolderBreakdown[variable["id"]] - var variableInfo = {} - variableInfo["type"] = "glossary" - variableInfo["path"] = varPath - variableInfo["name"] = variable["name"] - variableInfo["text"] = variable["text"] - variableInfo["title"] = variable["title"] - variableInfo["extra"] = variable["extra"] - variableInfo["glossary_type"] = variable["type"] - definitionFolderBreakdown[variable["id"]] = variableInfo - - if (definitionsFile["glossary"].size() + definitionsFile["variables"].size()) == definitionFolderBreakdown.size(): - %OutputLog.text += "Definitions found: [color=green]" + str((definitionsFile["glossary"].size() + definitionsFile["variables"].size())) + "[/color]\r\n" - %OutputLog.text += " • Glossaries found: " + str(definitionsFile["glossary"].size()) + "\r\n" - %OutputLog.text += " • Variables found: " + str(definitionsFile["variables"].size()) + "\r\n" - else: - %OutputLog.text += "Definition files found: [color=red]" + str(definitionsFile.size()) + "[/color]\r\n" - %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" - - var themeDirectory = list_files_in_directory("res://dialogic/themes") - if themeDirectory.size() == themeFolderBreakdown.size(): - %OutputLog.text += "Theme files found: [color=green]" + str(themeDirectory.size()) + "[/color]\r\n" - else: - %OutputLog.text += "Theme files found: [color=red]" + str(themeDirectory.size()) + "[/color]\r\n" - %OutputLog.text += "[color=yellow]There may be an issue, please check in Dialogic 1.x to make sure that is correct![/color]\r\n" - - # dirty check for the variable subsystem, as properly calling has subsystem is complicated currently - varSubsystemInstalled = file.file_exists(DialogicUtil.get_module_path('Variable').path_join("event_variable.gd")) - - if !varSubsystemInstalled: - %OutputLog.text += "[color=yellow]Variable subsystem is not present in this Dialogic! Variables will not be converted![/color]" - - %OutputLog.text += "\r\n" - - %OutputLog.text += "Initial integrity check completed!\r\n" - - if DirAccess.dir_exists_absolute(conversionRootFolder): - %OutputLog.text += "[color=yellow]Conversion folder already exists, coverting will overwrite existing files.[/color]\r\n" - else: -# %OutputLog.text += conversionRootFolder - %OutputLog.text += "[color=lightsalmon]Folders are being created in " + conversionRootFolder + ". Converted files will be located there.[/color]\r\n" - var directory = DirAccess.open("res://") - directory.make_dir(conversionRootFolder) - var sub_directory = DirAccess.open(conversionRootFolder) - sub_directory.open(conversionRootFolder) - sub_directory.make_dir("characters") - sub_directory.make_dir("timelines") - sub_directory.make_dir("themes") - - conversionReady = true - $HBox/RightPanel/Begin.disabled = false - - - -func list_files_in_directory(path): - var files = [] - var dir = DirAccess.open(path) - dir.list_dir_begin() - - while true: - var file = dir.get_next() - if file == "": - break - elif not file.begins_with("."): - if file.ends_with(".json") || file.ends_with(".cfg"): - files.append(file) - - dir.list_dir_end() - return files - -func recursive_search(currentCheck, currentDictionary, currentFolder): - for structureFile in currentDictionary["files"]: - match currentCheck: - "Timeline": timelineFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false) - "Character": characterFolderBreakdown[structureFile] = characterNameConversion(currentFolder,false) - "Definition": definitionFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false) - "Theme": themeFolderBreakdown[structureFile] = characterNameConversion(currentFolder, false) - - for structureFolder in currentDictionary["folders"]: - recursive_search(currentCheck, currentDictionary["folders"][structureFolder], currentFolder + structureFolder + "/") - - - - - - -func _on_begin_pressed(): - %OutputLog.text += "-----------------------------------------\r\n" - %OutputLog.text += "Beginning file conversion:\r\n" - %OutputLog.text += "\r\n" - - #Variable conversion needs to be first, to build the lookup table for new style - #Character conversion needs to be before timelines, so the character names are available - convertVariables() - convertCharacters() - convertTimelines() - convertGlossaries() - convertThemes() - convertSettings() - - %OutputLog.text += "All conversions complete!\r\n" - %OutputLog.text += "\r\nPlease check to make sure your timelines all look good. After that, you can remove the /addons/dialogic/Modules/Converter folder, as it is no longer needed.\r\n\r\n" - %OutputLog.text += "Please be aware, Godot may take some time on the next project load to reload all of the Characters and Timelines. This is normal, and should only happen the one time." - - -func convertTimelines(): - %OutputLog.text += "Converting timelines: \r\n" - for item in timelineFolderBreakdown: - var folderPath = timelineFolderBreakdown[item] - %OutputLog.text += "Timeline " + folderPath + item +": " - var jsonData = {} - var file := FileAccess.open("res://dialogic/timelines/" + item, FileAccess.READ) - var fileContent = file.get_as_text() - var json_object = JSON.new() - - var error = json_object.parse(fileContent) - - if error == OK: - contents = json_object.get_data() - var fileName = contents["metadata"]["name"] - %OutputLog.text += "Name: " + fileName + ", " + str(contents["events"].size()) + " timeline events\n" - - var dir_timelines = conversionRootFolder + "/timelines" - if not DirAccess.dir_exists_absolute(dir_timelines + folderPath): - var directory = DirAccess.open(dir_timelines) - if not DirAccess.dir_exists_absolute(dir_timelines): - DirAccess.make_dir_absolute(dir_timelines) - - var progresiveDirectory = "" - for pathItem in folderPath.split('/'): - directory.open(dir_timelines + progresiveDirectory) - if pathItem!= "": - progresiveDirectory += "/" + pathItem - if !directory.dir_exists(dir_timelines + progresiveDirectory): - directory.make_dir(dir_timelines + progresiveDirectory) - - #just double check because sometimes its making double slashes at the final filename - if folderPath.right(1) == "/": - folderPath = folderPath.left(-1) - # we will save it as an intermediary file first, then on second pass cleanup make it the .dtl - var newFilePath = dir_timelines + folderPath + "/" + fileName + ".cnv" - - file = FileAccess.open(newFilePath, FileAccess.WRITE) - - # update the new location so we know where second pass items are - - timelineFolderBreakdown[item] = newFilePath - - - var processedEvents = 0 - - - var depth = [] - for event in contents["events"]: - processedEvents += 1 - var eventLine = "" - - for i in depth: - eventLine += " " - - if "dialogic_" in event["event_id"]: - match event["event_id"]: - "dialogic_001": - #Text Event - var has_character:bool = false - if event['character'] != "" && event['character']: - has_character = true - eventLine += characterFolderBreakdown[event['character']]['searchable_name'] - - if event['portrait'] != "": - eventLine += "(" + event['portrait'] + ")" - - eventLine += ": " - if '\n' in event['text']: - var splitCount = 0 - var split = event['text'].split('\n') - for splitItem in split: - if has_character == false && splitItem.find(' ') > 0 && splitItem.find(':') > 0 && (splitItem.find(' ') > splitItem.find(':')): - splitItem = splitItem.insert(splitItem.find(':'), "\\" ) - if splitCount == 0: - file.store_line(eventLine + splitItem + "\\") - else: - file.store_line(splitItem + "\\") - splitCount += 1 - else: - var text_line = variableNameConversion(event['text']) - if has_character == false && text_line.find(' ') > 0 && text_line.find(':') > 0 && (text_line.find(' ') > text_line.find(':')): - text_line = text_line.insert(text_line.find(':'), "\\" ) - file.store_string(eventLine + text_line) - "dialogic_002": - # Character event - - #For some reason this is loading as a float, and the match is failing. so hard casting as string - var eventType:String - if 'type' in event: - eventType = str(event['type']) - else: - eventType = "0" - - match eventType: - "0": - if event['character'] != "": - eventLine += "join " - eventLine += characterFolderBreakdown[event['character']]['searchable_name'] - if (event['portrait'] != ""): - eventLine += " (" + event['portrait'] + ") " - - for i in event['position']: - if event['position'][i] == true: - #1.x uses positions 0-4, while the default 2.0 scene uses positions 1-5 - eventLine += str(i.to_int() + 1) - - if (event['animation'] != "[Default]" && event['animation'] != "") || ('z_index' in event) || ('mirror_portrait' in event): - # Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved - eventLine += " [" - if ('animation' in event && event['animation'] != "[Default]" && event['animation'] != ""): - eventLine += " [animation=\"Instant In Or Out\" " - eventLine += "length=\"" + str(event['animation_length']) + "\"" - if "animation_wait" in event: - eventLine += " wait=\"true\"" - - if 'z_index' in event: - if event['z_index'] != 0: - eventLine += ' z-index="' + str(event['z_index']) + '"' - if 'mirror_portrait' in event: - if event['mirror_portrait']: - eventLine += ' mirrored="true"' - - eventLine += "]" - - file.store_string(eventLine) - else: - eventLine += "# Character join event that did not have a selected character" - file.store_string(eventLine) - "2": - if event['character'] != "": - if event['character'] != "[All]": - - eventLine += "update " - eventLine += characterFolderBreakdown[event['character']]['searchable_name'] - if 'portrait' in event: - if (event['portrait'] != "") && (event['portrait'] != "(Don't change)"): - eventLine += " (" + event['portrait'] + ") " - - var positionCheck = false - if 'position' in event: - for i in event['position']: - - if event['position'][i] == true: - positionCheck = true - eventLine += str(i.to_int() + 1) - - if !positionCheck: - eventLine += " 0" - - if (event['animation'] != "[Default]" && event['animation'] != "") || ('z_index' in event) || ('mirror_portrait' in event): - # Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved - eventLine += " [" - if (event['animation'] != "[Default]" && event['animation'] != ""): - eventLine += "animation=\"Heartbeat\" " - eventLine += "length=\"" + str(event['animation_length']) + "\"" - if "animation_wait" in event: - eventLine += " wait=\"true\"" - if "animation_repeat" in event: - eventLine += " repeat=\"" + str(event['animation_repeat']) + "\"" - if 'z_index' in event: if 'z_index' in event: - if event['z_index'] != 0: - eventLine += ' z-index="' + str(event['z_index']) + '"' - if 'mirror_portrait' in event: - if event['mirror_portrait']: - eventLine += ' mirrored="true"' - eventLine += "]" - - file.store_string(eventLine) - else: - file.store_string(eventLine + "# Update and Leave All not currently implemented") - else: - eventLine += "# Character Update event that did not have a selected character" - file.store_string(eventLine) - "1": - if event['character'] != "": - - eventLine += "leave " - if event['character'] == "[All]": - eventLine += "--All--" - else: - eventLine += characterFolderBreakdown[event['character']]['searchable_name'] - - if event['animation'] != "[Default]" && event['animation'] != "": - # Note: due to Anima changes, animations will be converted into a default. Times and wait will be perserved - eventLine += " [animation=\"Instant In Or Out\" " - eventLine += "length=\"" + str(event['animation_length']) + "\"" - if "animation_wait" in event: - eventLine += " wait=\"true\"" - eventLine += "]" - file.store_string(eventLine) - else: - eventLine += " # Character Update event that did not have a selected character" - file.store_string(eventLine) - _: - file.store_string("# failed" + str(event['type'])) - - - "dialogic_010": - # Question event - # With the change in 2.0, the root of the Question block is simply text event - if event['character'] != "" && event['character']: - eventLine += characterFolderBreakdown[event['character']]['name'] - if event['portrait'] != "": - eventLine += "(" + event['portrait'] + ")" - - eventLine += ": " - if '\n' in event['question']: - var splitCount = 0 - var split = event['question'].split('\n') - for splitItem in split: - if splitCount == 0: - file.store_line(eventLine + splitItem + "\\") - else: - file.store_line(splitItem + "\\") - splitCount += 1 - else: - file.store_string(eventLine + event['question']) - - #depth.push_front("question") - - "dialogic_011": - #Choice event - - #Choice's in 1.x have depth, but they do not have matching End Nodes as Questions and Conditionals do - if depth.size() > 0: - if depth[0] == "choice": - #reset the tabs for this choice to be one tree up - - if depth.size() == 1: - eventLine = "" - else: - for i in (depth.size() - 1): - eventLine = " " - - else: - #for the next line we want to add a depth - - depth.push_front("choice") - - - eventLine += "- " - eventLine += event['choice'] - - if 'value' in event: - if event['value'] != "": - var valueLookup = variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" ) - - eventLine += " [if " - eventLine += valueLookup - if event['condition'] != "": - eventLine += " " + event['condition'] - else: - #default is true, so it may not store it - eventLine += " ==" - - # weird line due to missing type casts on String in current Godot 4 alpha - if event['value'] == str(event['value'].to_int()): - eventLine += " " + event['value'] - else: - eventLine += " \"" + event['value'] + "\"" - - eventLine += "]" - - - file.store_string(eventLine) - #print("choice node") - #print ("bracnh depth now" + str(depth)) - "dialogic_012": - #If event - var valueLookup = "broken variable" - if definitionFolderBreakdown.size(): - valueLookup = variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" ) - - eventLine += "if " - eventLine += valueLookup - if event['condition'] != "": - eventLine += " " + event['condition'] - else: - #default is true, so it may not store it - eventLine += " ==" - - # weird line due to missing type casts on String in current Godot 4 alpha - if event['value'] == str(event['value'].to_int()): - eventLine += " " + event['value'] - else: - eventLine += " \"" + event['value'] + "\"" - - eventLine += ":" - file.store_string(eventLine) - #print("if branch node") - depth.push_front("condition") - - #print ("bracnh depth now" + str(depth)) - "dialogic_013": - #End Branch event - # doesnt actually make any lines, just adjusts the tab depth - #print("end branch node") - - - var _popped = depth.pop_front() - #print ("bracnh depth now" + str(depth)) - "dialogic_014": - #Set Value event - if varSubsystemInstalled: - - - eventLine += "VAR " - if definitionFolderBreakdown.size(): - eventLine += variableNameConversion("[" + definitionFolderBreakdown[event['definition']]['path'] + definitionFolderBreakdown[event['definition']]['name'] + "]" ) - else: - eventLine += "{broken_variable}" - - eventLine += " = " - - if "set_random" in event: - if event['set_random'] == true: - eventLine += "[random=\"True\"" - if "random_lower_limit" in event: - eventLine += " min=\"" + str(event['random_lower_limit']) + "\"" - if "random_upper_limit" in event: - eventLine += " max=\"" + str(event['random_upper_limit']) + "\"" - - eventLine += "]" - else: - eventLine += event['set_value'] - else: - eventLine += event['set_value'] - - file.store_string(eventLine) - else: - file.store_string(eventLine + "# Set variable function. Variables subsystem is disabled") - "dialogic_015": - #Label event - file.store_string(eventLine + "[label name=\"" + event['name'] +"\"]") - anchorNames[event['id']] = event['name'] - "dialogic_016": - #Goto event - # Dialogic 1.x only allowed jumping to labels in the same timeline - # But since it is stored as a ID reference, we will have to get it on the second pass - - file.store_string(eventLine + "[jump label=<" + event['anchor_id'] +">]") - #file.store_string(eventLine + "# jump label, just a comment for testing") - "dialogic_020": - #Change Timeline event - # we will need to come back to this one on second pass, since we may not know the new path yet - - file.store_string(eventLine + "[jump timeline=<" + event['change_timeline'] +">]") - #file.store_string(eventLine + "# jump timeline, just a comment for testing") - "dialogic_021": - #Change Background event - #in Dialogic 1.x, fade default value was 1. its now 0, so we need to always write one here - var time: float = 1.0 - if "fade_duration" in event: - time = event['fade_duration'] - file.store_string(eventLine + "[background arg=\"" + event['background'] +"\" fade=\"" + str(time) + "\"]") - "dialogic_022": - #Close Dialog event - file.store_string(eventLine + "[end_timeline]") - "dialogic_023": - #Wait event - - eventLine += "[wait time=\"" + str(event['wait_seconds']) + "\"" - if !("hide_dialogbox" in event) || (event['hide_dialogbox'] == true): - eventLine += ' hide_text="true"' - else: - eventLine += ' hide_text="false"' - eventLine += "]" - file.store_string(eventLine) - "dialogic_024": - #Change Theme event - file.store_string(eventLine + '[style name="<' + event['set_theme'] + '>"]') - "dialogic_025": - #Set Glossary event - file.store_string(eventLine + "# Set Glossary event, not currently implemented") - "dialogic_026": - #Save event - if event['use_default_slot']: - file.store_string(eventLine + "[save slot=\"Default\"]") - else: - file.store_string(eventLine + "[save slot=\"" + event['custom_slot'] + "\"]") - - "dialogic_030": - #Audio event - eventLine += "[sound" - eventLine += " path=\"" + event['file'] + "\"" - eventLine += " volume=\"" + str(event['volume']) + "\"" - eventLine += " bus=\"" + event['audio_bus'] + "\"]" - file.store_string(eventLine) - "dialogic_031": - #Background Music event - eventLine += "[music" - eventLine += " path=\"" + event['file'] + "\"" - eventLine += " volume=\"" + str(event['volume']) + "\"" - eventLine += " fade=\"" + str(event['fade_length']) + "\"" - eventLine += " bus=\"" + event['audio_bus'] + "\"" - eventLine += " loop=\"true\"]" - file.store_string(eventLine) - "dialogic_040": - #Emit Signal event - file.store_string(eventLine + "[signal arg=\"" + event['emit_signal'] +"\"]") - "dialogic_041": - #Change Scene event - file.store_string(eventLine + "# Change scene event is deprecated. Scene called was: " + event['change_scene']) - "dialogic_042": - #Call Node event - eventLine += "[call_node path=\"" + event['call_node']['target_node_path'] + "\" " - eventLine += "method=\"" + event['call_node']['method_name'] + "\" " - eventLine += "args=\"[" - for arg in event['call_node']['arguments']: - eventLine += "\"" + arg + "\", " - - #remove the last comma and space - eventLine = eventLine.left(-2) - - eventLine += "]\"]" - file.store_string(eventLine) - _: - file.store_string(eventLine + "# unimplemented Dialogic control with unknown number") - - - - - else: - var returnString = CustomEventConverter.convertCustomEvent(event) - if returnString != "": - file.store_string(eventLine + returnString) - else: - eventLine += "# Custom event: " - eventLine += str(event) - eventLine = eventLine.replace("{", "*") - eventLine = eventLine.replace("}", "*") - - file.store_string(eventLine) - - file.store_string("\r\n\r\n") - - - - %OutputLog.text += "Processed events: " + str(processedEvents) + "\r\n" - else: - %OutputLog.text += "[color=red]There was a problem parsing this file![/color]\r\n" - - %OutputLog.text += "\r\n" - - #second pass - for item in timelineFolderBreakdown: - %OutputLog.text += "Verifying file: " + timelineFolderBreakdown[item] + "\r\n" - - var oldFile = FileAccess.open(timelineFolderBreakdown[item], FileAccess.READ) - var newFile = FileAccess.open(timelineFolderBreakdown[item].replace(".cnv", ".dtl"), FileAccess.WRITE) - - var regex = RegEx.new() - regex.compile('(<.*?>)') - #var result = regex.search_all(oldText) - - - var whitespaceCount = 0 - while oldFile.get_position() < oldFile.get_length(): - var line = oldFile.get_line() - - if line.length() == 0: - #clean up any extra whitespace so theres only one line betwen each command - whitespaceCount += 1 - if whitespaceCount < 2: - newFile.store_string("\r\n\r\n") - else: - whitespaceCount = 0 - - var result = regex.search_all(line) - if result: - for res in result: - var r_string = res.get_string() - var newString = r_string.substr(1,r_string.length()-2) - - if "timeline" in line: - if newString in timelineFolderBreakdown.keys(): - newString = "\"" + timelineFolderBreakdown[newString].replace(".cnv", ".dtl") + "\"" - if "label" in line: - if newString in anchorNames.keys(): - newString = "\"" + anchorNames[newString] + "\"" - if "style" in line: - var prev = newString - var config = ConfigFile.new() - var error = config.load("res://dialogic/themes/" + newString) - if error == OK: - if config.has_section_key("settings", "name"): - newString = config.get_value("settings", "name") - - if newString == prev: - newString = "DefaultTheme" - - line = line.replace(r_string,newString) - newFile.store_string(line) - else: - newFile.store_string(line) - - oldFile = null - - var fileDirectory = timelineFolderBreakdown[item].replace(timelineFolderBreakdown[item].split("/")[-1], "") - var dir = DirAccess.open(fileDirectory) - dir.remove(timelineFolderBreakdown[item]) - - %OutputLog.text += "Completed conversion of file: " + timelineFolderBreakdown[item].replace(".cnv", ".dtl") + "\r\n" - - #print(item) - - - -func convertCharacters(): - %OutputLog.text += "Converting characters: \r\n" - for item in characterFolderBreakdown: - var original_file = item - var folderPath = characterFolderBreakdown[item] - %OutputLog.text += "Character " + folderPath + item +": " - var jsonData = {} - var file := FileAccess.open("res://dialogic/characters/" + item, FileAccess.READ) - var fileContent = file.get_as_text() - var json_object = JSON.new() - - var error = json_object.parse(fileContent) - - if error == OK: - contents = json_object.get_data() - var fileName = contents["name"] - %OutputLog.text += "Name: " + fileName + "\r\n" - - if ("[" in fileName) || ("]" in fileName) || ("?" in fileName): - %OutputLog.text += " [color=yellow]Stripping invalid characters from file name![/color]\r\n" - fileName = characterNameConversion(fileName) - - - - var dir_char = conversionRootFolder + "/characters" - if not DirAccess.dir_exists_absolute(dir_char + folderPath): - if not DirAccess.dir_exists_absolute(dir_char): - DirAccess.make_dir_absolute(dir_char) - var directory = DirAccess.open(dir_char) - - var progresiveDirectory = "" - for pathItem in folderPath.split('/'): - directory.open(dir_char + progresiveDirectory) - if pathItem!= "": - progresiveDirectory += "/" + pathItem - if !directory.dir_exists(dir_char + progresiveDirectory): - directory.make_dir(dir_char + progresiveDirectory) - - #add the prefix if the prefix option is enabled - if prefixCharacters: - var prefix = "" - for level in folderPath.split('/'): - if level != "": - prefix += level.left(2) + "-" - fileName = prefix + fileName - # using the resource constructor for this one - - var current_character = DialogicCharacter.new() - current_character.resource_path = dir_char + folderPath + "/" + fileName + ".dch" - # Everything needs to be in exact order - - current_character.color = Color(contents["color"].right(6)) - var customInfoDict = {} - customInfoDict["sound_moods"] = {} - customInfoDict["theme"] = "" - current_character.custom_info = customInfoDict - current_character.description = varNameStripSpecial(contents["description"]) - if contents["display_name"] == "": - current_character.display_name = varNameStripSpecial(contents["name"]) - else: - current_character.display_name = varNameStripSpecial(contents["display_name"]) - current_character.mirror = contents["mirror_portraits"] - #current_character.name = varNameStripSpecial(contents["name"]) - current_character.nicknames = [] - current_character.offset = Vector2(0,0) - - var portraitsList = {} - var firstPortrait = "" - for portrait in contents['portraits']: - var portraitData = {} - if portrait['path'] != "": - if ".tscn" in portrait['path']: - portraitData['scene'] = portrait['path'] - else: - portraitData['image'] = portrait['path'] - - #use the global offset, scale, and mirror setting from the origianl character file - portraitData['offset'] = Vector2(contents['offset_x'], contents['offset_y']) - portraitData['scale'] = contents['scale'] / 100 - portraitData['mirror'] = contents['mirror_portraits'] - - #discard it if there's an empty Default, so it doesn't throw an error - if !((portrait['name'] == "Default") && (portrait['path'] == "")) && !((portrait['name'] == "") && (portrait['path'] == "")): - portraitsList[portrait['name']] = portraitData - if firstPortrait == "": - firstPortrait = portrait['name'] - - - - current_character.portraits = portraitsList - if firstPortrait != "": - current_character.default_portrait = firstPortrait - current_character.scale = 1.0 - - ResourceSaver.save(current_character, current_character.resource_path) - - # Before we're finished here, update the folder breakdown so it has the proper character name - var infoDict = {} - infoDict["original_file"] = original_file - infoDict["path"] = characterFolderBreakdown[item] - infoDict["name"] = fileName - var name_breakdown:Array = ( folderPath + fileName).split("/") - name_breakdown.reverse() - infoDict["name_breakdown"] = name_breakdown - - infoDict["searchable_name"] = infoDict["name_breakdown"][0] - - characterFolderBreakdown[item] = infoDict - - %OutputLog.text += "\r\n" - else: - %OutputLog.text += "[color=red]There was a problem parsing this file![/color]\r\n" - - # Second pass, shorten all of the paths, so they match the character dictionary in Dialogic itself - - # Temporarily need an array to be able to sort - var sorting_array:Array = [] - - for item in characterFolderBreakdown: - sorting_array.append(characterFolderBreakdown[item]) - - sorting_array.sort_custom(func(a, b):return a['name_breakdown'].count("/") < b['name_breakdown'].count("/")) - - var clean_search_path:bool = false - var depth = 1 - - while !clean_search_path: - var interim_array:Array = [] - clean_search_path = true - - for i in sorting_array.size(): - if sorting_array.filter(func(val): return val['searchable_name'] == sorting_array[i]['searchable_name']).size() > 1: - clean_search_path = false - var replace_dict:Dictionary = sorting_array[i] - replace_dict["searchable_name"] = replace_dict["name_breakdown"][depth] + "/" + replace_dict["searchable_name"] - interim_array.append(replace_dict) - else: - interim_array.append(sorting_array[i]) - depth += 1 - sorting_array = interim_array - - characterFolderBreakdown.clear() - - for item in sorting_array: - if item["searchable_name"].count(" ") > 0: - item["searchable_name"] = '"' + item["searchable_name"] + '"' - characterFolderBreakdown[item['original_file']] = item - %OutputLog.text += "Final character name for " + item['original_file'] + ": " + item['searchable_name'] + "\r\n" - - %OutputLog.text += "\r\n" - -func convertVariables(): - %OutputLog.text += "Converting variables: \r\n" - - var convertedVariables = 0 - # Creating a file with a format identical to how the variables are stored in project settings - if varSubsystemInstalled: - var newVariableDictionary = {} - for varItem in definitionFolderBreakdown: - if "type" in definitionFolderBreakdown[varItem]: - if definitionFolderBreakdown[varItem]["type"] == "variable": - if definitionFolderBreakdown[varItem]["path"] == "/": - newVariableDictionary[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = definitionFolderBreakdown[varItem]["value"] - flatDefinitionsFile[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = varItem - convertedVariables += 1 - else: - # I will fill this one in later, need to figure out the recursion for it - var dictRef = newVariableDictionary - var flatNameBuilder = "" - - for pathItem in varNameStripSpecial(definitionFolderBreakdown[varItem]["path"]).split("/"): - - if pathItem != "": - if pathItem in dictRef: - dictRef = dictRef[pathItem] - flatNameBuilder += pathItem + "." - else: - dictRef[pathItem] = {} - dictRef = dictRef[pathItem] - flatNameBuilder += pathItem + "." - - dictRef[varNameStripSpecial(definitionFolderBreakdown[varItem]["name"])] = definitionFolderBreakdown[varItem]["value"] - convertedVariables +=1 - var flatName = flatNameBuilder + varNameStripSpecial(definitionFolderBreakdown[varItem]["name"]) - flatDefinitionsFile[flatName] = varItem - - - ProjectSettings.set_setting('dialogic/variables', null) - ProjectSettings.set_setting('dialogic/variables', newVariableDictionary) - ProjectSettings.save() - - #rebuild the data in the other tabs, so it doesnt override it - _refresh() - %OutputLog.text += str(convertedVariables) + " variables converted, and saved to project!\r\n" - else: - %OutputLog.text += "[color=yellow]Variable subsystem is not present! Variables were not converted![/color]\r\n" - - - %OutputLog.text += "\r\n" - - -func convertGlossaries(): - %OutputLog.text += "Converting glossaries: [color=red]not currently implemented[/color] \r\n" - - %OutputLog.text += "\r\n" - -func convertThemes(): - %OutputLog.text += "Converting themes: [color=red]not currently implemented[/color] \r\n" - - %OutputLog.text += "\r\n" - -func varNameStripSpecial(oldVariable): - # This is to remove special characters from variable names - # Since in code variables are accessed by Dialogic.VAR.path.to.variable, characters not usable in Godot paths have to be removed - var newVariable = oldVariable - newVariable = newVariable.replace(" ", "_") - newVariable = newVariable.replace(".", "_") - newVariable = newVariable.replace("-", "_") - - - return(newVariable) - -func variableNameConversion(oldText): - var newText = oldText - var regex = RegEx.new() - regex.compile('(\\[.*?\\])') - var result = regex.search_all(oldText) - if result: - for res in result: - var r_string = res.get_string() - var newString = res.get_string() - newString = newString.replace("[", "") - newString = newString.replace("]", "") - if newString[0] == '/': - newString = newString.right(-1) - - newString = varNameStripSpecial(newString) - newString = newString.replace("/", ".") - - - - if newString in flatDefinitionsFile: - newString = "{" + newString + "}" - newText = newText.replace(r_string, newString) - - - - return(newText) - -func characterNameConversion(oldText:String, filter_forward_slash:bool = true): - #as some characters aren't supported in filenames, we need to convert both the filenames, and references to them - #Most character name restrictions are only restrictions of Windows, but we are going to enforce it for Dialogic due to node and regex complexities for platform uniformity - #We're also restricting the special characters used for other designations - var newText = oldText - newText = newText.replace("<","[") - newText = newText.replace(">","]") - newText = newText.replace(":","-") - newText = newText.replace("\\","/") - if filter_forward_slash: - newText = newText.replace("/","-") - newText = newText.replace("|","-") - newText = newText.replace("*","@") - newText = newText.replace("?","0") - newText = newText.replace('"',"'") - - #These ones as they are the delimiter for portraits - newText = newText.replace("(","[") - newText = newText.replace(")","]") - - - return newText - -func convertSettings(): - %OutputLog.text += "Converting other settings: \r\n" - %OutputLog.text += "[color=yellow]Note! Most original settings can't be converted.[/color] \r\n" - - - var config = ConfigFile.new() - - var err = config.load("res://dialogic/settings.cfg") - if err != OK: - %OutputLog.text += "[color=red]Dialogic 1.x Settings file could not be loaded![/color] \r\n" - return - - ProjectSettings.set_setting('dialogic/text/autocolor_names', config.get_value("dialog", "auto_color_names", true)) - ProjectSettings.set_setting('dialogic/choices/autofocus_first', config.get_value("input", "autofocus_choices", false)) - ProjectSettings.set_setting('dialogic/choices/delay', config.get_value("input", "delay_after_options", 0.2)) - ProjectSettings.save() - - - - -func _on_check_box_toggled(button_pressed): - var message := "\r\n\r\nToggling this will add a prefix to all character filenames, which will have letters from each folder depth they are in. Characters in the root folder will have no prefix. \r\n" - prefixCharacters = button_pressed - if button_pressed: - %OutputLog.text += message - else: - %OutputLog.text = %OutputLog.text.replace(message, '') - diff --git a/addons/dialogic/Modules/Converter/settings_converter.tscn b/addons/dialogic/Modules/Converter/settings_converter.tscn deleted file mode 100644 index f0f2d137..00000000 --- a/addons/dialogic/Modules/Converter/settings_converter.tscn +++ /dev/null @@ -1,74 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dign131itj5fu"] - -[ext_resource type="Script" path="res://addons/dialogic/Modules/Converter/settings_converter.gd" id="1_3feab"] - -[node name="Dialogic 1 Converter" type="PanelContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_vertical = 3 -theme_type_variation = &"DialogicPanelA" -script = ExtResource("1_3feab") -short_info = "This converter allows converting (some) dialogic 1 data into a usable format." - -[node name="HBox" type="HBoxContainer" parent="."] -layout_mode = 2 - -[node name="RightPanel" type="VBoxContainer" parent="HBox"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Label2" type="Label" parent="HBox/RightPanel"] -layout_mode = 2 -theme_type_variation = &"DialogicSettingsSection" -text = "Settings" - -[node name="CheckBox" type="CheckBox" parent="HBox/RightPanel"] -layout_mode = 2 -text = "Prefix duplicate characters" - -[node name="Verify" type="Button" parent="HBox/RightPanel"] -layout_mode = 2 -text = "Verify Files" - -[node name="Begin" type="Button" parent="HBox/RightPanel"] -layout_mode = 2 -disabled = true -text = "Begin Conversion" - -[node name="VSeparator" type="VSeparator" parent="HBox"] -layout_mode = 2 - -[node name="LeftPanel" type="VBoxContainer" parent="HBox"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_stretch_ratio = 1.75 - -[node name="Label" type="Label" parent="HBox/LeftPanel"] -layout_mode = 2 -theme_type_variation = &"DialogicSettingsSection" -text = "Output" - -[node name="ScrollContainer" type="ScrollContainer" parent="HBox/LeftPanel"] -layout_mode = 2 -size_flags_vertical = 3 -vertical_scroll_mode = 0 - -[node name="OutputLog" type="RichTextLabel" parent="HBox/LeftPanel/ScrollContainer"] -unique_name_in_owner = true -clip_contents = false -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -bbcode_enabled = true -text = "This is a converter script to convert Dialogic 1.4+ files into Dialogic 2.0 format. - -Please copy your res://dialogic folder from a Dialogic 1.4 project into this project before proceeding. - -Once you are ready, the Verify Files button will do a check to make sure all of the files are present, and prepare it for conversion." - -[connection signal="toggled" from="HBox/RightPanel/CheckBox" to="." method="_on_check_box_toggled"] -[connection signal="pressed" from="HBox/RightPanel/Verify" to="." method="_on_verify_pressed"] -[connection signal="pressed" from="HBox/RightPanel/Begin" to="." method="_on_begin_pressed"] diff --git a/addons/dialogic/Modules/Core/event_end_branch.gd b/addons/dialogic/Modules/Core/event_end_branch.gd index 58193dbe..c9dc90c0 100644 --- a/addons/dialogic/Modules/Core/event_end_branch.gd +++ b/addons/dialogic/Modules/Core/event_end_branch.gd @@ -3,11 +3,10 @@ class_name DialogicEndBranchEvent extends DialogicEvent ## Event that indicates the end of a condition or choice (or custom branch). -## In text this is not stored (only as a change in indentation). +## In text this is not stored (only as a change in indentation). -################################################################################ -## EXECUTE +#region EXECUTE ################################################################################ func _execute() -> void: @@ -15,17 +14,18 @@ func _execute() -> void: finish() -func find_next_index(): +func find_next_index() -> int: var idx: int = dialogic.current_event_idx - + var ignore: int = 1 while true: idx += 1 - var event :DialogicEvent= dialogic.current_timeline.get_event(idx) + var event: DialogicEvent = dialogic.current_timeline.get_event(idx) if not event: return idx if event is DialogicEndBranchEvent: - if ignore > 1: ignore -= 1 + if ignore > 1: + ignore -= 1 elif event.can_contain_events and not event.should_execute_this_branch(): ignore += 1 elif ignore <= 1: @@ -34,22 +34,40 @@ func find_next_index(): return idx -################################################################################ -## INITIALIZE +func find_opening_index() -> int: + var idx: int = dialogic.current_event_idx + + var ignore: int = 1 + while true: + idx -= 1 + var event: DialogicEvent = dialogic.current_timeline.get_event(idx) + if not event: + return idx + if event is DialogicEndBranchEvent: + ignore += 1 + elif event.can_contain_events: + ignore -= 1 + if ignore == 0: + return idx + + return idx +#endregion + +#region INITIALIZE ################################################################################ func _init() -> void: event_name = "End Branch" disable_editor_button = true +#endregion -################################################################################ -## SAVING/LOADING +#region SAVING/LOADING ################################################################################ -## NOTE: This event is very special. It is rarely stored at all, as it is usually +## NOTE: This event is very special. It is rarely stored at all, as it is usually ## just a placeholder for removing an indentation level. -## When copying events however, some representation of this is necessary. That's why this is half-implemented. +## When copying events however, some representation of this is necessary. That's why this is half-implemented. func to_text() -> String: return "<>" diff --git a/addons/dialogic/Modules/Core/index.gd b/addons/dialogic/Modules/Core/index.gd index 2e3be2ee..a9a17919 100644 --- a/addons/dialogic/Modules/Core/index.gd +++ b/addons/dialogic/Modules/Core/index.gd @@ -8,15 +8,15 @@ func _get_events() -> Array: func _get_subsystems() -> Array: return [ - {'name':'Expression', 'script':this_folder.path_join('subsystem_expression.gd')}, - {'name':'Animation', 'script':this_folder.path_join('subsystem_animation.gd')}, - {'name':'Input', 'script':this_folder.path_join('subsystem_input.gd')}, + {'name':'Expressions', 'script':this_folder.path_join('subsystem_expression.gd')}, + {'name':'Animations', 'script':this_folder.path_join('subsystem_animation.gd')}, + {'name':'Inputs', 'script':this_folder.path_join('subsystem_input.gd')}, ] func _get_text_effects() -> Array[Dictionary]: return [ - {'command':'aa', 'subsystem':'Input', 'method':'effect_autoadvance'}, - {'command':'ns', 'subsystem':'Input', 'method':'effect_noskip'}, - {'command':'input', 'subsystem':'Input', 'method':'effect_input'}, + {'command':'aa', 'subsystem':'Inputs', 'method':'effect_autoadvance'}, + {'command':'ns', 'subsystem':'Inputs', 'method':'effect_noskip'}, + {'command':'input', 'subsystem':'Inputs', 'method':'effect_input'}, ] diff --git a/addons/dialogic/Modules/Core/subsystem_animation.gd b/addons/dialogic/Modules/Core/subsystem_animation.gd index ad80a951..8ab0f284 100644 --- a/addons/dialogic/Modules/Core/subsystem_animation.gd +++ b/addons/dialogic/Modules/Core/subsystem_animation.gd @@ -4,20 +4,23 @@ extends DialogicSubsystem signal finished -var prev_state : int = 0 +var prev_state: int = 0 -#################################################################################################### -## MAIN METHODS + +#region MAIN METHODS #################################################################################################### func is_animating() -> bool: return dialogic.current_state == dialogic.States.ANIMATING + func start_animating() -> void: prev_state = dialogic.current_state dialogic.current_state = dialogic.States.ANIMATING -func animation_finished(arg:String= "") -> void: + +func animation_finished(arg := "") -> void: dialogic.current_state = prev_state finished.emit() - + +#endregion diff --git a/addons/dialogic/Modules/Core/subsystem_expression.gd b/addons/dialogic/Modules/Core/subsystem_expression.gd index f2b98ec5..992aed7b 100644 --- a/addons/dialogic/Modules/Core/subsystem_expression.gd +++ b/addons/dialogic/Modules/Core/subsystem_expression.gd @@ -4,12 +4,10 @@ extends DialogicSubsystem ## This is used by conditions and to allow expresions as variables. - -#################################################################################################### -## MAIN METHODS +#region MAIN METHODS #################################################################################################### -func execute_string(string:String, default = null) -> Variant: +func execute_string(string:String, default: Variant = null, no_warning := false) -> Variant: # Some methods are not supported by the expression class, but very useful. # Thus they are recreated below and secretly added. string = string.replace('range(', 'd_range(') @@ -20,10 +18,8 @@ func execute_string(string:String, default = null) -> Variant: var regex: RegEx = RegEx.create_from_string('{([^{}]*)}') for res in regex.search_all(string): - var value: String = str(dialogic.VAR.get_variable(res.get_string())) - if !value.is_valid_float(): - value = '"'+value+'"' - string = string.replace(res.get_string(), value) + var value: Variant = dialogic.VAR.get_variable(res.get_string()) + string = string.replace(res.get_string(), var_to_str(value)) var expr := Expression.new() @@ -34,12 +30,18 @@ func execute_string(string:String, default = null) -> Variant: autoload_names.append(c.name) if expr.parse(string, autoload_names) != OK: - printerr('Dialogic: Expression failed to parse: ', expr.get_error_text()) + if not no_warning: + printerr('[Dialogic] Expression "', string, '" failed to parse.') + printerr(' ', expr.get_error_text()) + dialogic.print_debug_moment() return default - var result := expr.execute(autoloads, self) + var result: Variant = expr.execute(autoloads, self) if expr.has_execute_failed(): - printerr('Dialogic: Expression failed to execute: ', expr.get_error_text()) + if not no_warning: + printerr('[Dialogic] Expression "', string, '" failed to parse.') + printerr(' ', expr.get_error_text()) + dialogic.print_debug_moment() return default return result @@ -49,9 +51,10 @@ func execute_condition(condition:String) -> bool: return true return false +#endregion -#################################################################################################### -## MAIN METHODS + +#region HELPERS #################################################################################################### func d_range(a1, a2=null,a3=null,a4=null) -> Array: if !a2: @@ -69,10 +72,12 @@ func d_len(arg:Variant) -> int: # Checks if there is a match in a string based on a regex pattern string. func d_regex(input: String, pattern: String, offset: int = 0, end: int = -1) -> bool: - var regex : RegEx = RegEx.create_from_string(pattern) + var regex: RegEx = RegEx.create_from_string(pattern) regex.compile(pattern) var match := regex.search(input, offset, end) if match: return true else: return false + +#endregion diff --git a/addons/dialogic/Modules/Core/subsystem_input.gd b/addons/dialogic/Modules/Core/subsystem_input.gd index 6215a483..0069beef 100644 --- a/addons/dialogic/Modules/Core/subsystem_input.gd +++ b/addons/dialogic/Modules/Core/subsystem_input.gd @@ -1,27 +1,35 @@ extends DialogicSubsystem - -## Subsystem that handles input, autoadvance & skipping. +## Subsystem that handles input, Auto-Advance, and skipping. +## +## This subsystem can be accessed via GDScript: `Dialogic.Inputs`. signal dialogic_action_priority signal dialogic_action + +## Whenever the Auto-Skip timer finishes, this signal is emitted. +## Configure Auto-Skip settings via [member auto_skip]. signal autoskip_timer_finished + 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 manual_advance: DialogicManualAdvance = null + -####### SUBSYSTEM METHODS ###################################################### #region SUBSYSTEM METHODS -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: +################################################################################ + +func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: if not is_node_ready(): await ready - - set_manualadvance(true) + manual_advance.disabled_until_next_event = false + manual_advance.system_enabled = true func pause() -> void: @@ -36,20 +44,29 @@ func resume() -> void: var is_autoskip_timer_done := _auto_skip_timer_left > 0.0 set_process(!is_autoskip_timer_done) + +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) + add_child(input_block_timer) + input_block_timer.one_shot = true + + #endregion -####### MAIN METHODS ########################################################### + #region MAIN METHODS +################################################################################ -func handle_input(): +func handle_input() -> void: if dialogic.paused or is_input_blocked(): return - if !action_was_consumed: + if not action_was_consumed: # We want to stop auto-advancing that cancels on user inputs. if (auto_advance.is_enabled() and auto_advance.enabled_until_user_input): - auto_advance.enabled_until_next_event = false + auto_advance.enabled_until_user_input = false action_was_consumed = true # We want to stop auto-skipping if it's enabled, we are listening @@ -71,8 +88,7 @@ func handle_input(): ## 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_action_released(ProjectSettings.get_setting('dialogic/text/input_action', 'dialogic_default_action')): + if Input.is_action_just_released(ProjectSettings.get_setting('dialogic/text/input_action', 'dialogic_default_action')): if event is InputEventMouse: return handle_input() @@ -81,12 +97,21 @@ 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 Input.is_action_just_released(ProjectSettings.get_setting('dialogic/text/input_action', 'dialogic_default_action')): + 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() +## 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_released(ProjectSettings.get_setting('dialogic/text/input_action', 'dialogic_default_action')): + 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 @@ -100,26 +125,23 @@ func block_input(time:=0.1) -> void: func _ready() -> void: auto_skip = DialogicAutoSkip.new() auto_advance = DialogicAutoAdvance.new() + manual_advance = DialogicManualAdvance.new() # We use the process method to count down the auto-start_autoskip_timer timer. set_process(false) -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) - add_child(input_block_timer) - input_block_timer.one_shot = true - -func stop() -> void: +func stop_timers() -> void: auto_advance.autoadvance_timer.stop() input_block_timer.stop() _auto_skip_timer_left = 0.0 #endregion -####### AUTO-SKIP ############################################################## + #region AUTO-SKIP +################################################################################ + ## This method will advance the timeline based on Auto-Skip settings. ## The state, whether Auto-Skip is enabled, is ignored. func start_autoskip_timer() -> void: @@ -133,10 +155,11 @@ func _on_autoskip_toggled(enabled: bool) -> void: if not enabled: _auto_skip_timer_left = 0.0 + ## Handles fine-grained Auto-Skip logic. ## The [method _process] method allows for a more precise timer than the ## [Timer] class. -func _process(delta): +func _process(delta: float) -> void: if _auto_skip_timer_left > 0: _auto_skip_timer_left -= delta @@ -146,40 +169,25 @@ func _process(delta): else: autoskip_timer_finished.emit() set_process(false) -#endregion - -####### MANUAL ADVANCE ######################################################### -#region MANUAL ADVANCE - -func set_manualadvance(enabled:=true, temp:= false) -> void: - if !dialogic.current_state_info.has('manual_advance'): - dialogic.current_state_info['manual_advance'] = {'enabled':false, 'temp_enabled':false} - if temp: - dialogic.current_state_info['manual_advance']['temp_enabled'] = enabled - else: - dialogic.current_state_info['manual_advance']['enabled'] = enabled - - -func is_manualadvance_enabled() -> bool: - return dialogic.current_state_info['manual_advance']['enabled'] and dialogic.current_state_info['manual_advance'].get('temp_enabled', true) #endregion -####### TEXT EFFECTS ########################################################### #region TEXT EFFECTS +################################################################################ + func effect_input(text_node:Control, skipped:bool, argument:String) -> void: if skipped: return dialogic.Text.show_next_indicators() - await dialogic.Input.dialogic_action_priority + await dialogic.Inputs.dialogic_action_priority dialogic.Text.hide_next_indicators() - dialogic.Input.action_was_consumed = true + dialogic.Inputs.action_was_consumed = true func effect_noskip(text_node:Control, skipped:bool, argument:String) -> void: dialogic.Text.set_text_reveal_skippable(false, true) - set_manualadvance(false, true) + manual_advance.disabled_until_next_event = true effect_autoadvance(text_node, skipped, argument) @@ -191,4 +199,5 @@ func effect_autoadvance(text_node: Control, skipped:bool, argument:String) -> vo if argument.is_valid_float(): auto_advance.override_delay_for_current_event = float(argument) + #endregion 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 66b55d6b..648b2cd2 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Base_Default/default_layout_base.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Base_Default/default_layout_base.gd @@ -3,17 +3,17 @@ extends DialogicLayoutBase ## The default layout base scene. -@export var canvas_layer := 1 +@export var canvas_layer: int = 1 @export_subgroup("Global") -@export var global_bg_color := Color(0, 0, 0, 0.9) -@export var global_font_color := Color("white") -@export_file('*.ttf') var global_font := "" -@export var global_font_size := 18 +@export var global_bg_color: Color = Color(0, 0, 0, 0.9) +@export var global_font_color: Color = Color("white") +@export_file('*.ttf', '*.tres') var global_font: String = "" +@export var global_font_size: int = 18 func _apply_export_overrides() -> void: # apply layer - self.layer = canvas_layer + set(&'layer', canvas_layer) 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 99a2dbf4..39c0032e 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.gd @@ -3,8 +3,11 @@ extends DialogicLayoutBase ## This layout won't do anything on it's 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(): @@ -12,39 +15,60 @@ func _ready(): 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:DialogicCharacter, node:Node): + registered_characters[character] = node + if len(registered_characters) > len(bubbles) and len(bubbles) < bubble_count: + add_bubble() -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 3889b321..c1c917c2 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Base_TextBubble/text_bubble_base.tscn @@ -1,7 +1,14 @@ -[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") @@ -13,24 +20,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 = -210.0 -offset_right = 647.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) +var layout = Dialogic.start(timeline_path) +layout.register_character(character_resource, node) [/color] -[color=lightblue]@character_resource[/color] should be a loaded DialogicCharacter (a .dtl file) -[color=lightblue]@node[/color] should be the 2D node the bubble should point at." +- [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 6c9fef59..b4766352 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.tscn @@ -1,14 +1,7 @@ -[gd_scene load_steps=5 format=3 uid="uid://c1k5m0w3r40xf"] +[gd_scene load_steps=3 format=3 uid="uid://c1k5m0w3r40xf"] [ext_resource type="Script" path="res://addons/dialogic/Modules/DefaultLayoutParts/Layer_FullBackground/full_background_layer.gd" id="1_tu40u"] [ext_resource type="Script" path="res://addons/dialogic/Modules/Background/node_background_holder.gd" id="2_ghan2"] -[ext_resource type="Shader" path="res://addons/dialogic/Modules/Background/Transitions/default_transition_shader.gdshader" id="2_mwc4m"] - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_msuxf"] -shader = ExtResource("2_mwc4m") -shader_parameter/progress = 0.0 -shader_parameter/feather = 0.1 -shader_parameter/keep_aspect_ratio = false [node name="BackgroundLayer" type="Control"] layout_mode = 3 @@ -20,11 +13,11 @@ grow_vertical = 2 script = ExtResource("1_tu40u") [node name="DialogicNode_BackgroundHolder" type="ColorRect" parent="."] -material = SubResource("ShaderMaterial_msuxf") layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 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 3de3f480..1c871e31 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Glossary/glossary_popup_layer.gd @@ -7,147 +7,172 @@ extends DialogicLayoutLayer @export_group('Text') enum Alignment {LEFT, CENTER, RIGHT} -@export var title_alignment := Alignment.LEFT -@export var text_alignment := Alignment.LEFT -@export var extra_alignment := Alignment.RIGHT +@export var title_alignment: Alignment = Alignment.LEFT +@export var text_alignment: Alignment = Alignment.LEFT +@export var extra_alignment: Alignment = Alignment.RIGHT @export_subgroup("Colors") enum TextColorModes {GLOBAL, ENTRY, CUSTOM} -@export var title_color_mode := TextColorModes.ENTRY -@export var title_custom_color := Color.WHITE -@export var text_color_mode := TextColorModes.ENTRY -@export var text_custom_color := Color.WHITE -@export var extra_color_mode := TextColorModes.ENTRY -@export var extra_custom_color := Color.WHITE +@export var title_color_mode: TextColorModes = TextColorModes.ENTRY +@export var title_custom_color: Color = Color.WHITE +@export var text_color_mode: TextColorModes = TextColorModes.ENTRY +@export var text_custom_color: Color = Color.WHITE +@export var extra_color_mode: TextColorModes = TextColorModes.ENTRY +@export var extra_custom_color: Color = Color.WHITE @export_group("Font") -@export var font_use_global := true -@export_file('*.ttf') var font_custom := "" +@export var font_use_global: bool = true +@export_file('*.ttf', '*.tres') var font_custom: String = "" @export_subgroup('Sizes') -@export var font_title_size := 18 -@export var font_text_size := 17 -@export var font_extra_size := 15 +@export var font_title_size: int = 18 +@export var font_text_size: int = 17 +@export var font_extra_size: int = 15 @export_group("Box") @export_subgroup("Color") enum ModulateModes {BASE_COLOR_ONLY, ENTRY_COLOR_ON_BOX, GLOBAL_BG_COLOR} -@export var box_modulate_mode := ModulateModes.ENTRY_COLOR_ON_BOX -@export var box_base_modulate := Color.WHITE +@export var box_modulate_mode: ModulateModes = ModulateModes.ENTRY_COLOR_ON_BOX +@export var box_base_modulate: Color = Color.WHITE @export_subgroup("Size") -@export var box_width := 200 +@export var box_width: int = 200 + +const MISSING_INDEX := -1 +func get_pointer() -> Control: + return $Pointer + + +func get_title() -> Label: + return %Title + + +func get_text() -> RichTextLabel: + return %Text + + +func get_extra() -> RichTextLabel: + return %Extra + + +func get_panel() -> PanelContainer: + return %Panel + + +func get_panel_point() -> PanelContainer: + return %PanelPoint func _ready() -> void: if Engine.is_editor_hint(): return - $Pointer.hide() - DialogicUtil.autoload().Text.animation_textbox_hide.connect($Pointer.hide) - DialogicUtil.autoload().Text.meta_hover_started.connect(_on_dialogic_display_dialog_text_meta_hover_started) - DialogicUtil.autoload().Text.meta_hover_ended.connect(_on_dialogic_display_dialog_text_meta_hover_ended) - DialogicUtil.autoload().Text.meta_clicked.connect(_on_dialogic_display_dialog_text_meta_clicked) + get_pointer().hide() + var text_system: Node = DialogicUtil.autoload().get(&'Text') + var _error: int = 0 + _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) ## Method that shows the bubble and fills in the info -func _on_dialogic_display_dialog_text_meta_hover_started(meta:String) -> void: - var info: Dictionary = DialogicUtil.autoload().Glossary.get_entry(meta) +func _on_dialogic_display_dialog_text_meta_hover_started(meta: String) -> void: + var entry_info := DialogicUtil.autoload().Glossary.get_entry(meta) - if not info: + if entry_info.is_empty(): return - $Pointer.show() - %Title.text = info.get('title', '') - %Text.text = info.get('text', '') - %Text.text = ['', '[center]', '[right]'][text_alignment] + %Text.text - %Extra.text = info.get('extra', '') - %Extra.text = ['', '[center]', '[right]'][extra_alignment] + %Extra.text - $Pointer.global_position = $Pointer.get_global_mouse_position() + get_pointer().show() + 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_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: - %Title.add_theme_color_override("font_color", info.get('color', title_custom_color)) + get_title().add_theme_color_override(&"font_color", entry_info.color) if text_color_mode == TextColorModes.ENTRY: - %Text.add_theme_color_override("default_color", info.get('color', text_custom_color)) + get_text().add_theme_color_override(&"default_color", entry_info.color) if extra_color_mode == TextColorModes.ENTRY: - %Extra.add_theme_color_override("default_color", info.get('color', extra_custom_color)) + get_extra().add_theme_color_override(&"default_color", entry_info.color) match box_modulate_mode: ModulateModes.ENTRY_COLOR_ON_BOX: - %Panel.self_modulate = info.get('color', Color.WHITE) - %PanelPoint.self_modulate = info.get('color', Color.WHITE) - - DialogicUtil.autoload().Input.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 -func _process(delta) -> void: +func _process(_delta: float) -> void: if Engine.is_editor_hint(): return - if $Pointer.visible: - $Pointer.global_position = $Pointer.get_global_mouse_position() + var pointer: Control = get_pointer() + if pointer.visible: + pointer.global_position = pointer.get_global_mouse_position() ## Method that hides the bubble -func _on_dialogic_display_dialog_text_meta_hover_ended(meta:String) -> void: - $Pointer.hide() - DialogicUtil.autoload().Input.action_was_consumed = false +func _on_dialogic_display_dialog_text_meta_hover_ended(_meta:String) -> void: + get_pointer().hide() -func _on_dialogic_display_dialog_text_meta_clicked(meta:String) -> void: - DialogicUtil.autoload().Input.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', '')): - font = load(get_global_setting('font', '')) + if font_use_global and ResourceLoader.exists(get_global_setting(&'font', '') as String): + font = load(get_global_setting(&'font', '') as String) elif ResourceLoader.exists(font_custom): font = load(font_custom) + var title: Label = get_title() if font: - %Title.add_theme_font_override("font", font) - %Title.horizontal_alignment = title_alignment + title.add_theme_font_override(&"font", font) + title.horizontal_alignment = title_alignment as HorizontalAlignment # Apply font & sizes - %Title.add_theme_font_size_override("font_size", font_title_size) - for i in [[%Text, font_text_size], [%Extra, font_extra_size]]: + title.add_theme_font_size_override(&"font_size", font_title_size) + var labels: Array[RichTextLabel] = [get_text(), get_extra()] + var sizes: PackedInt32Array = [font_text_size, font_extra_size] + for i : int in len(labels): if font: - i[0].add_theme_font_override('normal_font', font) + labels[i].add_theme_font_override(&'normal_font', font) - i[0].add_theme_font_size_override("normal_font_size", i[1]) - i[0].add_theme_font_size_override("bold_font_size", i[1]) - i[0].add_theme_font_size_override("italics_font_size", i[1]) - i[0].add_theme_font_size_override("bold_italics_font_size", i[1]) - i[0].add_theme_font_size_override("mono_font_size", i[1]) + labels[i].add_theme_font_size_override(&"normal_font_size", sizes[i]) + labels[i].add_theme_font_size_override(&"bold_font_size", sizes[i]) + labels[i].add_theme_font_size_override(&"italics_font_size", sizes[i]) + labels[i].add_theme_font_size_override(&"bold_italics_font_size", sizes[i]) + labels[i].add_theme_font_size_override(&"mono_font_size", sizes[i]) # Apply text colors - var texts := [ - [%Title, 'font_color', title_color_mode, title_custom_color], - [%Text, 'default_color', text_color_mode, text_custom_color], - [%Extra, 'default_color', extra_color_mode, extra_custom_color], - ] - for i in texts: - match i[2]: + # this applies Global or Custom colors, entry colors are applied on hover + var controls: Array[Control] = [get_title(), get_text(), get_extra()] + 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: - i[0].add_theme_color_override(i[1], get_global_setting('font_color', i[3])) + controls[i].add_theme_color_override(settings[i], get_global_setting(&'font_color', custom_colors[i]) as Color) TextColorModes.CUSTOM: - i[0].add_theme_color_override(i[1], i[3]) + controls[i].add_theme_color_override(settings[i], custom_colors[i]) # Apply box size - %Panel.size.x = box_width - %Panel.position.x = -box_width/2.0 + var panel: PanelContainer = get_panel() + panel.size.x = box_width + panel.position.x = -box_width/2.0 # Apply box coloring match box_modulate_mode: ModulateModes.BASE_COLOR_ONLY: - %Panel.self_modulate = box_base_modulate - %PanelPoint.self_modulate = box_base_modulate + panel.self_modulate = box_base_modulate + get_panel_point().self_modulate = box_base_modulate ModulateModes.GLOBAL_BG_COLOR: - %Panel.self_modulate = get_global_setting('bg_color', box_base_modulate) - %PanelPoint.self_modulate = get_global_setting('bg_color', box_base_modulate) - + panel.self_modulate = get_global_setting(&'bg_color', box_base_modulate) + get_panel_point().self_modulate = get_global_setting(&'bg_color', box_base_modulate) 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 b7bba192..f01fa0ff 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/example_history_item.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/example_history_item.gd @@ -1,27 +1,30 @@ extends Container +func get_text_box() -> RichTextLabel: + return %TextBox -func load_info(text:String, character:String = "", character_color:=Color(), icon:Texture= null) -> void: - %TextBox.text = text + +func get_name_label() -> Label: + return %NameLabel + + +func get_icon() -> TextureRect: + return %Icon + + +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() if character: - %NameLabel.text = character - %NameLabel.add_theme_color_override('font_color', character_color) - %NameLabel.show() + name_label.text = character + name_label.add_theme_color_override('font_color', character_color) + name_label.show() else: - %NameLabel.hide() + name_label.hide() + + var icon_node : TextureRect = get_icon() if icon == null: - %Icon.hide() + icon_node.hide() else: - %Icon.show() - %Icon.texture = icon - -# -#func prepare_textbox(history_root:Node) -> void: - #%TextBox.add_theme_font_override("normal_font", history_root.history_font_normal) - #%NameLabel.add_theme_font_override("font", history_root.history_font_normal) - #%NameLabel.add_theme_font_size_override("font_size", history_root.history_font_size) - #%TextBox.add_theme_font_override("bold_font", history_root.history_font_bold) - #%TextBox.add_theme_font_override("italics_font", history_root.history_font_italics) - #%TextBox.add_theme_font_size_override("normal_font_size", history_root.history_font_size) - #%TextBox.add_theme_font_size_override("bold_font_size", history_root.history_font_size) - #%TextBox.add_theme_font_size_override("italics_font_size", history_root.history_font_size) + icon_node.show() + icon_node.texture = icon \ No newline at end of file diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_icon.svg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_icon.svg index 82e02c6f..4d219366 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_icon.svg +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_icon.svg @@ -1,11 +1,23 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd index e7498bd3..d4200214 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_History/history_layer.gd @@ -6,12 +6,12 @@ extends DialogicLayoutLayer @export_group('Look') @export_subgroup('Font') -@export var font_use_global_size := true -@export var font_custom_size : int = 15 -@export var font_use_global_fonts := true -@export_file('*.ttf') var font_custom_normal := "" -@export_file('*.ttf') var font_custom_bold := "" -@export_file('*.ttf') var font_custom_italics := "" +@export var font_use_global_size: bool = true +@export var font_custom_size: int = 15 +@export var font_use_global_fonts: bool = true +@export_file('*.ttf', '*.tres') var font_custom_normal: String = "" +@export_file('*.ttf', '*.tres') var font_custom_bold: String = "" +@export_file('*.ttf', '*.tres') var font_custom_italics: String = "" @export_subgroup('Buttons') @export var show_open_button: bool = true @@ -32,78 +32,97 @@ var scroll_to_bottom_flag: bool = false @export_group('Private') @export var HistoryItem: PackedScene = null -var history_item_theme : Theme = null +var history_item_theme: Theme = null + +func get_show_history_button() -> Button: + return $ShowHistory + + +func get_hide_history_button() -> Button: + return $HideHistory + + +func get_history_box() -> ScrollContainer: + return %HistoryBox + + +func get_history_log() -> VBoxContainer: + return %HistoryLog func _ready() -> void: if Engine.is_editor_hint(): return + 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: - if DialogicUtil.autoload().has_subsystem('History'): - $ShowHistory.visible = show_open_button and DialogicUtil.autoload().History.simple_history_enabled + var history_subsystem: Node = DialogicUtil.autoload().get(&'History') + if history_subsystem != null: + get_show_history_button().visible = show_open_button and history_subsystem.get(&'simple_history_enabled') else: - self.visible = false + set(&'visible', false) history_item_theme = Theme.new() if font_use_global_size: - history_item_theme.default_font_size = get_global_setting('font_size', font_custom_size) + history_item_theme.default_font_size = get_global_setting(&'font_size', font_custom_size) else: history_item_theme.default_font_size = font_custom_size - if font_use_global_fonts and ResourceLoader.exists(get_global_setting('font', '')): - history_item_theme.default_font = load(get_global_setting('font', '')) + if font_use_global_fonts and ResourceLoader.exists(get_global_setting(&'font', '') as String): + history_item_theme.default_font = load(get_global_setting(&'font', '') as String) as Font elif ResourceLoader.exists(font_custom_normal): history_item_theme.default_font = load(font_custom_normal) if ResourceLoader.exists(font_custom_bold): - history_item_theme.set_font('RichtTextLabel', 'bold_font', load(font_custom_bold)) + history_item_theme.set_font(&'RichtTextLabel', &'bold_font', load(font_custom_bold) as Font) if ResourceLoader.exists(font_custom_italics): - history_item_theme.set_font('RichtTextLabel', 'italics_font', load(font_custom_italics)) + history_item_theme.set_font(&'RichtTextLabel', &'italics_font', load(font_custom_italics) as Font) -func _process(delta): +func _process(_delta : float) -> void: if Engine.is_editor_hint(): return - if scroll_to_bottom_flag and $HistoryBox.visible and %HistoryLog.get_child_count(): + if scroll_to_bottom_flag and get_history_box().visible and get_history_log().get_child_count(): await get_tree().process_frame - %HistoryBox.ensure_control_visible(%HistoryLog.get_children()[-1]) + get_history_box().ensure_control_visible(get_history_log().get_children()[-1] as Control) scroll_to_bottom_flag = false -func _on_show_history_pressed(): +func _on_show_history_pressed() -> void: DialogicUtil.autoload().paused = true show_history() func show_history() -> void: - for child in %HistoryLog.get_children(): + for child: Node in get_history_log().get_children(): child.queue_free() - for info in DialogicUtil.autoload().History.get_simple_history(): - var history_item = HistoryItem.instantiate() - history_item.theme = history_item_theme + var history_subsystem: Node = DialogicUtil.autoload().get(&'History') + for info: Dictionary in history_subsystem.call(&'get_simple_history'): + var history_item : Node = HistoryItem.instantiate() + history_item.set(&'theme', history_item_theme) match info.event_type: "Text": if info.has('character') and info['character']: if show_name_colors: - history_item.load_info(info['text'], info['character']+name_delimeter, info['character_color']) + history_item.call(&'load_info', info['text'], info['character']+name_delimeter, info['character_color']) else: - history_item.load_info(info['text'], info['character']+name_delimeter) + history_item.call(&'load_info', info['text'], info['character']+name_delimeter) else: - history_item.load_info(info['text']) + history_item.call(&'load_info', info['text']) "Character": if !show_join_and_leave: history_item.queue_free() continue - history_item.load_info('[i]'+info['text']) + history_item.call(&'load_info', '[i]'+info['text']) "Choice": - var choices_text := "" + var choices_text: String = "" if show_all_choices: - for i in info['all_choices']: + for i : String in info['all_choices']: if i.ends_with('#disabled'): choices_text += "- [i]("+i.trim_suffix('#disabled')+")[/i]\n" elif i == info['text']: @@ -112,20 +131,21 @@ func show_history() -> void: choices_text += "-> "+i+"\n" else: choices_text += "- [b]"+info['text']+"[/b]\n" - history_item.load_info(choices_text) + history_item.call(&'load_info', choices_text) - %HistoryLog.add_child(history_item) + get_history_log().add_child(history_item) if scroll_to_bottom: scroll_to_bottom_flag = true - $ShowHistory.hide() - $HideHistory.visible = show_close_button - %HistoryBox.show() + get_show_history_button().hide() + get_hide_history_button().visible = show_close_button + get_history_box().show() -func _on_hide_history_pressed(): +func _on_hide_history_pressed() -> void: DialogicUtil.autoload().paused = false - %HistoryBox.hide() - $HideHistory.hide() - $ShowHistory.visible = show_open_button and DialogicUtil.autoload().History.simple_history_enabled + get_history_box().hide() + get_hide_history_button().hide() + var history_subsystem: Node = DialogicUtil.autoload().get(&'History') + get_show_history_button().visible = show_open_button and history_subsystem.get(&'simple_history_enabled') diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg index 24198d06..02b5b02f 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg @@ -1,14 +1,19 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg.import b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg.import index 4a2d3988..b8126d4d 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg.import +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_SpeakerPortraitTextbox/speaker-textbox-icon.svg.import @@ -33,6 +33,6 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=0.3 +svg/scale=1.0 editor/scale_with_editor_scale=true editor/convert_colors_with_editor_theme=true 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 6d6712d1..5e28d9f7 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 @@ -6,118 +6,124 @@ enum LimitedAlignments {LEFT=0, RIGHT=1} @export_group('Text') @export_subgroup("Text") -@export var text_alignment :Alignments= Alignments.LEFT +@export var text_alignment: Alignments = Alignments.LEFT @export_subgroup('Size') -@export var text_use_global_size := true -@export var text_custom_size := 15 +@export var text_use_global_size: bool = true +@export var text_custom_size: int = 15 @export_subgroup('Color') -@export var text_use_global_color := true -@export var text_custom_color : Color = Color.WHITE +@export var text_use_global_color: bool = true +@export var text_custom_color: Color = Color.WHITE @export_subgroup('Fonts') -@export var use_global_fonts := true -@export_file('*.ttf') var custom_normal_font:String = "" -@export_file('*.ttf') var custom_bold_font:String = "" -@export_file('*.ttf') var custom_italic_font:String = "" -@export_file('*.ttf') var custom_bold_italic_font:String = "" +@export var use_global_fonts: bool = true +@export_file('*.ttf', '*.tres') var custom_normal_font: String = "" +@export_file('*.ttf', '*.tres') var custom_bold_font: String = "" +@export_file('*.ttf', '*.tres') var custom_italic_font: String = "" +@export_file('*.ttf', '*.tres') var custom_bold_italic_font: String = "" @export_group('Name Label') @export_subgroup("Color") enum NameLabelColorModes {GLOBAL_COLOR, CHARACTER_COLOR, CUSTOM_COLOR} -@export var name_label_color_mode := NameLabelColorModes.GLOBAL_COLOR -@export var name_label_custom_color := Color.WHITE +@export var name_label_color_mode: NameLabelColorModes = NameLabelColorModes.GLOBAL_COLOR +@export var name_label_custom_color: Color = Color.WHITE @export_subgroup("Behaviour") -@export var name_label_alignment := Alignments.LEFT -@export var name_label_hide_when_no_character := false +@export var name_label_alignment: Alignments = Alignments.LEFT +@export var name_label_hide_when_no_character: bool = false @export_subgroup("Font & Size") -@export var name_label_use_global_size := true -@export var name_label_custom_size := 15 -@export var name_label_use_global_font := true -@export_file('*.ttf') var name_label_customfont : String = "" +@export var name_label_use_global_size: bool = true +@export var name_label_custom_size: int = 15 +@export var name_label_use_global_font: bool = true +@export_file('*.ttf', '*.tres') var name_label_customfont: String = "" @export_group('Box') @export_subgroup("Box") -@export_file('*.tres') var box_panel := this_folder.path_join("default_stylebox.tres") -@export var box_modulate_global_color := true -@export var box_modulate_custom_color : Color = Color(0.47247135639191, 0.31728461384773, 0.16592600941658) -@export var box_size : Vector2 = Vector2(600, 160) -@export var box_distance := 25 +@export_file('*.tres') var box_panel: String = this_folder.path_join("default_stylebox.tres") +@export var box_modulate_global_color: bool = true +@export var box_modulate_custom_color: Color = Color(0.47247135639191, 0.31728461384773, 0.16592600941658) +@export var box_size: Vector2 = Vector2(600, 160) +@export var box_distance: int = 25 @export_group('Portrait') @export_subgroup('Portrait') -@export var portrait_stretch_factor = 0.3 -@export var portrait_position :LimitedAlignments = LimitedAlignments.LEFT -@export var portrait_bg_modulate := Color(0, 0, 0, 0.5137255191803) +@export var portrait_stretch_factor: float = 0.3 +@export var portrait_position: LimitedAlignments = LimitedAlignments.LEFT +@export var portrait_bg_modulate: Color = Color(0, 0, 0, 0.5137255191803) ## Called by dialogic whenever export overrides might change -func _apply_export_overrides(): +func _apply_export_overrides() -> void: ## FONT SETTINGS - %DialogicNode_DialogText.alignment = text_alignment + var dialog_text: DialogicNode_DialogText = %DialogicNode_DialogText + dialog_text.alignment = text_alignment as DialogicNode_DialogText.Alignment - var text_size := text_custom_size + var text_size: int = text_custom_size if text_use_global_size: - text_size = get_global_setting('font_size', text_custom_size) + text_size = get_global_setting(&'font_size', text_custom_size) - %DialogicNode_DialogText.add_theme_font_size_override("normal_font_size", text_size) - %DialogicNode_DialogText.add_theme_font_size_override("bold_font_size", text_size) - %DialogicNode_DialogText.add_theme_font_size_override("italics_font_size", text_size) - %DialogicNode_DialogText.add_theme_font_size_override("bold_italics_font_size", text_size) + dialog_text.add_theme_font_size_override(&"normal_font_size", text_size) + dialog_text.add_theme_font_size_override(&"bold_font_size", text_size) + dialog_text.add_theme_font_size_override(&"italics_font_size", text_size) + dialog_text.add_theme_font_size_override(&"bold_italics_font_size", text_size) - var text_color := text_custom_color + var text_color: Color = text_custom_color if text_use_global_color: - text_color = get_global_setting('font_color', text_custom_color) - %DialogicNode_DialogText.add_theme_color_override("default_color", text_color) + text_color = get_global_setting(&'font_color', text_custom_color) + dialog_text.add_theme_color_override(&"default_color", text_color) - var normal_font := custom_normal_font - if use_global_fonts and ResourceLoader.exists(get_global_setting('font', '')): - normal_font = get_global_setting('font', '') + var normal_font: String = custom_normal_font + if use_global_fonts and ResourceLoader.exists(get_global_setting(&'font', '') as String): + normal_font = get_global_setting(&'font', '') if !normal_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("normal_font", load(normal_font)) + dialog_text.add_theme_font_override(&"normal_font", load(normal_font) as Font) if !custom_bold_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("bold_font", load(custom_bold_font)) + dialog_text.add_theme_font_override(&"bold_font", load(custom_bold_font) as Font) if !custom_italic_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("italitc_font", load(custom_italic_font)) + dialog_text.add_theme_font_override(&"italics_font", load(custom_italic_font) as Font) if !custom_bold_italic_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("bold_italics_font", load(custom_bold_italic_font)) + dialog_text.add_theme_font_override(&"bold_italics_font", load(custom_bold_italic_font) as Font) ## BOX SETTINGS + var panel: PanelContainer = %Panel + var portrait_panel: Panel = %PortraitPanel if box_modulate_global_color: - %Panel.self_modulate = get_global_setting('bg_color', box_modulate_custom_color) + panel.self_modulate = get_global_setting(&'bg_color', box_modulate_custom_color) else: - %Panel.self_modulate = box_modulate_custom_color - %Panel.size = box_size - %Panel.position = Vector2(-box_size.x/2, -box_size.y-box_distance) - %PortraitPanel.size_flags_stretch_ratio = portrait_stretch_factor + panel.self_modulate = box_modulate_custom_color + panel.size = box_size + 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) - %Panel.add_theme_stylebox_override('panel', stylebox) + panel.add_theme_stylebox_override(&'panel', stylebox) ## PORTRAIT SETTINGS - %PortraitBackgroundColor.color = portrait_bg_modulate - %PortraitPanel.get_parent().move_child(%PortraitPanel, portrait_position) + var portrait_background_color: ColorRect = %PortraitBackgroundColor + portrait_background_color.color = portrait_bg_modulate + + portrait_panel.get_parent().move_child(portrait_panel, portrait_position) ## NAME LABEL SETTINGS + var name_label: DialogicNode_NameLabel = %DialogicNode_NameLabel if name_label_use_global_size: - %DialogicNode_NameLabel.add_theme_font_size_override("font_size", get_global_setting('font_size', name_label_custom_size)) + name_label.add_theme_font_size_override(&"font_size", get_global_setting(&'font_size', name_label_custom_size) as int) else: - %DialogicNode_NameLabel.add_theme_font_size_override("font_size", name_label_custom_size) + name_label.add_theme_font_size_override(&"font_size", name_label_custom_size) - var name_label_font := name_label_customfont - if name_label_use_global_font and ResourceLoader.exists(get_global_setting('font', '')): - name_label_font = get_global_setting('font', '') + var name_label_font: String = name_label_customfont + if name_label_use_global_font and ResourceLoader.exists(get_global_setting(&'font', '') as String): + name_label_font = get_global_setting(&'font', '') if !name_label_font.is_empty(): - %DialogicNode_NameLabel.add_theme_font_override('font', load(name_label_font)) + name_label.add_theme_font_override(&'font', load(name_label_font) as Font) - %DialogicNode_NameLabel.use_character_color = false + name_label.use_character_color = false match name_label_color_mode: NameLabelColorModes.GLOBAL_COLOR: - %DialogicNode_NameLabel.add_theme_color_override("font_color", get_global_setting('font_color', name_label_custom_color)) + name_label.add_theme_color_override(&"font_color", get_global_setting(&'font_color', name_label_custom_color) as Color) NameLabelColorModes.CUSTOM_COLOR: - %DialogicNode_NameLabel.add_theme_color_override("font_color", name_label_custom_color) + name_label.add_theme_color_override(&"font_color", name_label_custom_color) NameLabelColorModes.CHARACTER_COLOR: - %DialogicNode_NameLabel.use_character_color = true + name_label.use_character_color = true - %DialogicNode_NameLabel.horizontal_alignment = name_label_alignment - %DialogicNode_NameLabel.hide_when_empty = name_label_hide_when_no_character + name_label.horizontal_alignment = name_label_alignment as HorizontalAlignment + name_label.hide_when_empty = name_label_hide_when_no_character 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 766783c5..59fefc03 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) @@ -107,3 +108,6 @@ bbcode_enabled = true text = "Some text" scroll_following = true script = ExtResource("3_11puy") + +[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_TextInput/text_input_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.gd index 7abee44b..bd853aab 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer.gd @@ -4,10 +4,11 @@ extends DialogicLayoutLayer ## A layer that contains a text-input node. -func _apply_export_overrides(): - if self.theme == null: - self.theme = Theme.new() - - if get_global_setting('font', ''): - self.theme.default_font = load(get_global_setting('font', '')) - self.theme.default_font_size = get_global_setting('font_size', 0) +func _apply_export_overrides() -> void: + var layer_theme: Theme = get(&'theme') + if layer_theme == null: + layer_theme = Theme.new() + + if get_global_setting(&'font', ''): + layer_theme.default_font = load(get_global_setting(&'font', '') as String) + layer_theme.default_font_size = get_global_setting(&'font_size', 0) diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer_icon.svg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer_icon.svg index 3cc179cc..9f8d5920 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer_icon.svg +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_TextInput/text_input_layer_icon.svg @@ -1,13 +1,23 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/speech_bubble.gdshader index 17c529aa..c1e348fc 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 c2282c01..97773c96 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd @@ -1,28 +1,53 @@ extends Control -@onready var tail : Line2D = $Tail -@onready var bubble : Control = $Background -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 + +# Sets the padding shader paramter. +# It's the amount of spacing around the background to allow some wobbeling. +var bg_padding := 30 + + func _ready() -> void: + reset() + DialogicUtil.autoload().Choices.choices_shown.connect(_on_choices_shown) + + +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 @@ -38,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 @@ -60,52 +87,116 @@ func _process(delta): func open() -> void: + set_process(true) show() - %DialogText.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: - %DialogText.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 font :Font = %DialogText.get_theme_font("normal_font") - %DialogText.size = font.get_multiline_string_size(%DialogText.get_parsed_text(), HORIZONTAL_ALIGNMENT_LEFT, max_width, %DialogText.get_theme_font_size("normal_font_size")) - if DialogicUtil.autoload().Choices.is_question(DialogicUtil.autoload().current_event_idx): - font = $DialogText/ChoiceContainer/DialogicNode_ChoiceButton.get_theme_font('font') - %DialogText.size.y += font.get_string_size(%DialogText.get_parsed_text(), HORIZONTAL_ALIGNMENT_LEFT, max_width, $DialogText/ChoiceContainer/DialogicNode_ChoiceButton.get_theme_font_size("font_size")).y - %DialogText.position = -%DialogText.size/2 + _resize_bubble(get_base_content_size(), true) - _resize_bubble() - -func _resize_bubble() -> void: - var bubble_size :Vector2 = %DialogText.size+(padding*2) - var half_size :Vector2= (bubble_size / 2.0) - %DialogText.pivot_offset = half_size +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 := 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_choices_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 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 00000000..60ebcee1 --- /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 4e0d4cc7..6277c5f5 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 dad37a31..10fa7ba9 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd @@ -5,137 +5,185 @@ extends DialogicLayoutLayer @export_group("Main") @export_subgroup("Text") -@export var text_size := 15 +@export var text_size: int = 15 @export var text_color: Color = Color.BLACK @export_file('*.ttf') var normal_font: String = "" @export_file('*.ttf') var bold_font: String = "" @export_file('*.ttf') var italic_font: String = "" @export_file('*.ttf') var bold_italic_font: String = "" -@export var text_max_width := 300 +@export var text_max_width: int = 300 @export_subgroup('Box') -@export var box_modulate := Color.WHITE -@export var box_modulate_by_character_color := false -@export var box_padding := Vector2(10,10) -@export_range(0.1, 2) var box_corner_radius := 0.3 -@export_range(0.1, 5) var box_wobble_speed := 1 -@export_range(0, 1) var box_wobbliness := 0.2 +@export 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(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 := 50 -@export var behaviour_direction := Vector2(1, -1) +@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 := true -@export var name_label_font_size := 15 +@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 := true -@export var name_label_color := Color.BLACK -@export var name_label_box_modulate : Color = Color.WHITE -@export var name_label_padding := Vector2(5,0) -@export var name_label_offset := Vector2(0,0) +@export 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 := 15 -@export var choices_text_color := Color.LIGHT_SLATE_GRAY -@export var choices_text_color_hover := Color.DARK_GRAY -@export var choices_text_color_focus := Color.BLACK +@export var choices_text_size: int = 15 +@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 = [] -var fallback_bubble: Control = 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() -> Control: - var new_bubble: Control = 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(): - for bubble in bubbles: - bubble_apply_overrides(bubble) +func _apply_export_overrides() -> void: + pass - if fallback_bubble: - bubble_apply_overrides(fallback_bubble) -func bubble_apply_overrides(bubble:Control) -> 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_node('DialogText') - rtl.add_theme_font_size_override('normal_font', text_size) - rtl.add_theme_font_size_override("normal_font_size", text_size) - rtl.add_theme_font_size_override("bold_font_size", text_size) - rtl.add_theme_font_size_override("italics_font_size", text_size) - rtl.add_theme_font_size_override("bold_italics_font_size", text_size) + 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) + rtl.add_theme_font_size_override(&"italics_font_size", text_size) + rtl.add_theme_font_size_override(&"bold_italics_font_size", text_size) - rtl.add_theme_color_override("default_color", text_color) + rtl.add_theme_color_override(&"default_color", text_color) if !normal_font.is_empty(): - rtl.add_theme_font_override("normal_font", load(normal_font)) + rtl.add_theme_font_override(&"normal_font", load(normal_font) as Font) if !bold_font.is_empty(): - rtl.add_theme_font_override("bold_font", load(bold_font)) + rtl.add_theme_font_override(&"bold_font", load(bold_font) as Font) if !italic_font.is_empty(): - rtl.add_theme_font_override("italitc_font", load(italic_font)) + rtl.add_theme_font_override(&"italitc_font", load(italic_font) as Font) if !bold_italic_font.is_empty(): - rtl.add_theme_font_override("bold_italics_font", load(bold_italic_font)) - bubble.max_width = text_max_width + rtl.add_theme_font_override(&"bold_italics_font", load(bold_italic_font) as Font) + bubble.set(&'max_width', text_max_width) ## BOX & TAIL COLOR - bubble.get_node('Tail').default_color = box_modulate - bubble.get_node('Background').color = box_modulate - bubble.get_node('Background').material.set_shader_parameter('radius', box_corner_radius) - bubble.get_node('Background').material.set_shader_parameter('crease', box_wobbliness*0.1) - bubble.get_node('Background').material.set_shader_parameter('speed', box_wobble_speed) - if box_modulate_by_character_color and bubble.character != null: - bubble.get_node('Tail').modulate = bubble.character.color - bubble.get_node('Background').modulate = bubble.character.color + 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 : Label = bubble.get_node('%NameLabel') - nl.add_theme_font_size_override("font_size", name_label_font_size) + 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)) + 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: - nl.add_theme_color_override("font_color", name_label_color) - var nlp : Container = bubble.get_node('DialogText/NameLabel') + 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.name_label_box nlp.self_modulate = name_label_box_modulate - nlp.get_theme_stylebox('panel').content_margin_left = name_label_padding.x - nlp.get_theme_stylebox('panel').content_margin_right = name_label_padding.x - nlp.get_theme_stylebox('panel').content_margin_top = name_label_padding.y - nlp.get_theme_stylebox('panel').content_margin_bottom = name_label_padding.y - nlp.position += name_label_offset + if name_label_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 + bubble.name_label_offset = name_label_offset + bubble.name_label_alignment = name_label_alignment if !name_label_enabled: nlp.queue_free() ## CHOICE SETTINGS - var choice_theme := Theme.new() - choice_theme.set_font_size('font_size', 'Button', choices_text_size) - choice_theme.set_color('font_color', 'Button', choices_text_color) - choice_theme.set_color('font_pressed_color', 'Button', choices_text_color) - choice_theme.set_color('font_hover_color', 'Button', choices_text_color_hover) - choice_theme.set_color('font_focus_color', 'Button', choices_text_color_focus) - - bubble.get_node('DialogText/ChoiceContainer').theme = choice_theme - - ## BEHAVIOUR - bubble.safe_zone = behaviour_distance - bubble.base_direction = behaviour_direction + 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) + 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 6ed91452..5909325e 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 9dba926e..a0371f0e 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 @@ -6,93 +6,101 @@ extends DialogicLayoutLayer @export_group("Text") @export_subgroup('Font') -@export var font_use_global := true -@export_file('*.ttf') var font_custom : String = "" +@export var font_use_global: bool = true +@export_file('*.ttf', '*.tres') var font_custom: String = "" @export_subgroup('Size') -@export var font_size_use_global := true -@export var font_size_custom := 16 +@export var font_size_use_global: bool = true +@export var font_size_custom: int = 16 @export_subgroup('Color') -@export var text_color_use_global := true -@export var text_color_custom := Color.WHITE -@export var text_color_pressed := Color.WHITE -@export var text_color_hovered := Color.GRAY -@export var text_color_disabled := Color.DARK_GRAY -@export var text_color_focused := Color.WHITE +@export var text_color_use_global: bool = true +@export var text_color_custom: Color = Color.WHITE +@export var text_color_pressed: Color = Color.WHITE +@export var text_color_hovered: Color = Color.GRAY +@export var text_color_disabled: Color = Color.DARK_GRAY +@export var text_color_focused: Color = Color.WHITE @export_group('Boxes') @export_subgroup('Panels') -@export_file('*.tres') var boxes_stylebox_normal := "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/choice_panel_normal.tres" -@export_file('*.tres') var boxes_stylebox_hovered := "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/choice_panel_hover.tres" -@export_file('*.tres') var boxes_stylebox_pressed := "" -@export_file('*.tres') var boxes_stylebox_disabled := "" -@export_file('*.tres') var boxes_stylebox_focused := "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/choice_panel_focus.tres" +@export_file('*.tres') var boxes_stylebox_normal: String = "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/choice_panel_normal.tres" +@export_file('*.tres') var boxes_stylebox_hovered: String = "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/choice_panel_hover.tres" +@export_file('*.tres') var boxes_stylebox_pressed: String = "" +@export_file('*.tres') var boxes_stylebox_disabled: String = "" +@export_file('*.tres') var boxes_stylebox_focused: String = "res://addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/choice_panel_focus.tres" @export_subgroup('Modulate') @export_subgroup('Size & Position') -@export var boxes_v_separation := 10 -@export var boxes_fill_width := true -@export var boxes_min_size := Vector2() +@export var boxes_v_separation: int = 10 +@export var boxes_fill_width: bool = true +@export var boxes_min_size: Vector2 = Vector2() @export_group('Sounds') -@export_range(-80, 24, 0.01) var sounds_volume := -10 -@export_file("*.wav", "*.ogg", "*.mp3") var sounds_pressed := "res://addons/dialogic/Example Assets/sound-effects/typing1.wav" -@export_file("*.wav", "*.ogg", "*.mp3") var sounds_hover := "res://addons/dialogic/Example Assets/sound-effects/typing2.wav" -@export_file("*.wav", "*.ogg", "*.mp3") var sounds_focus := "res://addons/dialogic/Example Assets/sound-effects/typing4.wav" +@export_range(-80, 24, 0.01) var sounds_volume: float = -10 +@export_file("*.wav", "*.ogg", "*.mp3") var sounds_pressed: String = "res://addons/dialogic/Example Assets/sound-effects/typing1.wav" +@export_file("*.wav", "*.ogg", "*.mp3") var sounds_hover: String = "res://addons/dialogic/Example Assets/sound-effects/typing2.wav" +@export_file("*.wav", "*.ogg", "*.mp3") var sounds_focus: String = "res://addons/dialogic/Example Assets/sound-effects/typing4.wav" + +func get_choices() -> VBoxContainer: + return $Choices + + +func get_button_sound() -> DialogicNode_ButtonSound: + return %DialogicNode_ButtonSound ## Method that applies all exported settings -func _apply_export_overrides(): +func _apply_export_overrides() -> void: # apply text settings - var theme: Theme = Theme.new() + var layer_theme: Theme = Theme.new() # font - if font_use_global and get_global_setting('font', false): - theme.set_font('font', 'Button', load(get_global_setting('font', ''))) + if font_use_global and get_global_setting(&'font', false): + layer_theme.set_font(&'font', &'Button', load(get_global_setting(&'font', '') as String) as Font) elif ResourceLoader.exists(font_custom): - theme.set_font('font', 'Button', load(font_custom)) + layer_theme.set_font(&'font', &'Button', load(font_custom) as Font) # font size if font_size_use_global: - theme.set_font_size('font_size', 'Button', get_global_setting('font_size', font_size_custom)) + layer_theme.set_font_size(&'font_size', &'Button', get_global_setting(&'font_size', font_size_custom) as int) else: - theme.set_font_size('font_size', 'Button', font_size_custom) + layer_theme.set_font_size(&'font_size', &'Button', font_size_custom) # font color if text_color_use_global: - theme.set_color('font_color', 'Button', get_global_setting('font_color', text_color_custom)) + layer_theme.set_color(&'font_color', &'Button', get_global_setting(&'font_color', text_color_custom) as Color) else: - theme.set_color('font_color', 'Button', text_color_custom) + layer_theme.set_color(&'font_color', &'Button', text_color_custom) - theme.set_color('font_pressed_color', 'Button', text_color_pressed) - theme.set_color('font_hover_color', 'Button', text_color_hovered) - theme.set_color('font_disabled_color', 'Button', text_color_disabled) - theme.set_color('font_pressed_color', 'Button', text_color_pressed) - theme.set_color('font_focus_color', 'Button', text_color_focused) + layer_theme.set_color(&'font_pressed_color', &'Button', text_color_pressed) + layer_theme.set_color(&'font_hover_color', &'Button', text_color_hovered) + layer_theme.set_color(&'font_disabled_color', &'Button', text_color_disabled) + layer_theme.set_color(&'font_pressed_color', &'Button', text_color_pressed) + layer_theme.set_color(&'font_focus_color', &'Button', text_color_focused) # apply box settings if ResourceLoader.exists(boxes_stylebox_normal): var style_box: StyleBox = load(boxes_stylebox_normal) - theme.set_stylebox('normal', 'Button', style_box) - theme.set_stylebox('hover', 'Button', style_box) - theme.set_stylebox('pressed', 'Button', style_box) - theme.set_stylebox('disabled', 'Button', style_box) - theme.set_stylebox('focus', 'Button', style_box) + layer_theme.set_stylebox(&'normal', &'Button', style_box) + layer_theme.set_stylebox(&'hover', &'Button', style_box) + layer_theme.set_stylebox(&'pressed', &'Button', style_box) + layer_theme.set_stylebox(&'disabled', &'Button', style_box) + layer_theme.set_stylebox(&'focus', &'Button', style_box) if ResourceLoader.exists(boxes_stylebox_hovered): - theme.set_stylebox('hover', 'Button', load(boxes_stylebox_hovered)) + layer_theme.set_stylebox(&'hover', &'Button', load(boxes_stylebox_hovered) as StyleBox) if ResourceLoader.exists(boxes_stylebox_pressed): - theme.set_stylebox('pressed', 'Button', load(boxes_stylebox_pressed)) + layer_theme.set_stylebox(&'pressed', &'Button', load(boxes_stylebox_pressed) as StyleBox) if ResourceLoader.exists(boxes_stylebox_disabled): - theme.set_stylebox('disabled', 'Button', load(boxes_stylebox_disabled)) + layer_theme.set_stylebox(&'disabled', &'Button', load(boxes_stylebox_disabled) as StyleBox) if ResourceLoader.exists(boxes_stylebox_focused): - theme.set_stylebox('focus', 'Button', load(boxes_stylebox_focused)) + layer_theme.set_stylebox(&'focus', &'Button', load(boxes_stylebox_focused) as StyleBox) - $Choices.add_theme_constant_override("separation", boxes_v_separation) + get_choices().add_theme_constant_override(&"separation", boxes_v_separation) - for choice in $Choices.get_children(): - if not choice is DialogicNode_ChoiceButton: + for child: Node in get_choices().get_children(): + if not child is DialogicNode_ChoiceButton: continue + var choice: DialogicNode_ChoiceButton = child as DialogicNode_ChoiceButton if boxes_fill_width: choice.size_flags_horizontal = Control.SIZE_FILL @@ -101,10 +109,11 @@ func _apply_export_overrides(): choice.custom_minimum_size = boxes_min_size - self.theme = theme + set(&'theme', layer_theme) # apply sound settings - %DialogicNode_ButtonSound.volume_db = sounds_volume - %DialogicNode_ButtonSound.sound_pressed = load(sounds_pressed) - %DialogicNode_ButtonSound.sound_hover = load(sounds_hover) - %DialogicNode_ButtonSound.sound_focus = load(sounds_focus) + var button_sound: DialogicNode_ButtonSound = get_button_sound() + button_sound.volume_db = sounds_volume + button_sound.sound_pressed = load(sounds_pressed) + button_sound.sound_hover = load(sounds_hover) + button_sound.sound_focus = load(sounds_focus) 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 c5e34585..75482d9e 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 @@ -8,7 +8,7 @@ [sub_resource type="AudioStream" id="AudioStream_pe27w"] -[node name="VN_Choice_Layer" type="Control"] +[node name="VN_ChoiceLayer" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg index e7e17b12..9e00fc66 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg @@ -1,17 +1,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg.import b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg.import index 0ecfdc48..f8ad81be 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg.import +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/portrait_layer_icon.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://fwi64s4gbob2" path="res://.godot/imported/portrait_layer_icon.svg-4bc8b0ebd4dd0977a12c09f30758d7e1.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=0.3 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.gd index 60fd45c3..4844cc85 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Portraits/vn_portrait_layer.gd @@ -4,11 +4,12 @@ extends DialogicLayoutLayer ## A layer that allows showing 5 portraits, like in a visual novel. ## The canvas layer that the portraits are on. -@export var portrait_size_mode := DialogicNode_PortraitContainer.SizeModes.FIT_SCALE_HEIGHT +@export var portrait_size_mode: DialogicNode_PortraitContainer.SizeModes = DialogicNode_PortraitContainer.SizeModes.FIT_SCALE_HEIGHT -func _apply_export_overrides(): +func _apply_export_overrides() -> void: # apply portrait size - for child in %Portraits.get_children(): + for child: DialogicNode_PortraitContainer in %Portraits.get_children(): child.size_mode = portrait_size_mode + child.update_portrait_transforms() 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 374bc60f..b5e79f02 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 @@ -10,6 +10,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +mouse_filter = 2 script = ExtResource("1_1i7em") [node name="Portraits" type="Control" parent="."] diff --git a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd index 076d6222..43eca1f7 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/animations.gd @@ -11,63 +11,76 @@ var animation_in : AnimationsIn var animation_out : AnimationsOut var animation_new_text : AnimationsNewText -var full_clear := true +var full_clear : bool = true -func _ready(): - DialogicUtil.autoload().Text.animation_textbox_hide.connect(_on_textbox_hide) - DialogicUtil.autoload().Text.animation_textbox_show.connect(_on_textbox_show) - DialogicUtil.autoload().Text.animation_textbox_new_text.connect(_on_textbox_new_text) - DialogicUtil.autoload().Text.about_to_show_text.connect(_on_about_to_show_text) +func get_text_panel() -> PanelContainer: + return %DialogTextPanel -func _on_textbox_show(): +func get_dialog() -> DialogicNode_DialogText: + return %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) + + +func _on_textbox_show() -> void: if animation_in == AnimationsIn.NONE: return play('RESET') - DialogicUtil.autoload().Animation.start_animating() - %DialogTextPanel.get_parent().get_parent().modulate = Color.TRANSPARENT - %DialogicNode_DialogText.text = "" + 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 = "" match animation_in: AnimationsIn.POP_IN: play("textbox_pop") AnimationsIn.FADE_UP: play("textbox_fade_up") - if not animation_finished.is_connected(DialogicUtil.autoload().Animation.animation_finished): - animation_finished.connect(DialogicUtil.autoload().Animation.animation_finished, CONNECT_ONE_SHOT) + 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) -func _on_textbox_hide(): +func _on_textbox_hide() -> void: if animation_out == AnimationsOut.NONE: return play('RESET') - DialogicUtil.autoload().Animation.start_animating() + var animation_system : Node = DialogicUtil.autoload().get(&'Animations') + animation_system.call(&'start_animating') match animation_out: AnimationsOut.POP_OUT: play_backwards("textbox_pop") AnimationsOut.FADE_DOWN: play_backwards("textbox_fade_up") - if not animation_finished.is_connected(DialogicUtil.autoload().Animation.animation_finished): - animation_finished.connect(DialogicUtil.autoload().Animation.animation_finished, CONNECT_ONE_SHOT) + 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) func _on_about_to_show_text(info:Dictionary) -> void: full_clear = !info.append -func _on_textbox_new_text(): - if DialogicUtil.autoload().Input.auto_skip.enabled: +func _on_textbox_new_text() -> void: + if DialogicUtil.autoload().Inputs.auto_skip.enabled: return if animation_new_text == AnimationsNewText.NONE: return - DialogicUtil.autoload().Animation.start_animating() + var animation_system : Node = DialogicUtil.autoload().get(&'Animation') + animation_system.call(&'start_animating') if full_clear: - %DialogicNode_DialogText.text = "" + get_dialog().text = "" match animation_new_text: AnimationsNewText.WIGGLE: play("new_text") - if not animation_finished.is_connected(DialogicUtil.autoload().Animation.animation_finished): - animation_finished.connect(DialogicUtil.autoload().Animation.animation_finished, CONNECT_ONE_SHOT) + 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) 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 009a3d9e..fa8bbffc 100644 --- a/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/autoadvance_indicator.gd +++ b/addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Textbox/autoadvance_indicator.gd @@ -1,13 +1,13 @@ extends Range -var enabled := true +var enabled : bool = true -func _process(delta): +func _process(_delta : float) -> void: if !enabled: hide() return - if DialogicUtil.autoload().Input.auto_advance.get_progress() < 0: + if DialogicUtil.autoload().Inputs.auto_advance.get_progress() < 0: hide() else: show() - value = DialogicUtil.autoload().Input.auto_advance.get_progress() + value = DialogicUtil.autoload().Inputs.auto_advance.get_progress() 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 588d789e..e5b1c585 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 @@ -1,15 +1,22 @@ @tool extends DialogicLayoutLayer - -## A layer that contains +## This layer's scene file contains following nodes: ## - a dialog_text node ## - a name_label node ## - a next_indicator node ## - a type_sound node ## -## as well as custom +## As well as custom: ## - animations ## - auto-advance progress indicator +## +## If you want to customize this layer, here is a little rundown of this layer: +## The Layer Settings are divided into the `@export_group`s below. +## They get applied in [method _apply_export_overrides]. +## Each `@export_group` has its own method to apply the settings to the scene. +## If you want to change a specific part inside the scene, you can simply +## remove or add # (commenting) to the method line. + enum Alignments {LEFT, CENTER, RIGHT} @@ -19,196 +26,253 @@ enum AnimationsOut {NONE, POP_OUT, FADE_DOWN} enum AnimationsNewText {NONE, WIGGLE} @export_group("Text") + @export_subgroup("Alignment & Size") -@export var text_alignment :Alignments= Alignments.LEFT -@export var text_use_global_size := true -@export var text_size := 15 +@export var text_alignment: Alignments= Alignments.LEFT +@export var text_use_global_size: bool = true +@export var text_size: int = 15 @export_subgroup("Color") -@export var text_use_global_color := true -@export var text_custom_color : Color = Color.WHITE +@export var text_use_global_color: bool = true +@export var text_custom_color: Color = Color.WHITE @export_subgroup('Font') -@export var text_use_global_font := true -@export_file('*.ttf') var normal_font:String = "" -@export_file('*.ttf') var bold_font:String = "" -@export_file('*.ttf') var italic_font:String = "" -@export_file('*.ttf') var bold_italic_font:String = "" +@export var text_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_group("Box") + @export_subgroup("Panel") -@export_file("*.tres") var box_panel := this_folder.path_join("vn_textbox_default_panel.tres") +@export_file("*.tres") var box_panel: String = this_folder.path_join("vn_textbox_default_panel.tres") + @export_subgroup("Color") -@export var box_color_use_global := true -@export var box_color_custom : Color = Color.BLACK +@export var box_color_use_global: bool = true +@export var box_color_custom: Color = Color.BLACK + @export_subgroup("Size & Position") -@export var box_size : Vector2 = Vector2(550, 110) -@export var box_margin_bottom := 15 +@export var box_size: Vector2 = Vector2(550, 110) +@export var box_margin_bottom: int = 15 + @export_subgroup("Animation") -@export var box_animation_in := AnimationsIn.FADE_UP -@export var box_animation_out := AnimationsOut.FADE_DOWN -@export var box_animation_new_text := AnimationsNewText.NONE +@export var box_animation_in: AnimationsIn = AnimationsIn.FADE_UP +@export var box_animation_out: AnimationsOut = AnimationsOut.FADE_DOWN +@export var box_animation_new_text: AnimationsNewText = AnimationsNewText.NONE + @export_group("Name Label") + @export_subgroup('Color') -@export var name_label_use_global_color := true -@export var name_label_use_character_color := true -@export var name_label_custom_color := Color.WHITE +@export var name_label_use_global_color: bool= true +@export var name_label_use_character_color: bool = true +@export var name_label_custom_color: Color = Color.WHITE + @export_subgroup('Font') -@export var name_label_use_global_font := true -@export_file('*.ttf') var name_label_font : String = "" -@export var name_label_use_global_font_size := true -@export var name_label_custom_font_size := 15 +@export var name_label_use_global_font: bool = true +@export_file('*.ttf', '*.tres') var name_label_font: String = "" +@export var name_label_use_global_font_size: bool = true +@export var name_label_custom_font_size: int = 15 + @export_subgroup('Box') -@export_file("*.tres") var name_label_box_panel := this_folder.path_join("vn_textbox_name_label_panel.tres") -@export var name_label_box_use_global_color := true -@export var name_label_box_modulate : Color = box_color_custom +@export_file("*.tres") var name_label_box_panel: String = this_folder.path_join("vn_textbox_name_label_panel.tres") +@export var name_label_box_use_global_color: bool = true +@export var name_label_box_modulate: Color = box_color_custom + @export_subgroup('Alignment') -@export var name_label_alignment := Alignments.LEFT -@export var name_label_box_offset := Vector2.ZERO +@export var name_label_alignment: Alignments = Alignments.LEFT +@export var name_label_box_offset: Vector2 = Vector2.ZERO + @export_group("Indicators") + @export_subgroup("Next Indicator") -@export var next_indicator_enabled := true -@export var next_indicator_show_on_questions := true -@export var next_indicator_show_on_autoadvance := false -@export_enum('bounce', 'blink', 'none') var next_indicator_animation := 0 -@export_file("*.png","*.svg") var next_indicator_texture := '' -@export var next_indicator_size := Vector2(25,25) +@export var next_indicator_enabled: bool = true +@export var next_indicator_show_on_questions: bool = true +@export var next_indicator_show_on_autoadvance: bool = false +@export_enum('bounce', 'blink', 'none') var next_indicator_animation: int = 0 +@export_file("*.png","*.svg","*.tres") var next_indicator_texture: String = '' +@export var next_indicator_size: Vector2 = Vector2(25,25) @export_subgroup("Autoadvance") -@export var autoadvance_progressbar := true +@export var autoadvance_progressbar: bool = true + @export_group('Sounds') + @export_subgroup('Typing Sounds') -@export var typing_sounds_enabled := true -@export var typing_sounds_mode := DialogicNode_TypeSounds.Modes.INTERRUPT -@export_dir var typing_sounds_sounds_folder := "res://addons/dialogic/Example Assets/sound-effects/" -@export_file("*.wav", "*.ogg", "*.mp3") var typing_sounds_end_sound := "" -@export_range(1, 999, 1) var typing_sounds_every_nths_character := 1 -@export_range(0.01, 4, 0.01) var typing_sounds_pitch := 1.0 -@export_range(0.0, 3.0) var typing_sounds_pitch_variance := 0.0 -@export_range(-80, 24, 0.01) var typing_sounds_volume := -10 -@export_range(0.0, 10) var typing_sounds_volume_variance := 0.0 -@export var typing_sounds_ignore_characters := " .,!?" - - -func _apply_export_overrides(): +@export var typing_sounds_enabled: bool = true +@export var typing_sounds_mode: DialogicNode_TypeSounds.Modes = DialogicNode_TypeSounds.Modes.INTERRUPT +@export_dir var typing_sounds_sounds_folder: String = "res://addons/dialogic/Example Assets/sound-effects/" +@export_file("*.wav", "*.ogg", "*.mp3") var typing_sounds_end_sound: String = "" +@export_range(1, 999, 1) var typing_sounds_every_nths_character: int = 1 +@export_range(0.01, 4, 0.01) var typing_sounds_pitch: float = 1.0 +@export_range(0.0, 3.0) var typing_sounds_pitch_variance: float = 0.0 +@export_range(-80, 24, 0.01) var typing_sounds_volume: float = -10 +@export_range(0.0, 10) var typing_sounds_volume_variance: float = 0.0 +@export var typing_sounds_ignore_characters: String = " .,!?" + + +func _apply_export_overrides() -> void: if !is_inside_tree(): await ready ## FONT SETTINGS - %DialogicNode_DialogText.alignment = text_alignment + _apply_text_settings() - if text_use_global_size: - text_size = get_global_setting('font_size', text_size) - %DialogicNode_DialogText.add_theme_font_size_override("normal_font_size", text_size) - %DialogicNode_DialogText.add_theme_font_size_override("bold_font_size", text_size) - %DialogicNode_DialogText.add_theme_font_size_override("italics_font_size", text_size) - %DialogicNode_DialogText.add_theme_font_size_override("bold_italics_font_size", text_size) - if text_use_global_color: - %DialogicNode_DialogText.add_theme_color_override("default_color", get_global_setting('font_color', text_custom_color)) - else: - %DialogicNode_DialogText.add_theme_color_override("default_color", text_custom_color) + ## BOX SETTINGS + _apply_box_settings() - if text_use_global_font and get_global_setting('font', false): - %DialogicNode_DialogText.add_theme_font_override("normal_font", load(get_global_setting('font', ''))) - elif !normal_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("normal_font", load(normal_font)) - if !bold_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("bold_font", load(bold_font)) - if !italic_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("italitc_font", load(italic_font)) - if !bold_italic_font.is_empty(): - %DialogicNode_DialogText.add_theme_font_override("bold_italics_font", load(bold_italic_font)) + ## BOX ANIMATIONS + _apply_box_animations_settings() - ## BOX SETTINGS + ## NAME LABEL SETTINGS + _apply_name_label_settings() + + ## NEXT INDICATOR SETTINGS + _apply_indicator_settings() + + ## OTHER + var progress_bar: ProgressBar = %AutoAdvanceProgressbar + progress_bar.set(&'enabled', autoadvance_progressbar) + + #### SOUNDS + + ## TYPING SOUNDS + _apply_sounds_settings() + + +## Applies all text box settings to the scene. +## Except the box animations. +func _apply_box_settings() -> void: + var dialog_text_panel: PanelContainer = %DialogTextPanel if ResourceLoader.exists(box_panel): - %DialogTextPanel.add_theme_stylebox_override('panel', load(box_panel)) + dialog_text_panel.add_theme_stylebox_override(&'panel', load(box_panel) as StyleBox) if box_color_use_global: - %DialogTextPanel.self_modulate = get_global_setting('bg_color', box_color_custom) + dialog_text_panel.self_modulate = get_global_setting(&'bg_color', box_color_custom) else: - %DialogTextPanel.self_modulate = box_color_custom + dialog_text_panel.self_modulate = box_color_custom - %DialogTextPanel.custom_minimum_size = box_size - %Minimizer.size = Vector2.ZERO - %Minimizer.position.y = -box_margin_bottom + var sizer: Control = %Sizer + sizer.size = box_size + sizer.position = box_size * Vector2(-0.5, -1)+Vector2(0, -box_margin_bottom) - ## BOX ANIMATIONS - %Animations.animation_in = box_animation_in - %Animations.animation_out = box_animation_out - %Animations.animation_new_text = box_animation_new_text - ## NAME LABEL SETTINGS +## Applies box animations settings to the scene. +func _apply_box_animations_settings() -> void: + var animations: AnimationPlayer = %Animations + animations.set(&'animation_in', box_animation_in) + animations.set(&'animation_out', box_animation_out) + animations.set(&'animation_new_text', box_animation_new_text) + + +## Applies all name label settings to the scene. +func _apply_name_label_settings() -> void: + var name_label: DialogicNode_NameLabel = %DialogicNode_NameLabel + if name_label_use_global_font_size: - %DialogicNode_NameLabel.add_theme_font_size_override("font_size", get_global_setting('font_size', name_label_custom_font_size)) + name_label.add_theme_font_size_override(&"font_size", get_global_setting(&'font_size', name_label_custom_font_size) as int) else: - %DialogicNode_NameLabel.add_theme_font_size_override("font_size", name_label_custom_font_size) + name_label.add_theme_font_size_override(&"font_size", name_label_custom_font_size) - if name_label_use_global_font and get_global_setting('font', false): - %DialogicNode_NameLabel.add_theme_font_override('font', load(get_global_setting('font', ''))) + if name_label_use_global_font and get_global_setting(&'font', false): + name_label.add_theme_font_override(&'font', load(get_global_setting(&'font', '') as String) as Font) elif not name_label_font.is_empty(): - %DialogicNode_NameLabel.add_theme_font_override('font', load(name_label_font)) + name_label.add_theme_font_override(&'font', load(name_label_font) as Font) if name_label_use_global_color: - %DialogicNode_NameLabel.add_theme_color_override("font_color", get_global_setting('font_color', name_label_custom_color)) + name_label.add_theme_color_override(&"font_color", get_global_setting(&'font_color', name_label_custom_color) as Color) else: - %DialogicNode_NameLabel.add_theme_color_override("font_color", name_label_custom_color) + name_label.add_theme_color_override(&"font_color", name_label_custom_color) - %DialogicNode_NameLabel.use_character_color = name_label_use_character_color + name_label.use_character_color = name_label_use_character_color + var name_label_panel: PanelContainer = %NameLabelPanel if ResourceLoader.exists(name_label_box_panel): - %NameLabelPanel.add_theme_stylebox_override('panel', load(name_label_box_panel)) + name_label_panel.add_theme_stylebox_override(&'panel', load(name_label_box_panel) as StyleBox) else: - %NameLabelPanel.add_theme_stylebox_override('panel', load(this_folder.path_join("vn_textbox_name_label_panel.tres"))) + name_label_panel.add_theme_stylebox_override(&'panel', load(this_folder.path_join("vn_textbox_name_label_panel.tres")) as StyleBox) if name_label_box_use_global_color: - %NameLabelPanel.self_modulate = get_global_setting('bg_color', name_label_box_modulate) + name_label_panel.self_modulate = get_global_setting(&'bg_color', name_label_box_modulate) else: - %NameLabelPanel.self_modulate = name_label_box_modulate + name_label_panel.self_modulate = name_label_box_modulate + var dialog_text_panel: PanelContainer = %DialogTextPanel + name_label_panel.position = name_label_box_offset+Vector2(0, -40) + name_label_panel.position -= Vector2( + dialog_text_panel.get_theme_stylebox(&'panel', &'PanelContainer').content_margin_left, + dialog_text_panel.get_theme_stylebox(&'panel', &'PanelContainer').content_margin_top) + name_label_panel.anchor_left = name_label_alignment/2.0 + name_label_panel.anchor_right = name_label_alignment/2.0 + name_label_panel.grow_horizontal = [1, 2, 0][name_label_alignment] + + +## Applies all text settings to the scene. +func _apply_text_settings() -> void: + var dialog_text: DialogicNode_DialogText = %DialogicNode_DialogText + dialog_text.alignment = text_alignment as DialogicNode_DialogText.Alignment - %NameLabelPanel.position = name_label_box_offset+Vector2(0, -40) - %NameLabelPanel.position -= Vector2( - %DialogTextPanel.get_theme_stylebox('panel', 'PanelContainer').content_margin_left, - %DialogTextPanel.get_theme_stylebox('panel', 'PanelContainer').content_margin_top) - %NameLabelPanel.anchor_left = name_label_alignment/2.0 - %NameLabelPanel.anchor_right = name_label_alignment/2.0 - %NameLabelPanel.grow_horizontal = [1, 2, 0][name_label_alignment] + if text_use_global_size: + text_size = get_global_setting(&'font_size', text_size) + dialog_text.add_theme_font_size_override(&"normal_font_size", text_size) + dialog_text.add_theme_font_size_override(&"bold_font_size", text_size) + dialog_text.add_theme_font_size_override(&"italics_font_size", text_size) + dialog_text.add_theme_font_size_override(&"bold_italics_font_size", text_size) - ## NEXT INDICATOR SETTINGS - if !next_indicator_enabled: - %NextIndicator.queue_free() + if text_use_global_color: + dialog_text.add_theme_color_override(&"default_color", get_global_setting(&'font_color', text_custom_color) as Color) else: - %NextIndicator.animation = next_indicator_animation - if FileAccess.file_exists(next_indicator_texture): - %NextIndicator.texture = load(next_indicator_texture) - %NextIndicator.show_on_questions = next_indicator_show_on_questions - %NextIndicator.show_on_autoadvance = next_indicator_show_on_autoadvance - %NextIndicator.texture_size = next_indicator_size + dialog_text.add_theme_color_override(&"default_color", text_custom_color) - ## OTHER - %AutoAdvanceProgressbar.enabled = autoadvance_progressbar + if text_use_global_font and get_global_setting(&'font', false): + dialog_text.add_theme_font_override(&"normal_font", load(get_global_setting(&'font', '') as String) as Font) + elif !normal_font.is_empty(): + dialog_text.add_theme_font_override(&"normal_font", load(normal_font) as Font) + if !bold_font.is_empty(): + dialog_text.add_theme_font_override(&"bold_font", load(bold_font) as Font) + if !italics_font.is_empty(): + dialog_text.add_theme_font_override(&"italics_font", load(italics_font) as Font) + if !bold_italics_font.is_empty(): + dialog_text.add_theme_font_override(&"bold_italics_font", load(bold_italics_font) as Font) - #### SOUNDS - ## TYPING SOUNDS - %DialogicNode_TypeSounds.enabled = typing_sounds_enabled - %DialogicNode_TypeSounds.mode = typing_sounds_mode +## Applies all indicator settings to the scene. +func _apply_indicator_settings() -> void: + var next_indicator: DialogicNode_NextIndicator = %NextIndicator + next_indicator.enabled = next_indicator_enabled + + if next_indicator_enabled: + next_indicator.animation = next_indicator_animation + 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 + next_indicator.texture_size = next_indicator_size + + +## Applies all sound settings to the scene. +func _apply_sounds_settings() -> void: + var type_sounds: DialogicNode_TypeSounds = %DialogicNode_TypeSounds + type_sounds.enabled = typing_sounds_enabled + type_sounds.mode = typing_sounds_mode + if not typing_sounds_sounds_folder.is_empty(): - %DialogicNode_TypeSounds.sounds = %DialogicNode_TypeSounds.load_sounds_from_path(typing_sounds_sounds_folder) + type_sounds.sounds = DialogicNode_TypeSounds.load_sounds_from_path(typing_sounds_sounds_folder) else: - %DialogicNode_TypeSounds.sounds.clear() + type_sounds.sounds.clear() + if not typing_sounds_end_sound.is_empty(): - %DialogicNode_TypeSounds.end_sound = load(typing_sounds_end_sound) + type_sounds.end_sound = load(typing_sounds_end_sound) else: - %DialogicNode_TypeSounds.end_sound = null - - %DialogicNode_TypeSounds.play_every_character = typing_sounds_every_nths_character - %DialogicNode_TypeSounds.base_pitch = typing_sounds_pitch - %DialogicNode_TypeSounds.base_volume = typing_sounds_volume - %DialogicNode_TypeSounds.pitch_variance = typing_sounds_pitch_variance - %DialogicNode_TypeSounds.volume_variance = typing_sounds_volume_variance - %DialogicNode_TypeSounds.ignore_characters = typing_sounds_ignore_characters - + type_sounds.end_sound = null + + type_sounds.play_every_character = typing_sounds_every_nths_character + type_sounds.base_pitch = typing_sounds_pitch + type_sounds.base_volume = typing_sounds_volume + type_sounds.pitch_variance = typing_sounds_pitch_variance + type_sounds.volume_variance = typing_sounds_volume_variance + type_sounds.ignore_characters = typing_sounds_ignore_characters 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 57bc23df..06569dbc 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 @@ -16,7 +16,7 @@ length = 0.001 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:position") +tracks/0/path = NodePath("Anchor/AnimationParent:position") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { @@ -28,7 +28,7 @@ tracks/0/keys = { tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true -tracks/1/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:rotation") +tracks/1/path = NodePath("Anchor/AnimationParent:rotation") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { @@ -40,7 +40,7 @@ tracks/1/keys = { tracks/2/type = "value" tracks/2/imported = false tracks/2/enabled = true -tracks/2/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:scale") +tracks/2/path = NodePath("Anchor/AnimationParent:scale") tracks/2/interp = 1 tracks/2/loop_wrap = true tracks/2/keys = { @@ -52,7 +52,7 @@ tracks/2/keys = { tracks/3/type = "value" tracks/3/imported = false tracks/3/enabled = true -tracks/3/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:modulate") +tracks/3/path = NodePath("Anchor/AnimationParent:modulate") tracks/3/interp = 1 tracks/3/loop_wrap = true tracks/3/keys = { @@ -64,7 +64,7 @@ tracks/3/keys = { tracks/4/type = "bezier" tracks/4/imported = false tracks/4/enabled = true -tracks/4/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel:rotation") +tracks/4/path = NodePath("Anchor/AnimationParent/Sizer/DialogTextPanel:rotation") tracks/4/interp = 1 tracks/4/loop_wrap = true tracks/4/keys = { @@ -79,7 +79,7 @@ length = 0.4 tracks/0/type = "bezier" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel:rotation") +tracks/0/path = NodePath("Anchor/AnimationParent/Sizer/DialogTextPanel:rotation") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { @@ -94,7 +94,7 @@ length = 0.7 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:position") +tracks/0/path = NodePath("Anchor/AnimationParent:position") tracks/0/interp = 2 tracks/0/loop_wrap = true tracks/0/keys = { @@ -106,7 +106,7 @@ tracks/0/keys = { tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true -tracks/1/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:modulate") +tracks/1/path = NodePath("Anchor/AnimationParent:modulate") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { @@ -118,7 +118,7 @@ tracks/1/keys = { tracks/2/type = "value" tracks/2/imported = false tracks/2/enabled = true -tracks/2/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:rotation") +tracks/2/path = NodePath("Anchor/AnimationParent:rotation") tracks/2/interp = 1 tracks/2/loop_wrap = true tracks/2/keys = { @@ -130,7 +130,7 @@ tracks/2/keys = { tracks/3/type = "value" tracks/3/imported = false tracks/3/enabled = true -tracks/3/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:scale") +tracks/3/path = NodePath("Anchor/AnimationParent:scale") tracks/3/interp = 1 tracks/3/loop_wrap = true tracks/3/keys = { @@ -146,7 +146,7 @@ length = 0.3 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true -tracks/0/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:position") +tracks/0/path = NodePath("Anchor/AnimationParent:position") tracks/0/interp = 2 tracks/0/loop_wrap = true tracks/0/keys = { @@ -158,7 +158,7 @@ tracks/0/keys = { tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true -tracks/1/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:rotation") +tracks/1/path = NodePath("Anchor/AnimationParent:rotation") tracks/1/interp = 2 tracks/1/loop_wrap = true tracks/1/keys = { @@ -170,7 +170,7 @@ tracks/1/keys = { tracks/2/type = "value" tracks/2/imported = false tracks/2/enabled = true -tracks/2/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:scale") +tracks/2/path = NodePath("Anchor/AnimationParent:scale") tracks/2/interp = 2 tracks/2/loop_wrap = true tracks/2/keys = { @@ -182,7 +182,7 @@ tracks/2/keys = { tracks/3/type = "value" tracks/3/imported = false tracks/3/enabled = true -tracks/3/path = NodePath("DialogicTextAnchor/DialogTextAnimationParent:modulate") +tracks/3/path = NodePath("Anchor/AnimationParent:modulate") tracks/3/interp = 1 tracks/3/loop_wrap = true tracks/3/keys = { @@ -202,7 +202,7 @@ _data = { [sub_resource type="FontVariation" id="FontVariation_v8y64"] -[node name="VN_Textbox_Layer" type="Control"] +[node name="VN_TextboxLayer" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -224,7 +224,7 @@ libraries = { autoplay = "RESET" script = ExtResource("2_xy7a2") -[node name="DialogicTextAnchor" type="Control" parent="."] +[node name="Anchor" type="Control" parent="."] layout_mode = 1 anchors_preset = 7 anchor_left = 0.5 @@ -234,7 +234,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 0 -[node name="DialogTextAnimationParent" type="Control" parent="DialogicTextAnchor"] +[node name="AnimationParent" type="Control" parent="Anchor"] layout_mode = 1 anchors_preset = 7 anchor_left = 0.5 @@ -245,7 +245,7 @@ grow_horizontal = 2 grow_vertical = 0 mouse_filter = 2 -[node name="Minimizer" type="CenterContainer" parent="DialogicTextAnchor/DialogTextAnimationParent"] +[node name="Sizer" type="Control" parent="Anchor/AnimationParent"] unique_name_in_owner = true layout_mode = 1 anchors_preset = 7 @@ -253,23 +253,28 @@ anchor_left = 0.5 anchor_top = 1.0 anchor_right = 0.5 anchor_bottom = 1.0 -offset_left = -17.0 +offset_left = -150.0 offset_top = -50.0 -offset_right = 17.0 -offset_bottom = -10.0 +offset_right = 150.0 grow_horizontal = 2 grow_vertical = 0 +mouse_filter = 2 -[node name="DialogTextPanel" type="PanelContainer" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer"] +[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(400, 100) -layout_mode = 2 +custom_minimum_size = Vector2(300, 50) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 mouse_filter = 2 theme_override_styles/panel = ExtResource("3_ssa84") metadata/_edit_layout_mode = 1 -[node name="DialogicNode_DialogText" type="RichTextLabel" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel" node_paths=PackedStringArray("textbox_root")] +[node name="DialogicNode_DialogText" type="RichTextLabel" parent="Anchor/AnimationParent/Sizer/DialogTextPanel" node_paths=PackedStringArray("textbox_root")] unique_name_in_owner = true layout_mode = 2 mouse_filter = 1 @@ -284,50 +289,53 @@ visible_characters_behavior = 1 script = ExtResource("3_4634k") textbox_root = NodePath("..") -[node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel/DialogicNode_DialogText"] +[node name="DialogicNode_TypeSounds" type="AudioStreamPlayer" parent="Anchor/AnimationParent/Sizer/DialogTextPanel/DialogicNode_DialogText"] unique_name_in_owner = true script = ExtResource("4_ma5mw") play_every_character = 0 -[node name="NextIndicator" type="Control" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel"] +[node name="NextIndicator" type="Control" parent="Anchor/AnimationParent/Sizer/DialogTextPanel"] 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") metadata/_edit_layout_mode = 1 -[node name="AutoAdvanceProgressbar" type="ProgressBar" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel"] +[node name="AutoAdvanceProgressbar" type="ProgressBar" parent="Anchor/AnimationParent/Sizer/DialogTextPanel"] unique_name_in_owner = true 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 show_percentage = false script = ExtResource("6_07xym") -[node name="NameLabelHolder" type="Control" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel"] +[node name="NameLabelHolder" type="Control" parent="Anchor/AnimationParent/Sizer/DialogTextPanel"] layout_mode = 2 mouse_filter = 2 -[node name="NameLabelPanel" type="PanelContainer" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel/NameLabelHolder"] +[node name="NameLabelPanel" type="PanelContainer" parent="Anchor/AnimationParent/Sizer/DialogTextPanel/NameLabelHolder"] unique_name_in_owner = true self_modulate = Color(0.00784314, 0.00784314, 0.00784314, 0.843137) 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 metadata/_edit_group_ = true -[node name="DialogicNode_NameLabel" type="Label" parent="DialogicTextAnchor/DialogTextAnimationParent/Minimizer/DialogTextPanel/NameLabelHolder/NameLabelPanel" node_paths=PackedStringArray("name_label_root")] +[node name="DialogicNode_NameLabel" type="Label" parent="Anchor/AnimationParent/Sizer/DialogTextPanel/NameLabelHolder/NameLabelPanel" node_paths=PackedStringArray("name_label_root")] unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(1, 1, 1, 1) 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 7413de11..d9200d67 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 d24237b5..0e673836 100644 --- a/addons/dialogic/Modules/End/event_end.gd +++ b/addons/dialogic/Modules/End/event_end.gd @@ -5,18 +5,16 @@ extends DialogicEvent ## Event that ends a timeline (even if more events come after). -################################################################################ -## EXECUTE +#region EXECUTE ################################################################################ func _execute() -> void: - for character in dialogic.Portraits.get_joined_characters(): - dialogic.Portraits.remove_character(character) dialogic.end_timeline() +#endregion -################################################################################ -## INITIALIZE + +#region INITIALIZE ################################################################################ func _init() -> void: @@ -25,17 +23,22 @@ func _init() -> void: event_category = "Flow" event_sorting_index = 10 +#endregion -################################################################################ -## SAVING/LOADING + +#region SAVING/LOADING ################################################################################ func get_shortcode() -> String: return "end_timeline" -################################################################################ -## EDITOR REPRESENTATION +#endregion + + +#region EDITOR REPRESENTATION ################################################################################ func build_event_editor(): add_header_label('End Timeline') + +#endregion diff --git a/addons/dialogic/Modules/Glossary/glossary_editor.gd b/addons/dialogic/Modules/Glossary/glossary_editor.gd index b6ea3464..2e8014ef 100644 --- a/addons/dialogic/Modules/Glossary/glossary_editor.gd +++ b/addons/dialogic/Modules/Glossary/glossary_editor.gd @@ -1,8 +1,9 @@ @tool extends DialogicEditor -var current_glossary :DialogicGlossary = null +var current_glossary: DialogicGlossary = null var current_entry_name := "" +var current_entry := {} ################################################################################ ## BASICS @@ -13,7 +14,9 @@ func _get_title() -> String: func _get_icon() -> Texture: - return load(self.get_script().get_path().get_base_dir() + "/icon.svg") + var base_directory: String = self.get_script().get_path().get_base_dir() + var icon_path := base_directory + "/icon.svg" + return load(icon_path) func _register() -> void: @@ -22,11 +25,16 @@ func _register() -> void: func _ready() -> void: - %AddGlossaryFile.icon = load(self.get_script().get_path().get_base_dir() + "/add-glossary.svg") + var add_glossary_icon_path: String = self.get_script().get_path().get_base_dir() + "/add-glossary.svg" + var add_glossary_icon := load(add_glossary_icon_path) + %AddGlossaryFile.icon = add_glossary_icon + %LoadGlossaryFile.icon = get_theme_icon('Folder', 'EditorIcons') %DeleteGlossaryFile.icon = get_theme_icon('Remove', 'EditorIcons') %DeleteGlossaryEntry.icon = get_theme_icon('Remove', 'EditorIcons') + %DeleteGlossaryFile.pressed.connect(_on_delete_glossary_file_pressed) + %AddGlossaryEntry.icon = get_theme_icon('Add', 'EditorIcons') %EntrySearch.right_icon = get_theme_icon('Search', 'EditorIcons') @@ -38,19 +46,23 @@ func _ready() -> void: %EntryCaseSensitive.icon = get_theme_icon("MatchCase", "EditorIcons") + %EntryAlternatives.text_changed.connect(_on_entry_alternatives_text_changed) -func set_setting(value, setting:String) -> void: + +func set_setting(value: Variant, setting: String) -> void: ProjectSettings.set_setting(setting, value) ProjectSettings.save() -func _open(argument:Variant = null) -> void: + +func _open(_argument: Variant = null) -> void: %DefaultColor.color = ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE) %DefaultCaseSensitive.button_pressed = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true) %GlossaryList.clear() var idx := 0 - for file in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): - if FileAccess.file_exists(file): + for file: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): + + 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')) @@ -70,17 +82,43 @@ func _open(argument:Variant = null) -> void: ################################################################################ ## GLOSSARY LIST ################################################################################ -func _on_GlossaryList_item_selected(idx:int) -> void: +func _on_GlossaryList_item_selected(idx: int) -> void: %EntryList.clear() - if FileAccess.file_exists(%GlossaryList.get_item_tooltip(idx)): - current_glossary = load(%GlossaryList.get_item_tooltip(idx)) + var tooltip_item: String = %GlossaryList.get_item_tooltip(idx) + + if ResourceLoader.exists(tooltip_item): + var glossary_item := load(tooltip_item) + + if not glossary_item is DialogicGlossary: + return + + current_glossary = load(tooltip_item) + if not current_glossary is DialogicGlossary: return + var entry_idx := 0 - for entry in current_glossary.entries: - %EntryList.add_item(entry, get_theme_icon("Breakpoint", "EditorIcons")) + + for entry_key: String in current_glossary.entries.keys(): + var entry: Variant = current_glossary.entries.get(entry_key) + + if entry is String: + continue + + # Older glossary entries may not have the name property and the + # alternatives may not be set up as alias entries. + if not entry.has(DialogicGlossary.NAME_PROPERTY): + entry[DialogicGlossary.NAME_PROPERTY] = entry_key + var alternatives_array: Array = entry.get(DialogicGlossary.ALTERNATIVE_PROPERTY, []) + var alternatives := ",".join(alternatives_array) + _on_entry_alternatives_text_changed(alternatives) + ResourceSaver.save(current_glossary) + + %EntryList.add_item(entry.get(DialogicGlossary.NAME_PROPERTY, str(DialogicGlossary.NAME_PROPERTY)), get_theme_icon("Breakpoint", "EditorIcons")) + var modulate_color: Color = entry.get('color', %DefaultColor.color) %EntryList.set_item_metadata(entry_idx, entry) - %EntryList.set_item_icon_modulate(entry_idx, current_glossary.entries[entry].get('color', %DefaultColor.color)) + %EntryList.set_item_icon_modulate(entry_idx, modulate_color) + entry_idx += 1 if %EntryList.item_count != 0: @@ -89,51 +127,69 @@ func _on_GlossaryList_item_selected(idx:int) -> void: else: hide_entry_editor() + func _on_add_glossary_file_pressed() -> void: find_parent('EditorView').godot_file_dialog(create_new_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_SAVE_FILE, 'Create new glossary resource') + func create_new_glossary_file(path:String) -> void: var glossary := DialogicGlossary.new() glossary.resource_path = path ResourceSaver.save(glossary, path) load_glossary_file(path) + func _on_load_glossary_file_pressed() -> void: find_parent('EditorView').godot_file_dialog(load_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_OPEN_FILE, 'Select glossary resource') + func load_glossary_file(path:String) -> void: var list :Array= ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + if not path in list: list.append(path) ProjectSettings.set_setting('dialogic/glossary/glossary_files', list) ProjectSettings.save() %GlossaryList.add_item(DialogicUtil.pretty_name(path), get_theme_icon('FileList', 'EditorIcons')) - %GlossaryList.set_item_tooltip(%GlossaryList.item_count-1, path) - %GlossaryList.select(%GlossaryList.item_count-1) - _on_GlossaryList_item_selected(%GlossaryList.item_count-1) + + var selected_item_index: int = %GlossaryList.item_count - 1 + + %GlossaryList.set_item_tooltip(selected_item_index, path) + %GlossaryList.select(selected_item_index) + _on_GlossaryList_item_selected(selected_item_index) + func _on_delete_glossary_file_pressed() -> void: - if len(%GlossaryList.get_selected_items()) != 0: - var list :Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) - list.erase(%GlossaryList.get_item_tooltip( - %GlossaryList.get_selected_items()[0])) + var selected_items: PackedInt32Array = %GlossaryList.get_selected_items() + + if not selected_items.is_empty(): + var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', []) + var selected_item_index := selected_items[0] + list.remove_at(selected_item_index) + ProjectSettings.set_setting('dialogic/glossary/glossary_files', list) ProjectSettings.save() + _open() + ################################################################################ ## ENTRY LIST ################################################################################ -func _on_EntryList_item_selected(idx:int) -> void: - current_entry_name = %EntryList.get_item_metadata(idx) - var entry_info: Dictionary = current_glossary.entries[current_entry_name] +func _on_EntryList_item_selected(idx: int) -> void: + current_entry_name = %EntryList.get_item_text(idx) + + var entry_info: Dictionary = current_glossary.get_entry(current_entry_name) + current_entry = entry_info + %EntrySettings.show() %EntryName.text = current_entry_name %EntryCaseSensitive.button_pressed = entry_info.get('case_sensitive', %DefaultCaseSensitive.button_pressed) - var alts := "" - for i in entry_info.get('alternatives', []): - alts += i+", " - %EntryAlternatives.text = alts + + var alternative_property: Array = entry_info.get(DialogicGlossary.ALTERNATIVE_PROPERTY, []) + var alternatives := ", ".join(alternative_property) + %EntryAlternatives.text = alternatives + %EntryTitle.text = entry_info.get('title', '') %EntryText.text = entry_info.get('text', '') %EntryExtra.text = entry_info.get('extra', '') @@ -143,108 +199,263 @@ func _on_EntryList_item_selected(idx:int) -> void: %EntryCustomColor.button_pressed = entry_info.has('color') %EntryColor.disabled = !entry_info.has('color') + _check_entry_alternatives(alternatives) + _check_entry_name(current_entry_name, current_entry) + func _on_add_glossary_entry_pressed() -> void: if !current_glossary: return - var new_name := "New Entry" - if new_name in current_glossary.entries: - var count := 2 - while new_name + " " +str(count) in current_glossary.entries: - count += 1 - new_name += " " + str(count) - current_glossary.entries[new_name] = {} + var entry_count := current_glossary.entries.size() + 1 + var new_name := "New Entry " + str(entry_count) + + if new_name in current_glossary.entries.keys(): + var random_hex_number := str(randi() % 0xFFFFFF) + new_name = new_name + " " + str(random_hex_number) + + var new_glossary := {} + new_glossary[DialogicGlossary.NAME_PROPERTY] = new_name + + if not current_glossary.try_add_entry(new_glossary): + print_rich("[color=red]Failed adding '" + new_name + "', exists already.[/color]") + return + ResourceSaver.save(current_glossary) + %EntryList.add_item(new_name, get_theme_icon("Breakpoint", "EditorIcons")) - %EntryList.set_item_metadata(%EntryList.item_count-1, new_name) - %EntryList.set_item_icon_modulate(%EntryList.item_count-1, %DefaultColor.color) - %EntryList.select(%EntryList.item_count-1) - _on_EntryList_item_selected(%EntryList.item_count-1) + var item_count: int = %EntryList.item_count - 1 + + %EntryList.set_item_metadata(item_count, new_name) + %EntryList.set_item_icon_modulate(item_count, %DefaultColor.color) + %EntryList.select(item_count) + + _on_EntryList_item_selected(item_count) + %EntryList.ensure_current_is_visible() %EntryName.grab_focus() func _on_delete_glossary_entry_pressed() -> void: - if len(%EntryList.get_selected_items()) != 0: - if current_glossary: - current_glossary.entries.erase(%EntryList.get_item_metadata( - %EntryList.get_selected_items()[0])) - %EntryList.remove_item(%EntryList.get_selected_items()[0]) + var selected_items: Array = %EntryList.get_selected_items() + + if not selected_items.is_empty(): + var selected_item_index: int = selected_items[0] + + if not current_glossary == null: + current_glossary.remove_entry(current_entry_name) + ResourceSaver.save(current_glossary) + + %EntryList.remove_item(selected_item_index) + var entries_count: int = %EntryList.item_count + + if entries_count > 0: + var previous_item_index := selected_item_index - 1 + %EntryList.select(previous_item_index) + -func _on_entry_search_text_changed(new_text:String) -> void: + +func _on_entry_search_text_changed(new_text: String) -> void: if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(%EntryList.get_selected_items()[0]).to_lower(): return - for i in %EntryList.item_count: + + for i: int in %EntryList.item_count: + if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(i).to_lower(): %EntryList.select(i) _on_EntryList_item_selected(i) %EntryList.ensure_current_is_visible() + ################################################################################ ## ENTRY EDITOR ################################################################################ func hide_entry_editor() -> void: %EntrySettings.hide() -func _on_entry_name_text_changed(new_text:String) -> void: - if current_entry_name != new_text.strip_edges(): - if new_text.strip_edges().is_empty() or new_text.strip_edges() in current_glossary.entries.keys(): - %EntryList.set_item_custom_bg_color(%EntryList.get_selected_items()[0], - get_theme_color("warning_color", "Editor").darkened(0.8)) - if new_text.strip_edges().is_empty(): - %EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges() + " (invalid empty name)") - else: - %EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges() + " (invalid duplicate name)") + +func _update_alias_entries(old_alias_value_key: String, new_alias_value_key: String) -> void: + for entry_key: String in current_glossary.entries.keys(): + + var entry_value: Variant = current_glossary.entries.get(entry_key) + + if not entry_value is String: + continue + + if not entry_value == old_alias_value_key: + continue + + current_glossary.entries[entry_key] = new_alias_value_key + + +## Checks if the [param entry_name] is already used as a key for another entry +## and returns true if it doesn't. +## The [param entry] will be used to check if found entry uses the same +## reference in memory. +func _check_entry_name(entry_name: String, entry: Dictionary) -> bool: + var selected_item: int = %EntryList.get_selected_items()[0] + var raised_error: bool = false + + var entry_assigned: Variant = current_glossary.entries.get(entry_name, {}) + + # Alternative entry uses the entry name already. + if entry_assigned is String: + raised_error = true + + if entry_assigned is Dictionary and not entry_assigned.is_empty(): + var entry_name_assigned: String = entry_assigned.get(DialogicGlossary.NAME_PROPERTY, "") + + # Another entry uses the entry name already. + if not entry_name_assigned == entry_name: + raised_error = true + + # Not the same memory reference. + if not entry == entry_assigned: + raised_error = true + + if raised_error: + %EntryList.set_item_custom_bg_color(selected_item, + get_theme_color("warning_color", "Editor").darkened(0.8)) + %EntryName.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")) + %EntryName.right_icon = get_theme_icon("StatusError", "EditorIcons") + + return false + + else: + %EntryName.add_theme_color_override("font_color", get_theme_color("font_color", "Editor")) + %EntryName.add_theme_color_override("caret_color", get_theme_color("font_color", "Editor")) + %EntryName.right_icon = null + %EntryList.set_item_custom_bg_color( + selected_item, + Color.TRANSPARENT + ) + + return true + + +func _on_entry_name_text_changed(new_name: String) -> void: + new_name = new_name.strip_edges() + + if current_entry_name != new_name: + var selected_item: int = %EntryList.get_selected_items()[0] + + if not _check_entry_name(new_name, current_entry): return - else: - %EntryList.set_item_custom_bg_color(%EntryList.get_selected_items()[0], - Color.TRANSPARENT) - var info: Dictionary = current_glossary.entries[current_entry_name] - current_glossary.entries.erase(current_entry_name) - current_glossary.entries[new_text.strip_edges()] = info - %EntryList.set_item_text(%EntryList.get_selected_items()[0], new_text.strip_edges()) - %EntryList.set_item_metadata(%EntryList.get_selected_items()[0], new_text.strip_edges()) - current_entry_name = new_text.strip_edges() - ResourceSaver.save(current_glossary) + print_rich("[color=green]Renaming entry '" + current_entry_name + "'' to '" + new_name + "'[/color]") -func _on_entry_case_sensitive_toggled(button_pressed:bool) -> void: - current_glossary.entries[current_entry_name]['case_sensitive'] = button_pressed + _update_alias_entries(current_entry_name, new_name) + + current_glossary.replace_entry_key(current_entry_name, new_name) + + %EntryList.set_item_text(selected_item, new_name) + %EntryList.set_item_metadata(selected_item, new_name) + ResourceSaver.save(current_glossary) + current_entry_name = new_name + + +func _on_entry_case_sensitive_toggled(button_pressed: bool) -> void: + current_glossary.get_entry(current_entry_name)['case_sensitive'] = button_pressed ResourceSaver.save(current_glossary) -func _on_entry_alternatives_text_changed(new_text:String) -> void: - var alts := [] - for i in new_text.split(',', false): - alts.append(i.strip_edges()) - current_glossary.entries[current_entry_name]['alternatives'] = alts + +## Checks if the [param new_alternatives] has any alternatives that are already +## used as a key for another entry and returns true if it doesn't. +func _can_change_alternative(new_alternatives: String) -> bool: + for alternative: String in new_alternatives.split(',', false): + var stripped_alternative := alternative.strip_edges() + + var value: Variant = current_glossary.entries.get(stripped_alternative, null) + + if value == null: + continue + + if value is String: + value = current_glossary.entries.get(value, null) + + var value_name: String = value[DialogicGlossary.NAME_PROPERTY] + + if not current_entry_name == value_name: + return false + + return true + + +## Checks if [entry_alternatives] has any alternatives that are already +## used by any entry and returns true if it doesn't. +## If false, it will set the alternatives text field to a warning color and +## set an icon. +## If true, the alternatives text field will be set to the default color and +## the icon will be removed. +func _check_entry_alternatives(entry_alternatives: String) -> bool: + + if not _can_change_alternative(entry_alternatives): + %EntryAlternatives.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")) + %EntryAlternatives.right_icon = get_theme_icon("StatusError", "EditorIcons") + return false + + else: + %EntryAlternatives.add_theme_color_override("font_color", get_theme_color("font_color", "Editor")) + %EntryAlternatives.right_icon = null + + return true + + +## The [param new_alternatives] is a passed as a string of comma separated +## values form the Dialogic editor. +## +## Saves the glossary resource file. +func _on_entry_alternatives_text_changed(new_alternatives: String) -> void: + var current_alternatives: Array = current_glossary.get_entry(current_entry_name).get(DialogicGlossary.ALTERNATIVE_PROPERTY, []) + + if not _check_entry_alternatives(new_alternatives): + return + + for current_alternative: String in current_alternatives: + current_glossary._remove_entry_alias(current_alternative) + + var alternatives := [] + + for new_alternative: String in new_alternatives.split(',', false): + var stripped_alternative := new_alternative.strip_edges() + alternatives.append(stripped_alternative) + current_glossary._add_entry_key_alias(current_entry_name, stripped_alternative) + + current_glossary.get_entry(current_entry_name)[DialogicGlossary.ALTERNATIVE_PROPERTY] = alternatives ResourceSaver.save(current_glossary) + func _on_entry_title_text_changed(new_text:String) -> void: - current_glossary.entries[current_entry_name]['title'] = new_text + current_glossary.get_entry(current_entry_name)['title'] = new_text ResourceSaver.save(current_glossary) + func _on_entry_text_text_changed() -> void: - current_glossary.entries[current_entry_name]['text'] = %EntryText.text + current_glossary.get_entry(current_entry_name)['text'] = %EntryText.text ResourceSaver.save(current_glossary) + func _on_entry_extra_text_changed() -> void: - current_glossary.entries[current_entry_name]['extra'] = %EntryExtra.text + current_glossary.get_entry(current_entry_name)['extra'] = %EntryExtra.text ResourceSaver.save(current_glossary) + func _on_entry_enabled_toggled(button_pressed:bool) -> void: - current_glossary.entries[current_entry_name]['enabled'] = button_pressed + current_glossary.get_entry(current_entry_name)['enabled'] = button_pressed ResourceSaver.save(current_glossary) + func _on_entry_custom_color_toggled(button_pressed:bool) -> void: %EntryColor.disabled = !button_pressed + if !button_pressed: - current_glossary.entries[current_entry_name].erase('color') + current_glossary.get_entry(current_entry_name).erase('color') %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %DefaultColor.color) else: - current_glossary.entries[current_entry_name]['color'] = %EntryColor.color + current_glossary.get_entry(current_entry_name)['color'] = %EntryColor.color %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %EntryColor.color) + func _on_entry_color_color_changed(color:Color) -> void: - current_glossary.entries[current_entry_name]['color'] = color + current_glossary.get_entry(current_entry_name)['color'] = color %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], color) ResourceSaver.save(current_glossary) diff --git a/addons/dialogic/Modules/Glossary/glossary_editor.tscn b/addons/dialogic/Modules/Glossary/glossary_editor.tscn index 9bc8b9ba..75e506b8 100644 --- a/addons/dialogic/Modules/Glossary/glossary_editor.tscn +++ b/addons/dialogic/Modules/Glossary/glossary_editor.tscn @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://addons/dialogic/Modules/Glossary/glossary_editor.gd" id="1_tf3p1"] [ext_resource type="Texture2D" uid="uid://cenut3sc5cul0" path="res://addons/dialogic/Modules/Glossary/add-glossary.svg" id="2_0elx7"] -[sub_resource type="Image" id="Image_kueg2"] +[sub_resource type="Image" id="Image_puu06"] 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_ajdpw"] -image = SubResource("Image_kueg2") +[sub_resource type="ImageTexture" id="ImageTexture_dfvxn"] +image = SubResource("Image_puu06") [node name="GlossaryEditor" type="VBoxContainer"] anchors_preset = 15 @@ -63,14 +63,14 @@ unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 tooltip_text = "Import Glossary File" -icon = SubResource("ImageTexture_ajdpw") +icon = SubResource("ImageTexture_dfvxn") [node name="DeleteGlossaryFile" type="Button" parent="Entries/Settings/Glossaries/Glossaries/HBox"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 tooltip_text = "Delete Glossary" -icon = SubResource("ImageTexture_ajdpw") +icon = SubResource("ImageTexture_dfvxn") [node name="ScrollContainer" type="ScrollContainer" parent="Entries/Settings/Glossaries/Glossaries"] layout_mode = 2 @@ -149,21 +149,21 @@ unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 tooltip_text = "New Glossary Entry" -icon = SubResource("ImageTexture_ajdpw") +icon = SubResource("ImageTexture_dfvxn") [node name="DeleteGlossaryEntry" type="Button" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 tooltip_text = "Delete Glossary Entry" -icon = SubResource("ImageTexture_ajdpw") +icon = SubResource("ImageTexture_dfvxn") [node name="EntrySearch" type="LineEdit" parent="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 placeholder_text = "Search" -right_icon = SubResource("ImageTexture_ajdpw") +right_icon = SubResource("ImageTexture_dfvxn") [node name="ScrollContainer" type="ScrollContainer" parent="Entries/HSplit/VBoxContainer/Tabs/Entries"] layout_mode = 2 @@ -220,7 +220,8 @@ unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 focus_neighbor_left = NodePath("../../../../../../VBoxContainer/Tabs/Entries/ScrollContainer/EntryList") -placeholder_text = "MyEntry" +theme_override_colors/caret_color = Color(0, 0, 0, 1) +placeholder_text = "Enter unique name..." caret_blink = true [node name="EntryCaseSensitive" type="Button" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2"] @@ -228,7 +229,7 @@ unique_name_in_owner = true layout_mode = 2 tooltip_text = "Case sensitive" toggle_mode = true -icon = SubResource("ImageTexture_ajdpw") +icon = SubResource("ImageTexture_dfvxn") flat = true [node name="Label3" type="Label" parent="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings"] @@ -305,13 +306,11 @@ size_flags_horizontal = 3 [connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/AddGlossaryFile" to="." method="_on_add_glossary_file_pressed"] [connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/LoadGlossaryFile" to="." method="_on_load_glossary_file_pressed"] -[connection signal="pressed" from="Entries/Settings/Glossaries/Glossaries/HBox/DeleteGlossaryFile" to="." method="_on_delete_glossary_file_pressed"] [connection signal="pressed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/AddGlossaryEntry" to="." method="_on_add_glossary_entry_pressed"] [connection signal="pressed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/DeleteGlossaryEntry" to="." method="_on_delete_glossary_entry_pressed"] [connection signal="text_changed" from="Entries/HSplit/VBoxContainer/Tabs/Entries/HBox/EntrySearch" to="." method="_on_entry_search_text_changed"] [connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2/EntryName" to="." method="_on_entry_name_text_changed"] [connection signal="toggled" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/HBox2/EntryCaseSensitive" to="." method="_on_entry_case_sensitive_toggled"] -[connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryAlternatives" to="." method="_on_entry_alternatives_text_changed"] [connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryTitle" to="." method="_on_entry_title_text_changed"] [connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryText" to="." method="_on_entry_text_text_changed"] [connection signal="text_changed" from="Entries/HSplit/EntryEditor/VBox/Entry Settings/EntrySettings/EntryExtra" to="." method="_on_entry_extra_text_changed"] diff --git a/addons/dialogic/Modules/Glossary/glossary_resource.gd b/addons/dialogic/Modules/Glossary/glossary_resource.gd index 34838ab5..edc1fcc5 100644 --- a/addons/dialogic/Modules/Glossary/glossary_resource.gd +++ b/addons/dialogic/Modules/Glossary/glossary_resource.gd @@ -1,12 +1,340 @@ @tool -class_name DialogicGlossary -extends Resource - ## Resource used to store glossary entries. Can be saved to disc and used as a glossary. ## Add/create glossaries fom the glossaries editor +class_name DialogicGlossary +extends Resource -## Stores all entry information -@export var entries :Dictionary = {} +## Stores all entries for the glossary. +## +## The value may either be a dictionary, representing an entry, or +## 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 = {} ## If false, no entries from this glossary will be shown -@export var enabled :bool = true +@export var enabled: bool = true + +## Refers to the translation type of this resource used for CSV translation files. +const RESOURCE_NAME := "Glossary" +## The name of glossary entries, the value is the key in [member entries]. +## This constant is used for CSV translation files. +const NAME_PROPERTY := "name" +## Property in a glossary entry. Alternative words for the entry name. +const ALTERNATIVE_PROPERTY := "alternatives" +## Property in a glossary entry. +const TITLE_PROPERTY := "title" +## Property in a glossary entry. +const TEXT_PROPERTY := "text" +## Property in a glossary entry. +const EXTRA_PROPERTY := "extra" +## Property in a glossary entry. The translation ID of the entry. +## May be empty if the entry has not been translated yet. +const TRANSLATION_PROPERTY := "_translation_id" +## Property in a glossary entry. +const REGEX_OPTION_PROPERTY := "regex_options" +## Prefix used for private properties in entries. +## Ignored when entries are translated. +const PRIVATE_PROPERTY_PREFIX := "_" + + +## Private ID assigned when this glossary is translated. +@export var _translation_id: String = "" + +## 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 = {} + + + +## Removes an entry and all its aliases (alternative property) from +## the glossary. +## [param entry_key] may be an entry name or an alias. +## +## Returns true if the entry matching the given [param entry_key] was found. +func remove_entry(entry_key: String) -> bool: + var entry: Dictionary = get_entry(entry_key) + + if entry.is_empty(): + return false + + var aliases: Array = entry.get(ALTERNATIVE_PROPERTY, []) + + for alias: String in aliases: + _remove_entry_alias(alias) + + entries.erase(entry_key) + + return true + + +## This is an internal method. +## Erases an entry alias key based the given [param entry_key]. +## +## Returns true if [param entry_key] lead to a value and the value +## was an alias. +## +## This method does not update the entry's alternative property. +func _remove_entry_alias(entry_key: String) -> bool: + var value: Variant = entries.get(entry_key, null) + + if value == null or value is Dictionary: + return false + + entries.erase(entry_key) + + return true + + +## Updates the glossary entry's name and related alias keys. +## The [param old_entry_key] is the old unique name of the entry. +## The [param new_entry_key] is the new unique name of the entry. +## +## This method fails if the [param old_entry_key] does not exist. + +## Do not use this to update alternative names. +## In order to update alternative names, delete all with +## [method _remove_entry_alias] and then add them again with +## [method _add_entry_key_alias]. +func replace_entry_key(old_entry_key: String, new_entry_key: String) -> void: + var entry := get_entry(old_entry_key) + + if entry == null: + return + + entry.name = new_entry_key + + entries.erase(old_entry_key) + entries[new_entry_key] = entry + + +## Gets the glossary entry for the given [param entry_key]. +## If there is no matching entry, an empty Dictionary will be returned. +## Valid glossary entry dictionaries will never be empty. +func get_entry(entry_key: String) -> Dictionary: + var entry: Variant = entries.get(entry_key, {}) + + # Handle alias value. + if entry is String: + entry = entries.get(entry, {}) + + return entry + + +## This is an internal method. +## The [param entry_key] must be valid entry key for an entry. +## Adds the [param alias] as a valid entry key for that entry. +## +## Returns the index of the entry, -1 if the entry does not exist. +func _add_entry_key_alias(entry_key: String, alias: String) -> bool: + var entry := get_entry(entry_key) + var alias_entry := get_entry(alias) + + if not entry.is_empty() and alias_entry.is_empty(): + entries[alias] = entry_key + return true + + return false + + +## Adds [param entry] to the glossary if it does not exist. +## If it does exist, returns false. +func try_add_entry(entry: Dictionary) -> bool: + var entry_key: String = entry[NAME_PROPERTY] + + if entries.has(entry_key): + return false + + entries[entry_key] = entry + + for alternative: String in entry.get(ALTERNATIVE_PROPERTY, []): + entries[alternative.strip_edges()] = entry_key + + return true + + +## Returns an array of words that can trigger the glossary popup. +## This method respects whether translation is enabled or not. +## The words may be: The entry key and the alternative words. +func _get_word_options(entry_key: String) -> Array: + var word_options: Array = [] + + var translation_enabled: bool = ProjectSettings.get_setting("dialogic/translation/enabled", false) + + if not translation_enabled: + word_options.append(entry_key) + + for alternative: String in get_entry(entry_key).get(ALTERNATIVE_PROPERTY, []): + word_options.append(alternative.strip_edges()) + + return word_options + + var translation_entry_key_id: String = get_property_translation_key(entry_key, NAME_PROPERTY) + + if translation_entry_key_id.is_empty(): + return [] + + var translated_entry_key := tr(translation_entry_key_id) + + if not translated_entry_key == translation_entry_key_id: + word_options.append(translated_entry_key) + + var translation_alternatives_id: String = get_property_translation_key(entry_key, ALTERNATIVE_PROPERTY) + var translated_alternatives_str := tr(translation_alternatives_id) + + if not translated_alternatives_str == translation_alternatives_id: + var translated_alternatives := translated_alternatives_str.split(",") + + for alternative: String in translated_alternatives: + word_options.append(alternative.strip_edges()) + + return word_options + + +## Gets the regex option for the given [param entry_key]. +## If the regex option does not exist, it will be generated. +## +## A regex option is the accumulation of valid words that can trigger the +## glossary popup. +## +## The [param entry_key] must be valid or an error will occur. +func get_set_regex_option(entry_key: String) -> String: + var entry: Dictionary = get_entry(entry_key) + + var regex_options: Dictionary = entry.get(REGEX_OPTION_PROPERTY, {}) + + if regex_options.is_empty(): + entry[REGEX_OPTION_PROPERTY] = regex_options + + var locale_key: String = TranslationServer.get_locale() + var regex_option: String = regex_options.get(locale_key, "") + + if not regex_option.is_empty(): + return regex_option + + var word_options: Array = _get_word_options(entry_key) + regex_option = "|".join(word_options) + + regex_options[locale_key] = regex_option + + 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() + return _translation_id + + +## Removes the translation ID of this glossary. +func remove_translation_id() -> void: + _translation_id = "" + + +## Removes the translation ID of all glossary entries. +func remove_entry_translation_ids() -> void: + for entry: Variant in entries.values(): + + # Ignore aliases. + if entry is String: + continue + + if entry.has(TRANSLATION_PROPERTY): + entry[TRANSLATION_PROPERTY] = "" + + +## Clears the lookup tables using translation keys. +func clear_translation_keys() -> void: + const RESOURCE_NAME_KEY := RESOURCE_NAME + "/" + + for translation_key: String in entries.keys(): + + if translation_key.begins_with(RESOURCE_NAME_KEY): + entries.erase(translation_key) + + _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. +## +## Time complexity: O(1) +func get_property_translation_key(entry_key: String, property: String) -> String: + var entry := get_entry(entry_key) + + if entry == null: + return "" + + var entry_translation_key: String = entry.get(TRANSLATION_PROPERTY, "") + + if entry_translation_key.is_empty() or _translation_id.is_empty(): + return "" + + var glossary_csv_key := (RESOURCE_NAME + .path_join(_translation_id) + .path_join(entry_translation_key) + .path_join(property)) + + return glossary_csv_key + + + +## Returns the translation key prefix for this glossary. +## The resulting format will look like this: Glossary/a2/ +## This prefix can be used to find translations for this glossary. +func _get_glossary_translation_id_prefix() -> String: + return ( + DialogicGlossary.RESOURCE_NAME + .path_join(_translation_id) + ) + + +## Returns the translation key for the given [param glossary_translation_id] and +## [param entry_translation_id]. +## +## By key, we refer to the uniquely named property per translation entry. +## +## The resulting format will look like this: Glossary/a2/b4/name +func _get_glossary_translation_key(entry_translation_id: String, property: String) -> String: + return ( + DialogicGlossary.RESOURCE_NAME + .path_join(_translation_id) + .path_join(entry_translation_id) + .path_join(property) + ) + + +## Tries to get the glossary entry's translation ID. +## If it does not exist, a new one will be generated. +func get_set_glossary_entry_translation_id(entry_key: String) -> String: + var glossary_entry: Dictionary = get_entry(entry_key) + var entry_translation_id := "" + + var glossary_translation_id: String = glossary_entry.get(TRANSLATION_PROPERTY, "") + + if glossary_translation_id.is_empty(): + entry_translation_id = DialogicUtil.get_next_translation_id() + glossary_entry[TRANSLATION_PROPERTY] = entry_translation_id + + else: + entry_translation_id = glossary_entry[TRANSLATION_PROPERTY] + + return entry_translation_id + + +## Tries to get the glossary's translation ID. +## If it does not exist, a new one will be generated. +func get_set_glossary_translation_id() -> String: + if _translation_id == null or _translation_id.is_empty(): + add_translation_id() + + return _translation_id + +#endregion diff --git a/addons/dialogic/Modules/Glossary/subsystem_glossary.gd b/addons/dialogic/Modules/Glossary/subsystem_glossary.gd index d205f27d..d438bc4b 100644 --- a/addons/dialogic/Modules/Glossary/subsystem_glossary.gd +++ b/addons/dialogic/Modules/Glossary/subsystem_glossary.gd @@ -10,75 +10,165 @@ 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' -#################################################################################################### -## STATE + +#region STATE #################################################################################################### -func clear_game_state(clear_flag:=DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: +func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void: glossaries = [] - for path in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): + + for path: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []): add_glossary(path) +#endregion + +#region MAIN METHODS #################################################################################################### -## MAIN METHODS -#################################################################################################### -func parse_glossary(text:String) -> String: - if !enabled: return text - var def_case_sensitive :bool = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true) - var def_color : Color= ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE) +func parse_glossary(text: String) -> String: + if not enabled: + return text + + var def_case_sensitive: bool = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true) + var def_color: Color = ProjectSettings.get_setting(SETTING_DEFAULT_COLOR, Color.POWDER_BLUE) var regex := RegEx.new() - for glossary in glossaries: + + for glossary: DialogicGlossary in glossaries: + if !glossary.enabled: continue - for entry in glossary.entries.keys(): - if !glossary.entries[entry].get('enabled', true): + + for entry_value: Variant in glossary.entries.values(): + + if not entry_value is Dictionary: + continue + + var entry: Dictionary = entry_value + var entry_key: String = entry.get(DialogicGlossary.NAME_PROPERTY, "") + + # Older versions of the glossary resource do not have a property + # for their name, we must skip these. + # They can be updated by opening the resource in the glossary + # editor. + if entry_key.is_empty(): + continue + + if not entry.get('enabled', true): continue - var pattern :String = '(?<=\\W|^)(?'+glossary.entries[entry].get('regopts', entry)+')(?!])(?=\\W|$)' - if glossary.entries[entry].get('case_sensitive', def_case_sensitive): + + var regex_options := glossary.get_set_regex_option(entry_key) + + if regex_options.is_empty(): + continue + + var pattern: String = '(?<=\\W|^)(?' + regex_options + ')(?!])(?=\\W|$)' + + if entry.get('case_sensitive', def_case_sensitive): regex.compile(pattern) + else: regex.compile('(?i)'+pattern) - var color: String = glossary.entries[entry].get('color', def_color).to_html() - if entry in color_overrides: - color = color_overrides[entry].to_html() + var color: String = entry.get('color', def_color).to_html() + + if entry_key in color_overrides: + color = color_overrides[entry_key].to_html() text = regex.sub(text, - '[url=' + entry + ']' + + '[url=' + entry_key + ']' + '[color=' + color + ']${word}[/color]' + - '[/url]', true + '[/url]', + true ) + return text func add_glossary(path:String) -> void: if ResourceLoader.exists(path): - var x = load(path) - if x is DialogicGlossary: - glossaries.append(x) - for entry in x.entries.keys(): - var regopts :String = entry - for i in x.entries[entry].get('alternatives', []): - regopts += '|'+i - x.entries[entry]['regopts'] = regopts + var resource: DialogicGlossary = load(path) + + if resource is DialogicGlossary: + glossaries.append(resource) else: printerr('[Dialogic] The glossary file "' + path + '" is missing. Make sure it exists.') -func get_entry(name:String, parse_variables:bool = true) -> Dictionary: - for glossary in glossaries: - if name in glossary.entries: - var info:Dictionary = glossary.entries[name].duplicate() - if parse_variables and dialogic.has_subsystem('VAR'): - for key in info.keys(): - if typeof(info[key]) == TYPE_STRING: - info[key] = dialogic.VAR.parse_variables(info[key]) - return info - return {} +## Iterates over all glossaries and returns the first one that matches the +## [param entry_key]. +## +## Runtime complexity: +## O(n), where n is the number of glossaries. +func find_glossary(entry_key: String) -> DialogicGlossary: + for glossary: DialogicGlossary in glossaries: + + 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, "") -func set_entry(name: String, value: Dictionary) -> bool: - return false + return tr_value diff --git a/addons/dialogic/Modules/History/definition.svg.import b/addons/dialogic/Modules/History/definition.svg.import index 80aea9b9..29dbf39f 100644 --- a/addons/dialogic/Modules/History/definition.svg.import +++ b/addons/dialogic/Modules/History/definition.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://dlwtdexd63bxi" path="res://.godot/imported/definition.svg-dbaabe55d84e4ad95047a50fc6c13843.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/History/event_history.gd b/addons/dialogic/Modules/History/event_history.gd index b0b4e931..0b0297d7 100644 --- a/addons/dialogic/Modules/History/event_history.gd +++ b/addons/dialogic/Modules/History/event_history.gd @@ -19,12 +19,12 @@ var action := Actions.PAUSE func _execute() -> void: match action: Actions.CLEAR: - dialogic.History.full_history = [] + dialogic.History.simple_history_content = [] Actions.PAUSE: - dialogic.History.full_history_enabled = false + dialogic.History.simple_history_enabled = false Actions.RESUME: - dialogic.History.full_history_enabled = true - + dialogic.History.simple_history_enabled = true + finish() @@ -49,8 +49,8 @@ func get_shortcode() -> String: func get_shortcode_parameters() -> Dictionary: return { #param_name : property_info - "action" : {"property": "action", "default": Actions.PAUSE, - "suggestions": func(): return {"Clear":{'value':0}, "Pause":{'value':1}, "Resume":{'value':2}}}, + "action" : {"property": "action", "default": Actions.PAUSE, + "suggestions": func(): return {"Clear":{'value':0, 'text_alt':['clear']}, "Pause":{'value':1, 'text_alt':['pause']}, "Resume":{'value':2, 'text_alt':['resume', 'start']}}}, } ################################################################################ @@ -58,8 +58,8 @@ func get_shortcode_parameters() -> Dictionary: ################################################################################ func build_event_editor(): - add_header_edit('action', ValueType.FIXED_OPTION_SELECTOR, { - 'selector_options': [ + add_header_edit('action', ValueType.FIXED_OPTIONS, { + 'options': [ { 'label': 'Pause History', 'value': Actions.PAUSE, diff --git a/addons/dialogic/Modules/History/icon.svg.import b/addons/dialogic/Modules/History/icon.svg.import index b7409f91..4362cc9f 100644 --- a/addons/dialogic/Modules/History/icon.svg.import +++ b/addons/dialogic/Modules/History/icon.svg.import @@ -5,6 +5,7 @@ type="CompressedTexture2D" uid="uid://1n5bqdv34pmy" path="res://.godot/imported/icon.svg-82841efe3f86e947d4f66fd24dc8f52c.ctex" metadata={ +"has_editor_variant": true, "vram_texture": false } @@ -33,5 +34,5 @@ process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/dialogic/Modules/History/settings_history.gd b/addons/dialogic/Modules/History/settings_history.gd index ba2ff8a5..75607120 100644 --- a/addons/dialogic/Modules/History/settings_history.gd +++ b/addons/dialogic/Modules/History/settings_history.gd @@ -9,15 +9,17 @@ func _get_priority() -> int: func _ready() -> void: %SimpleHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/simple_history_enabled')) %FullHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/full_history_enabled')) - %AlreadyReadHistoryEnabled.toggled.connect(setting_toggled.bind('dialogic/history/already_read_history_enabled')) + %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')) func _refresh() -> void: %SimpleHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', false) %FullHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/full_history_enabled', false) - %AlreadyReadHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/already_read_history_enabled', false) + %AlreadyReadHistoryEnabled.button_pressed = ProjectSettings.get_setting('dialogic/history/visited_event_history_enabled', false) -func setting_toggled(button_pressed:bool, setting:String) -> void: +func setting_toggled(button_pressed: bool, setting: String) -> void: ProjectSettings.set_setting(setting, button_pressed) ProjectSettings.save() diff --git a/addons/dialogic/Modules/History/settings_history.tscn b/addons/dialogic/Modules/History/settings_history.tscn index dec920e3..7bf916d7 100644 --- a/addons/dialogic/Modules/History/settings_history.tscn +++ b/addons/dialogic/Modules/History/settings_history.tscn @@ -1,6 +1,19 @@ -[gd_scene load_steps=2 format=3 uid="uid://b5yq6xh412ilm"] +[gd_scene load_steps=5 format=3 uid="uid://b5yq6xh412ilm"] [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"] +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_8li2l"] +image = SubResource("Image_3h4fk") [node name="History" type="PanelContainer"] anchors_preset = 15 @@ -28,16 +41,17 @@ layout_mode = 2 layout_mode = 2 text = "Enabled" -[node name="SimpleHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer"] -unique_name_in_owner = true +[node name="HintTooltip" parent="HistoryOptions/HBoxContainer" instance=ExtResource("2_wefye")] 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") +hint_text = "When enabled, some events (Text, Join, Leave, Choice) will store a log. +Also, the default layout will feature the log panel option." -[node name="FullHistoryInfo2" type="Label" parent="HistoryOptions"] +[node name="SimpleHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer"] +unique_name_in_owner = true layout_mode = 2 -size_flags_horizontal = 3 -theme_type_variation = &"DialogicHintText2" -text = "When enabled some events (Text, Join, Leave, Choice) will store a log that can easily be displayed. The default layout will have a history panel if this is enabled. " -autowrap_mode = 3 [node name="Title" type="Label" parent="HistoryOptions"] layout_mode = 2 @@ -51,36 +65,78 @@ layout_mode = 2 layout_mode = 2 text = "Enabled" -[node name="FullHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer5"] -unique_name_in_owner = true +[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") +hint_text = "When enabled, stores a copy of each event." -[node name="FullHistoryInfo" type="Label" parent="HistoryOptions"] +[node name="FullHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer5"] +unique_name_in_owner = true layout_mode = 2 -size_flags_horizontal = 3 -theme_type_variation = &"DialogicHintText2" -text = "When enabled a copy of each event that is played is stored. This can be used to implement some undo logic or other complitacted things. " -autowrap_mode = 3 [node name="Title2" type="Label" parent="HistoryOptions"] layout_mode = 2 theme_type_variation = &"DialogicSettingsSection" -text = "Already read History" +text = "Seen Events History" [node name="HBoxContainer4" type="HBoxContainer" parent="HistoryOptions"] layout_mode = 2 -[node name="Label" type="Label" parent="HistoryOptions/HBoxContainer4"] +[node name="EnabledLabel" type="Label" parent="HistoryOptions/HBoxContainer4"] layout_mode = 2 text = "Enabled" +[node name="HintTooltip" parent="HistoryOptions/HBoxContainer4" instance=ExtResource("2_wefye")] +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") +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. +" + [node name="AlreadyReadHistoryEnabled" type="CheckBox" parent="HistoryOptions/HBoxContainer4"] unique_name_in_owner = true layout_mode = 2 -[node name="AlreadReadInfo" type="Label" parent="HistoryOptions"] +[node name="HBoxContainerSaveOnAutoSave" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="EnabledLabel" type="Label" parent="HistoryOptions/HBoxContainerSaveOnAutoSave"] +layout_mode = 2 +text = "Save on Auto-Save signal" + +[node name="HintTooltip" parent="HistoryOptions/HBoxContainerSaveOnAutoSave" instance=ExtResource("2_wefye")] +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") +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." + +[node name="SaveOnAutoSaveToggle" type="CheckBox" parent="HistoryOptions/HBoxContainerSaveOnAutoSave"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="HBoxContainerSaveOnSave" type="HBoxContainer" parent="HistoryOptions"] +layout_mode = 2 + +[node name="EnabledLabel" type="Label" parent="HistoryOptions/HBoxContainerSaveOnSave"] +layout_mode = 2 +text = "Save on Save signal" + +[node name="HintTooltip" parent="HistoryOptions/HBoxContainerSaveOnSave" instance=ExtResource("2_wefye")] +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") +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." + +[node name="SaveOnSaveToggle" type="CheckBox" parent="HistoryOptions/HBoxContainerSaveOnSave"] +unique_name_in_owner = true layout_mode = 2 -size_flags_horizontal = 3 -theme_type_variation = &"DialogicHintText2" -text = "When enabled the signals [Dialogic.History.already_read_event_reached] and [Dialogic.History.not_read_event_reached] are emmited. This could be used to auto-skip already read text or show an indicator." -autowrap_mode = 3 diff --git a/addons/dialogic/Modules/History/subsystem_history.gd b/addons/dialogic/Modules/History/subsystem_history.gd index 0dca6410..01f910fb 100644 --- a/addons/dialogic/Modules/History/subsystem_history.gd +++ b/addons/dialogic/Modules/History/subsystem_history.gd @@ -2,6 +2,9 @@ extends DialogicSubsystem ## Subsystem that manages history storing. +signal open_requested +signal close_requested + ## Simple history that stores limited information ## Used for the history display @@ -9,41 +12,106 @@ var simple_history_enabled := true var simple_history_content : Array[Dictionary] = [] signal simple_history_changed -## Full event history (can be used for undo) +## Whether to keep a history of every Dialogic event encountered. var full_event_history_enabled := false + +## The full history of all Dialogic events encountered. +## Requires [member full_event_history_enabled] to be true. var full_event_history_content := [] + +## Emitted if a new event has been inserted into the full event history. signal full_event_history_changed ## Read text history ## Stores which text events and choices have already been visited -var already_read_history_enabled := false -var already_read_history_content := {} -var _was_last_event_already_read := false +var visited_event_history_enabled := false -signal already_read_event_reached -signal not_read_event_reached +## A history of visited Dialogic events. +var visited_event_history_content := {} -#################################################################################################### -## INITIALIZE +## Whether the last event has been encountered for the first time. +var _visited_last_event := false + +## Emitted if an encountered timeline event has been inserted into the visited +## event history. +## +## This will trigger only once per unique event instance. +signal visited_event + +## Emitted if an encountered timeline event has not been visited before. +signal unvisited_event + +## Used to store [member visited_event_history_content] in the global info file. +## You can change this to a custom name if you want to use a different key +## in the global save info file. +var visited_event_save_key := "visited_event_history_content" + +## Whether to automatically save the already-visited history on auto-save. +var save_visited_history_on_autosave := false: + set(value): + save_visited_history_on_autosave = value + _update_saved_connection(value) + + +## Whether to automatically save the already-visited history on manual save. +var save_visited_history_on_save := false: + set(value): + save_visited_history_on_save = value + _update_saved_connection(value) + + +## 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) + + else: + + if DialogicUtil.autoload().Save.saved.is_connected(_on_save): + DialogicUtil.autoload().Save.saved.disconnect(_on_save) + + +#region INITIALIZE #################################################################################################### func _ready() -> void: - dialogic.event_handled.connect(store_full_event) - dialogic.event_handled.connect(check_already_read) + var _result := dialogic.event_handled.connect(store_full_event) + _result = dialogic.event_handled.connect(_check_seen) - simple_history_enabled = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', false) - full_event_history_enabled = ProjectSettings.get_setting('dialogic/history/full_history_enabled', false) - already_read_history_enabled = ProjectSettings.get_setting('dialogic/history/already_read_history_enabled', false) + simple_history_enabled = ProjectSettings.get_setting('dialogic/history/simple_history_enabled', simple_history_enabled ) + full_event_history_enabled = ProjectSettings.get_setting('dialogic/history/full_history_enabled', full_event_history_enabled) + visited_event_history_enabled = ProjectSettings.get_setting('dialogic/history/visited_event_history_enabled', visited_event_history_enabled) -#################################################################################################### -## STATE -#################################################################################################### -# nothing implemented right now +func _on_save(info: Dictionary) -> void: + var is_autosave: bool = info["is_autosave"] -#################################################################################################### -## SIMPLE HISTORY + var save_on_autosave := save_visited_history_on_autosave and is_autosave + var save_on_save := save_visited_history_on_save and not is_autosave + + if save_on_save or save_on_autosave: + save_visited_history() + + +func post_install() -> void: + save_visited_history_on_autosave = ProjectSettings.get_setting('dialogic/history/save_on_autosave', save_visited_history_on_autosave) + save_visited_history_on_save = ProjectSettings.get_setting('dialogic/history/save_on_save', save_visited_history_on_save) + + +func open_history() -> void: + open_requested.emit() + + +func close_history() -> void: + close_requested.emit() + +#endregion + + +#region SIMPLE HISTORY #################################################################################################### func store_simple_history_entry(text:String, event_type:String, extra_info := {}) -> void: @@ -57,44 +125,54 @@ func store_simple_history_entry(text:String, event_type:String, extra_info := {} func get_simple_history() -> Array: return simple_history_content +#endregion -#################################################################################################### -## FULL EVENT HISTORY +#region FULL EVENT HISTORY #################################################################################################### -# called on each event -func store_full_event(event:DialogicEvent) -> void: +## Called on each event. +func store_full_event(event: DialogicEvent) -> void: if !full_event_history_enabled: return full_event_history_content.append(event) full_event_history_changed.emit() -#################################################################################################### -## ALREADY READ HISTORY +#region ALREADY READ HISTORY #################################################################################################### ## Takes the current timeline event and creates a unique key for it. ## Uses the timeline resource path as well. func _current_event_key() -> String: - var resource_path = dialogic.current_timeline.resource_path - var event_idx = str(dialogic.current_event_idx) - var event_key = resource_path+event_idx + var resource_path := dialogic.current_timeline.resource_path + var event_index := dialogic.current_event_idx + var event_key := _get_event_key(event_index, resource_path) return event_key -# Called if a Text event marks an unread Text event as read. -func event_was_read(_event: DialogicEvent) -> void: - if !already_read_history_enabled: +## Composes an event key from the event index and the timeline path. +## If either of these variables are in an invalid state, the resulting +## key may be wrong. +## There are no safety checks in place. +func _get_event_key(event_index: int, timeline_path: String) -> String: + var event_idx := str(event_index) + var event_key := timeline_path + event_idx + + return event_key + + +# Called if a Text event marks an unvisited Text event as visited. +func mark_event_as_visited(_event: DialogicEvent) -> void: + if !visited_event_history_enabled: return - var event_key = _current_event_key() + var event_key := _current_event_key() - already_read_history_content[event_key] = dialogic.current_event_idx + visited_event_history_content[event_key] = dialogic.current_event_idx # Called on each event, but we filter for Text events. -func check_already_read(event: DialogicEvent) -> void: - if !already_read_history_enabled: +func _check_seen(event: DialogicEvent) -> void: + if !visited_event_history_enabled: return # At this point, we only care about Text events. @@ -103,14 +181,81 @@ func check_already_read(event: DialogicEvent) -> void: if event.event_name != "Text": return - var event_key = _current_event_key() + var event_key := _current_event_key() - if event_key in already_read_history_content: - already_read_event_reached.emit() - _was_last_event_already_read = true - else: - not_read_event_reached.emit() - _was_last_event_already_read = false + if event_key in visited_event_history_content: + visited_event.emit() + _visited_last_event = true -func was_last_event_already_read() -> bool: - return _was_last_event_already_read + else: + unvisited_event.emit() + _visited_last_event = false + + +## Whether the last event has been visited for the first time or not. +## This will return `true` exactly once for each unique timeline event instance. +func has_last_event_been_visited() -> bool: + return _visited_last_event + + +## If called with with no arguments, the method will return whether +## the last encountered event was visited before. +## +## Otherwise, if [param event_index] and [param timeline] are passed, +## the method will check if the event from that given timeline has been +## visited yet. +## +## If no [param timeline] is passed, the current timeline will be used. +## If there is no current timeline, `false` will be returned. +## +## If no [param event_index] is passed, the current event index will be used. +func has_event_been_visited(event_index := dialogic.current_event_idx, timeline := dialogic.current_timeline) -> bool: + if timeline == null: + return false + + var event_key := _get_event_key(event_index, timeline.resource_path) + var visited := event_key in visited_event_history_content + + return visited + + +## Saves all seen events to the global info file. +## This can be useful when the player saves the game. +## In visual novels, callings this at the end of a route can be useful, as the +## player may not save the game. +## +## Be aware, this won't add any events but completely overwrite the already saved ones. +## +## Relies on the [subsystem Save] subsystem. +func save_visited_history() -> void: + DialogicUtil.autoload().Save.set_global_info(visited_event_save_key, visited_event_history_content) + + +## Loads the seen events from the global info save file. +## Calling this when a game gets loaded may be useful. +## +## Relies on the [subsystem Save] subsystem. +func load_visited_history() -> void: + visited_event_history_content = get_saved_visited_history() + + +## Returns the saved already-visited history from the global info save file. +## If none exist in the global info file, returns an empty dictionary. +## +## Relies on the [subsystem Save] subsystem. +func get_saved_visited_history() -> Dictionary: + return DialogicUtil.autoload().Save.get_global_info(visited_event_save_key, {}) + + +## Resets the already-visited history in the global info save file. +## If [param reset_property] is true, it will also reset the already-visited +## history in the Dialogic Autoload. +## +## Relies on the [subsystem Save] subsystem. +func reset_visited_history(reset_property := true) -> void: + DialogicUtil.autoload().Save.set_global_info(visited_event_save_key, {}) + + if reset_property: + visited_event_history_content = {} + +#endregion diff --git a/addons/dialogic/Modules/Jump/event_jump.gd b/addons/dialogic/Modules/Jump/event_jump.gd index ccc17a75..1760147b 100644 --- a/addons/dialogic/Modules/Jump/event_jump.gd +++ b/addons/dialogic/Modules/Jump/event_jump.gd @@ -8,23 +8,24 @@ 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 = null: - get: - if timeline == null: - if !_timeline_file.is_empty(): - if _timeline_file.contains("res://"): - return load(_timeline_file) - else: - return DialogicResourceUtil.get_timeline_resource(_timeline_file) - return timeline +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 = "" ### Helpers -## Path to the timeline. Mainly used by the editor. -var _timeline_file: String = "" +## Used to set the timeline resource from the unique name identifier and vice versa +var timeline_identifier: String = "": + get: + if timeline: + var identifier := DialogicResourceUtil.get_unique_identifier(timeline.resource_path) + if not identifier.is_empty(): + return identifier + return timeline_identifier + set(value): + timeline_identifier = value + timeline = DialogicResourceUtil.get_timeline_resource(value) ################################################################################ @@ -64,8 +65,8 @@ func _get_icon() -> Resource: ################################################################################ func to_text() -> String: var result := "jump " - if _timeline_file: - result += _timeline_file+'/' + if timeline_identifier: + result += timeline_identifier+'/' if label_name: result += label_name elif label_name: @@ -76,7 +77,7 @@ func to_text() -> String: func from_text(string:String) -> void: var result := RegEx.create_from_string('jump (?.*\\/)?(?