tool extends Control var timeline:String var alternativeChoicesOn = false; var investigationOn = false; var preview:bool = false var windowSize; enum state{ IDLE, READY, TYPING, WAITING, WAITING_INPUT, ANIMATING } var _state:int = state.IDLE var do_fade_in: = true var dialog_faded_in_already = false var definition_visible:bool = false var last_mouse_mode = null var current_default_theme = null var settings:ConfigFile var custom_events = {} var record_history:bool = false var definitions = {} var questions var anchors = {} var current_timeline:String = "" var dialog_script:Dictionary = {} var current_event:Dictionary var dialog_index:int = 0 var is_last_text:bool var current_background = "" var current_theme:ConfigFile var current_theme_file_name = null var history_theme:ConfigFile var audio_data = {} var button_container = null onready var ChoiceButton = load("res://addons/dialogic/Nodes/ChoiceButton.tscn") onready var Portrait = load("res://addons/dialogic/Nodes/Portrait.tscn") onready var Background = load("res://addons/dialogic/Nodes/Background.tscn") onready var HistoryTimeline = $History signal event_start(type, event) signal event_end(type) signal text_complete(text_data) signal timeline_start(timeline_name) signal timeline_end(timeline_name) signal dialogic_signal(value) func _ready(): if Dialogic.get_variable("TimelineSave") == "Timeline_0": $BackButton.visible = false windowSize = Vector2(1920, 1080); SetTooltips() for i in $BackButton.get_children(): i.connect("mouse_entered", self, "_on_BackDialog_mouse_entered") i.connect("mouse_exited", self, "_on_BackDialog_mouse_exited") Engine.get_main_loop().set_meta("latest_dialogic_node", self) load_config_files() if Dialogic.get_variable("EasyMode") == "0": $BackButton / LoadButton.visible = false $BackButton / SaveButton.visible = false $BackButton / KarmaButton.rect_position = $BackButton / SaveButton.rect_position $BackButton / KarmaButton.rect_position.x -= 5 $CustomEvents.update() if not timeline.empty(): set_current_dialog(timeline) elif dialog_script.keys().size() == 0: dialog_script = { "events":[ {"event_id":"dialogic_001", "character":"", "portrait":"", "text":"[Dialogic Error] No timeline specified."}] } else : load_dialog() get_viewport().connect("size_changed", self, "resize_main") resize_main() if not DialogicResources.get_settings_value("dialog", "stop_mouse", true): mouse_filter = Control.MOUSE_FILTER_IGNORE $OptionsDelayedInput.connect("timeout", self, "_on_OptionsDelayedInput_timeout") $DefinitionInfo.visible = false $TextBubble.connect("text_completed", self, "_on_text_completed") $TextBubble.connect("letter_written", self, "_on_letter_written") $TextBubble.connect("signal_request", self, "_on_signal_request") $TextBubble.text_label.connect("meta_hover_started", self, "_on_RichTextLabel_meta_hover_started") $TextBubble.text_label.connect("meta_hover_ended", self, "_on_RichTextLabel_meta_hover_ended") $TouchScreenButton.action = Dialogic.get_action_button() if Engine.is_editor_hint(): if preview: get_parent().connect("resized", self, "resize_main") _init_dialog() $DefinitionInfo.in_theme_editor = true else : if do_fade_in:_hide_dialog() _init_dialog() $BackButton / AutoReadButton.pressed = SettingsSingleton.GetAutoRead(); $BackButton / FastForwardButton.pressed = SettingsSingleton.GetSkipSeen(); func load_config_files(): if not Engine.is_editor_hint(): definitions = Dialogic._get_definitions() else : definitions = DialogicResources.get_default_definitions() settings = DialogicResources.get_settings_config() var theme_file = "res://addons/dialogic/Editor/ThemeEditor/default-theme.cfg" if settings.has_section("theme"): theme_file = settings.get_value("theme", "default") current_default_theme = theme_file current_theme = load_theme(theme_file) if settings.has_section("history"): record_history = settings.get_value("history", "enable_history_logging", false) if settings.has_section_key("history", "history_theme"): theme_file = settings.get_value("history", "history_theme") history_theme = load_theme(theme_file) HistoryTimeline.load_theme(history_theme) if settings.has_section_key("history", "enable_history_logging"): if settings.get_value("history", "enable_history_logging"): HistoryTimeline.initalize_history() $Menu / MenuLayer / Settings.setType(1); $Menu / MenuLayer / Settings.connect("BackFromSettings", self, "backFromSettings"); $Menu / MenuLayer / Settings.connect("ReturnToGame", self, "returnToGame"); $Menu / Blur.set_polygon(PoolVector2Array([Vector2(0, 0), Vector2(0, windowSize.y), Vector2(windowSize.x, windowSize.y), Vector2(windowSize.x, 0) ])) $Karma / KarmaLayer / KarmaScene.connect("ReturnToGameFromKarma", self, "returnToGameFromKarma"); $Karma / Blur.set_polygon(PoolVector2Array([Vector2(0, 0), Vector2(0, windowSize.y), Vector2(windowSize.x, windowSize.y), Vector2(windowSize.x, 0) ])) var block_dialog_for_back_button = false func _on_toggle_back(): var previous_event = dialog_script["events"][dialog_index - 1] if previous_event["event_id"] == "dialogic_001": _load_previous_event() else : var labelString = Dialogic.get_variable("CurrentLabel") var labelInt = int(labelString.substr(1)) if (labelInt > 1): labelInt -= 1 Dialogic.set_variable("BackPressed", 1) labelString = "a" + str(labelInt) Dialogic.set_variable("CurrentLabel", labelString) if (_state == state.TYPING): $TextBubble.skip() Dialogic.change_timeline(Dialogic.get_variable("TimelineSave")) func _on_BackDialog_mouse_entered(): block_dialog_for_back_button = true; func _on_BackDialog_mouse_exited(): block_dialog_for_back_button = false; func update_custom_events()->void : custom_events = {} var path:String = DialogicResources.get_working_directories()["CUSTOM_EVENTS_DIR"] var dir = Directory.new() if dir.open(path) == OK: dir.list_dir_begin() var file_name = dir.get_next() while file_name != "": if dir.current_is_dir() and not file_name in [".", ".."]: var event = load(path.plus_file(file_name).plus_file("EventBlock.tscn")).instance() if event: custom_events[event.event_data["event_id"]] = { "event_script":path.plus_file(file_name).plus_file("event_" + event.event_data["event_id"] + ".gd"), "event_name":event.event_name, } event.queue_free() else : print("[D] An error occurred when trying to access a custom event.") else : pass file_name = dir.get_next() else : print("[D] An error occurred when trying to access the custom event folder.") func resize_main(): var reference = rect_size if not Engine.is_editor_hint(): set_global_position(Vector2(0, 0)) if get_viewport() != null: var referenceVisibleRect = get_viewport().get_visible_rect(); if referenceVisibleRect != null: reference = referenceVisibleRect.size var anchor = current_theme.get_value("box", "anchor", 9) var margin_bottom = current_theme.get_value("box", "box_margin_bottom", current_theme.get_value("box", "box_margin_v", 40) * - 1) var margin_top = current_theme.get_value("box", "box_margin_top", current_theme.get_value("box", "box_margin_v", 40)) var margin_left = current_theme.get_value("box", "box_margin_left", current_theme.get_value("box", "box_margin_h", 40)) var margin_right = current_theme.get_value("box", "box_margin_right", current_theme.get_value("box", "box_margin_h", 40) * - 1) if anchor in [0, 1, 2]: $TextBubble.rect_position.y = margin_top elif anchor in [4, 5, 6]: $TextBubble.rect_position.y = (reference.y / 2) - ($TextBubble.rect_size.y / 2) else : $TextBubble.rect_position.y = (reference.y) - ($TextBubble.rect_size.y) + margin_bottom if anchor in [0, 4, 8]: $TextBubble.rect_position.x = margin_left elif anchor in [1, 5, 9]: $TextBubble.rect_position.x = (reference.x / 2) - ($TextBubble.rect_size.x / 2) else : $TextBubble.rect_position.x = reference.x - ($TextBubble.rect_size.x) + margin_right $TextBubble.set_text_label_size(); var pos_x = 0 if current_theme.get_value("background", "full_width", false): if preview: pos_x = get_parent().rect_global_position.x $TextBubble / TextureRect.rect_global_position.x = pos_x $TextBubble / ColorRect.rect_global_position.x = pos_x $TextBubble / TextureRect.rect_size.x = reference.x $TextBubble / ColorRect.rect_size.x = reference.x else : $TextBubble / TextureRect.rect_global_position.x = $TextBubble.rect_global_position.x $TextBubble / ColorRect.rect_global_position.x = $TextBubble.rect_global_position.x $TextBubble / TextureRect.rect_size.x = $TextBubble.rect_size.x $TextBubble / ColorRect.rect_size.x = $TextBubble.rect_size.x var button_anchor = current_theme.get_value("buttons", "anchor", 5) var anchor_vertical = 1 var anchor_horizontal = 1 if button_anchor == 0: anchor_vertical = 0 anchor_horizontal = 0 elif button_anchor == 1: anchor_vertical = 0 elif button_anchor == 2: anchor_vertical = 0 anchor_horizontal = 2 elif button_anchor == 4: anchor_horizontal = 0 elif button_anchor == 6: anchor_horizontal = 2 elif button_anchor == 8: anchor_vertical = 2 anchor_horizontal = 0 elif button_anchor == 9: anchor_vertical = 2 elif button_anchor == 10: anchor_vertical = 2 anchor_horizontal = 2 var theme_choice_offset = current_theme.get_value("buttons", "offset", Vector2(0, 0)) var position_offset = Vector2(0, 0) if anchor_horizontal == 0: position_offset.x = (reference.x / 2) * - 1 elif anchor_horizontal == 1: position_offset.x = 0 elif anchor_horizontal == 2: position_offset.x = (reference.x / 2) if anchor_vertical == 0: position_offset.y -= (reference.y / 2) elif anchor_vertical == 1: position_offset.y += 0 elif anchor_vertical == 2: position_offset.y += (reference.y / 2) $Options.rect_global_position = Vector2(0, 0) + theme_choice_offset + position_offset $Options.rect_size = reference $Options.rect_global_position = Vector2($Options.rect_global_position.x, $Options.rect_global_position.y + windowSize.y * 0.15); if settings.get_value("input", "clicking_dialog_action", true): $TouchScreenButton.shape.extents = reference var background = get_node_or_null("Background") if background != null: background.rect_size = reference var portraits = get_node_or_null("Portraits") if portraits != null: portraits.rect_position.x = reference.x / 2 portraits.rect_position.y = reference.y func deferred_resize(current_size, result, anchor): if windowSize == null: windowSize = Vector2(1920, 1080); result = Vector2(windowSize.x * 0.8, windowSize.y * 0.2); $TextBubble.rect_size = result if current_size != $TextBubble.rect_size or current_theme.get_value("box", "anchor", 9) != anchor: resize_main() $TextBubble.set_text_label_size(); func load_theme(filename): var current_theme_anchor = - 1 if current_theme: current_theme_anchor = current_theme.get_value("box", "anchor", 9) var load_theme = DialogicResources.get_theme_config(filename) if not load_theme: return current_theme var theme = load_theme current_theme_file_name = filename call_deferred("deferred_resize", $TextBubble.rect_size, theme.get_value("box", "size", Vector2(910, 167)), current_theme_anchor) $TextBubble.load_theme(theme) HistoryTimeline.change_theme(theme) $DefinitionInfo.load_theme(theme) if theme.get_value("buttons", "layout", 0) == 0: button_container = VBoxContainer.new() else : button_container = HBoxContainer.new() button_container.name = "ButtonContainer" button_container.alignment = 1 for n in $Options.get_children(): n.queue_free() $Options.add_child(button_container) load_audio(theme) if theme.get_value("box", "portraits_behind_dialog_box", true): move_child($Portraits, 0) else : move_child($Portraits, 1) return theme func load_audio(theme): var default_audio_file = "res://addons/dialogic/Example Assets/Sound Effects/Beep.wav" var default_audio_data = { "enable":false, "path":default_audio_file, "volume":0.0, "volume_rand_range":0.0, "pitch":1.0, "pitch_rand_range":0.0, "allow_interrupt":true, "audio_bus":AudioServer.get_bus_name(0) } for audio_node in $FX / Audio.get_children(): var name = audio_node.name.to_lower() audio_data[name] = theme.get_value("audio", name, default_audio_data) var file_system = Directory.new() if file_system.dir_exists(audio_data[name].path): audio_node.load_samples_from_folder(audio_data[name].path) elif file_system.file_exists(audio_data[name].path) or file_system.file_exists(audio_data[name].path + ".import"): audio_node.samples = [load(audio_data[name].path)] audio_node.set_volume_db(audio_data[name].volume) audio_node.random_volume_range = audio_data[name].volume_rand_range audio_node.set_pitch_scale(audio_data[name].pitch) audio_node.random_pitch_range = audio_data[name].pitch_rand_range audio_node.set_bus(audio_data[name].audio_bus) func play_audio(name): var node = $FX / Audio.get_node(name.capitalize()) name = name.to_lower() if audio_data[name].enable: if audio_data[name].allow_interrupt or not node.is_playing(): node.play() func set_current_dialog(dialog_path:String): current_timeline = dialog_path dialog_script = DialogicResources.get_timeline_json(dialog_path) return load_dialog() func load_dialog(): if settings.get_value("dialog", "auto_color_names", true): dialog_script = DialogicParser.parse_characters(dialog_script) dialog_script = DialogicParser.parse_text_lines(dialog_script, preview) dialog_script = DialogicParser.parse_branches(self, dialog_script) DialogicParser.parse_anchors(self) return dialog_script func _process(delta): if isSpriteChoice: $TextBubble.visible = false; $TextBubble / NextIndicatorContainer / NextIndicator.visible = is_state(state.READY) $Options.visible = is_state(state.WAITING_INPUT) if current_event.has("text"): if "[nw]" in current_event["text"] or "[nw=" in current_event["text"] or SettingsSingleton.GetAutoRead(): $TextBubble / NextIndicatorContainer / NextIndicator.visible = false if current_theme and current_theme.get_value("settings", "dont_close_after_last_event", false) and is_last_text: $TextBubble / NextIndicatorContainer / NextIndicator.visible = false if is_state(state.ANIMATING): $TextBubble / NextIndicatorContainer / NextIndicator.visible = false var preventInput = [KEY_ENTER, KEY_X, JOY_BUTTON_0]; func _input(event:InputEvent)->void : var debug = event.is_action_pressed(Dialogic.get_action_button()) if event is InputEventKey and event.scancode in preventInput: return ; if event is InputEventKey and event.pressed == false and event.scancode == KEY_M: _on_MapButton_button_down(); return ; if event is InputEventKey and event.pressed == false and event.scancode == KEY_F5: if not $LoadSaveMenu.visible: _on_SaveButton_button_down(); else : BackFromSaveLoad(); return ; if event is InputEventKey and event.pressed == false and event.scancode == KEY_F6: if not $LoadSaveMenu.visible: _on_LoadButton_button_down(); else : BackFromSaveLoad(); return ; if not Engine.is_editor_hint() and debug: if isSpriteChoice: return ; if showMenu: return ; if catOnHover: return ; if HistoryTimeline.block_dialog_advance: return if block_dialog_for_back_button: return ; if is_state(state.WAITING): if not current_event: return var timer = current_event.get("waiting_timer_skippable") if timer: timer.time_left = 0 else : if is_state(state.TYPING): $TextBubble.skip() if current_event.has("seen"): if current_event["seen"] and SettingsSingleton.GetSkipSeen(): $FX / CharacterVoice.stop_voice() else : if current_event.has("options") and not is_state(state.WAITING_INPUT): pass elif is_state(state.WAITING_INPUT) or is_state(state.ANIMATING): pass elif $TextBubble / NextIndicatorContainer / NextIndicator.is_visible(): $FX / CharacterVoice.stop_voice() play_audio("passing") _load_next_event() else : next_event(false) if settings.has_section_key("dialog", "propagate_input"): var propagate_input:bool = settings.get_value("dialog", "propagate_input") if not propagate_input and not is_state(state.WAITING_INPUT): get_tree().set_input_as_handled() func next_event(discreetly:bool): $FX / CharacterVoice.stop_voice() if not discreetly: play_audio("passing") _load_next_event() func _on_text_completed(): emit_signal("text_complete", current_event) play_audio("waiting") if current_event.has("options"): set_state(state.WAITING_INPUT) var waiting_until_options_enabled = float(settings.get_value("input", "delay_after_options", 0.1)) $OptionsDelayedInput.start(waiting_until_options_enabled) var size = current_event["options"].size(); if current_event["options"][size - 1]["label"] != tr("choice3000.6"): current_event["options"].shuffle(); var index = 0; for o in current_event["options"]: if _should_add_choice_button(o): add_choice_button(o, index) index += 1; if SettingsSingleton.GetTwitchEnabled(): if not (current_event["options"].size() == 1 and current_event["options"][0]["label"] == tr("choiceEND")): $Poll / SizeTimer.connect("timeout", self, "forTwitchResize") $Poll / Timer.connect("timeout", self, "TwitchPollTimeout") $Poll.Init(button_container.get_child_count()) else : for i in button_container.get_children(): i.disabled = false; if current_event.has("text"): set_state(state.READY) SeenTextSingleton.SerializeSeen() var seenThis = false; if current_event.has("seen"): seenThis = current_event["seen"] and SettingsSingleton.GetSkipSeen(); if "[nw]" in current_event["text"] or "[nw=" in current_event["text"] or SettingsSingleton.GetAutoRead() or seenThis: var waiting_time = 2 if seenThis: waiting_time = 0.1 var current_index = dialog_index if "[nw=" in current_event["text"]: var regex = RegEx.new() regex.compile("\\[nw=(.+?)\\](.*?)") var result = regex.search(current_event["text"]) var wait_settings = result.get_string() waiting_time = wait_settings.split("=")[1] if (waiting_time.begins_with("v")): waiting_time = $"FX/CharacterVoice".remaining_time() else : waiting_time = float(waiting_time) elif SettingsSingleton.GetAutoRead(): waiting_time = 0.0 if current_event.has("voice_data"): waiting_time = $"FX/CharacterVoice".remaining_time() else : waiting_time = float(waiting_time) $DialogicTimer.start(waiting_time);yield ($DialogicTimer, "timeout") while (showMenu): $DialogicTimer.start(0.5);yield ($DialogicTimer, "timeout") if dialog_index == current_index: _load_next_event() func _on_signal_request(name): emit_signal("dialogic_signal", name) func on_timeline_start(): if not Engine.is_editor_hint(): if settings.get_value("saving", "autosave", true): Dialogic.save("", true) emit_signal("event_start", "timeline", current_timeline) emit_signal("timeline_start", current_timeline) func on_timeline_end(): if not Engine.is_editor_hint(): if settings.get_value("saving", "autosave", true): Dialogic.save("", true) emit_signal("event_end", "timeline") emit_signal("timeline_end", current_timeline) func _emit_timeline_signals(): if dialog_script.has("events"): if dialog_index == 0: on_timeline_start() elif _is_dialog_finished(): on_timeline_end() func _init_dialog(): dialog_index = 0 var loading = int(Dialogic.get_variable("NeedCharacter")) if loading == 0: _load_event() else : Dialogic.set_variable("NeedCharacter", 0) for i in range(1, 6): var posSave = "Position" + str(i) var emoteSave = "Emote" + str(i) var mirrorSave = "Mirror" + str(i) if Dialogic.get_variable(posSave) != "None": var character_data = DialogicUtil.get_character(Dialogic.get_variable(posSave)) var p = Portrait.instance() p.character_data = character_data var char_portrait = Dialogic.get_variable(emoteSave) p.init(char_portrait) if (int(Dialogic.get_variable("Mirror" + str(i)))) == 1: p.set_mirror(true) $Portraits.add_child(p) if i == 1: p.current_state["position"] = [true, false, false, false, false] p.move_to_position("left") elif i == 2: p.current_state["position"] = [false, true, false, false, false] p.move_to_position("center_left") elif i == 3: p.current_state["position"] = [false, false, true, false, false] p.move_to_position("center") elif i == 4: p.move_to_position("center_right") p.current_state["position"] = [false, false, false, true, false] elif i == 5: p.current_state["position"] = [false, false, false, false, true] p.move_to_position("right") p.current_state["character"] = Dialogic.get_variable(posSave) _load_event_at_index(int(Dialogic.get_variable("DialogIndex"))) func _load_event_at_index(index:int): dialog_index = index _load_event() func _load_next_event(): dialog_index += 1 _load_event() func _load_previous_event(): dialog_index -= 1 _load_event() func _is_dialog_finished(): return dialog_index >= dialog_script["events"].size() func _load_event(): if dialog_script.size() != 0: if dialog_index + 1 >= dialog_script["events"].size(): is_last_text = true else : var next_event = dialog_script["events"][dialog_index + 1] if next_event["event_id"] == "dialogic_001": is_last_text = false elif "end_branch_of" in next_event: is_last_text = dialog_index + 2 >= dialog_script["events"].size() elif "choice" in next_event and not "options" in dialog_script["events"][dialog_index]: var index_in_questions = next_event["question_idx"] var question = questions[index_in_questions] var index_in_events = dialog_script["events"].rfind(question, dialog_index) var end_index = question["end_idx"] is_last_text = end_index + 1 >= dialog_script["events"].size() _emit_timeline_signals() _hide_definition_popup() if dialog_script.has("events"): if not _is_dialog_finished(): var func_state = event_handler(dialog_script["events"][dialog_index]) elif not Engine.is_editor_hint(): if not current_theme.get_value("settings", "dont_close_after_last_event", false): queue_free() func event_handler(event:Dictionary): $TextBubble.reset() clear_options() current_event = event if record_history: HistoryTimeline.add_history_row_event(current_event, self, $History / HistoryPopup / ScrollHistoryContainer / MarginContainer / HistoryTimeline) match event["event_id"]: "dialogic_001": if event["text"] != "": if int(Dialogic.get_variable("DoNotSave")) != 1 and int(Dialogic.get_variable("EasyMode")) != 1: Dialogic.set_variable("DialogIndex", dialog_index) Dialogic.save("AutosaveNormal") elif int(Dialogic.get_variable("DoNotSave")) != 1 and int(Dialogic.get_variable("EasyMode")) == 1: Dialogic.set_variable("DialogIndex", dialog_index) emit_signal("event_start", "text", event) if fade_in_dialog(): yield (get_node("fade_in_tween_show_time"), "tween_completed") set_state(state.TYPING) if event.has("character"): var character_data = DialogicUtil.get_character(event["character"]) grab_portrait_focus(character_data, event) if character_data.get("data", {}).get("theme", "") and current_theme_file_name != character_data.get("data", {}).get("theme", ""): current_theme = load_theme(character_data.get("data", {}).get("theme", "")) elif not character_data.get("data", {}).get("theme", "") and current_default_theme and current_theme_file_name != current_default_theme: current_theme = load_theme(current_default_theme) update_name(character_data) handle_voice(event) SeenTextSingleton.AddSeen(event["originalText"]); update_text(event["text"], event["seen"], event["originalText"]) if event["seen"] == true and SettingsSingleton.GetSkipSeen() == true: $TextBubble.skip(); _on_text_completed(); "dialogic_002": emit_signal("event_start", "action", event) set_state(state.WAITING) if event["character"] == "": _load_next_event() else : var character_data = DialogicUtil.get_character(event["character"]) if event.get("type", 0) == 0 and not portrait_exists(character_data): var p = Portrait.instance() if current_theme.get_value("settings", "single_portrait_mode", false): p.single_portrait_mode = true p.character_data = character_data p.dim_time = current_theme.get_value("animation", "dim_time", 0.5) var char_portrait = get_portrait_name(event) p.init(char_portrait) p.set_mirror(event.get("mirror_portrait", false)) $Portraits.add_child(p) p.move_to_position(get_character_position(event["position"])) if get_character_position(event["position"]) == "left": p.z_index = 0 Dialogic.set_variable("Position1", event["character"]) Dialogic.set_variable("Emote1", event["portrait"]) if event["mirror_portrait"] == true: Dialogic.set_variable("Mirror1", 1) else :Dialogic.set_variable("Mirror1", 0) elif get_character_position(event["position"]) == "center_left": p.z_index = - 1 Dialogic.set_variable("Position2", event["character"]) Dialogic.set_variable("Emote2", event["portrait"]) if event["mirror_portrait"] == true: Dialogic.set_variable("Mirror2", 1) else :Dialogic.set_variable("Mirror2", 0) elif get_character_position(event["position"]) == "center": p.z_index = - 2 Dialogic.set_variable("Position3", event["character"]) Dialogic.set_variable("Emote3", event["portrait"]) if event["mirror_portrait"] == true: Dialogic.set_variable("Mirror3", 1) else :Dialogic.set_variable("Mirror3", 0) elif get_character_position(event["position"]) == "center_right": p.z_index = - 1 Dialogic.set_variable("Position4", event["character"]) Dialogic.set_variable("Emote4", event["portrait"]) if event["mirror_portrait"] == true: Dialogic.set_variable("Mirror4", 1) else :Dialogic.set_variable("Mirror4", 0) elif get_character_position(event["position"]) == "right": p.z_index = 0 Dialogic.set_variable("Position5", event["character"]) Dialogic.set_variable("Emote5", event["portrait"]) if event["mirror_portrait"] == true: Dialogic.set_variable("Mirror5", 1) else :Dialogic.set_variable("Mirror5", 0) event = insert_animation_data(event, "join", "fade_in_up.gd") p.animate(event.get("animation", "[No Animation]"), event.get("animation_length", 1)) p.current_state["character"] = event["character"] p.current_state["position"] = event["position"] $Portraits.move_child(p, get_portrait_z_index_point(event.get("z_index", 0))) elif event.get("type", 0) == 1: if event["character"] == "[All]": var positions = ["Position1", "Position2", "Position3", "Position4", "Position5"]; var cont = false; for i in positions: if Dialogic.get_variable(i) != "None": cont = true; if cont: for i in positions: Dialogic.set_variable(i, "None") event = insert_animation_data(event, "leave", "fade_out_down.gd") characters_leave_all(event.get("animation", "[No Animation]"), event.get("animation_length", - 1)) if event.get("animation_wait", false): $DialogicTimer.start(event.get("animation_duration", 1)) yield ($DialogicTimer, "timeout") else : for p in $Portraits.get_children(): if is_instance_valid(p) and p.character_data["file"] == event["character"]: for i in range(1, 5): if Dialogic.get_variable("Position" + str(i)) == event["character"]: Dialogic.set_variable("Position" + str(i), "None") event = insert_animation_data(event, "leave", "fade_out_down.gd") p.animate(event.get("animation", "instant_out.gd"), event.get("animation_length", 1), 1, true) if event.get("animation_wait", false): yield (p, "animation_finished") else : if portrait_exists(character_data): for portrait in $Portraits.get_children(): if portrait.character_data.get("file", true) == character_data.get("file", false): var portrait_name = get_portrait_name(event) if portrait_name != portrait.current_state["portrait"]: for i in range(1, 5): if Dialogic.get_variable("Position" + str(i)) == event["character"]: Dialogic.set_variable("Emote" + str(i), event["portrait"]) portrait.set_portrait(portrait_name) if event.get("change_mirror_portrait", false): portrait.set_mirror(event.get("mirror_portrait", false)) if event.get("change_z_index", false): $Portraits.move_child(portrait, get_portrait_z_index_point(event.get("z_index", 0))) portrait.z_index = event.get("z_index", 0) set_state(state.READY) _load_next_event() "dialogic_010": if int(Dialogic.get_variable("DoNotSave")) != 1 and Dialogic.get_variable("EasyMode") != "1": Dialogic.set_variable("DialogIndex", dialog_index) Dialogic.save("AutosaveNormal") SceneManagerSingleton.TakeScreenShot("AutosaveNormal") elif int(Dialogic.get_variable("DoNotSave")) != 1 and Dialogic.get_variable("EasyMode") == "1": Dialogic.set_variable("DialogIndex", dialog_index) emit_signal("event_start", "question", event) if fade_in_dialog(): yield (get_node("fade_in_tween_show_time"), "tween_completed") set_state(state.TYPING) if event.has("name"): update_name(event["name"]) elif event.has("character"): var character_data = DialogicUtil.get_character(event["character"]) grab_portrait_focus(character_data, event) if character_data.get("data", {}).get("theme", "") and current_theme_file_name != character_data.get("data", {}).get("theme", ""): current_theme = load_theme(character_data.get("data", {}).get("theme", "")) elif not character_data.get("data", {}).get("theme", "") and current_default_theme and current_theme_file_name != current_default_theme: current_theme = load_theme(current_default_theme) update_name(character_data) handle_voice(event) update_text(event["question"], false, event["originalText"]) "dialogic_011": emit_signal("event_start", "choice", event) for q in questions: if q["question_idx"] == event["question_idx"]: if q["answered"]: _load_event_at_index(q["end_idx"]) "dialogic_012": var def_value = null var current_question = questions[event["question_idx"]] for d in definitions["variables"]: if d["id"] == event["definition"]: def_value = d["value"] var condition_met = def_value != null and DialogicUtil.compare_definitions(def_value, event["value"], event["condition"]); current_question["answered"] = not condition_met if not condition_met: _load_event_at_index(current_question["end_idx"]) else : _load_next_event() "dialogic_013": emit_signal("event_start", "endbranch", event) _load_next_event() "dialogic_014": emit_signal("event_start", "set_value", event) var operation = "=" if "operation" in event and not event["operation"].empty(): operation = event["operation"] var value = event["set_value"] if event.get("set_random", false): value = str(randi() % int(event.get("random_upper_limit", 100) - event.get("random_lower_limit", 0)) + event.get("random_lower_limit", 0)) Dialogic.set_variable_from_id(event["definition"], value, operation) KarmaChanged(event); _load_next_event() "dialogic_015": emit_signal("event_start", "anchor", event) _load_next_event() "dialogic_016": emit_signal("event_start", "goto", event) dialog_index = anchors[event.get("anchor_id")] _load_next_event() "dialogic_020": if not event["change_timeline"].empty(): change_timeline(event["change_timeline"]) "dialogic_021": emit_signal("event_start", "background", event) var fade_time = event.get("fade_duration", 1) var value = event.get("background", "") var background = get_node_or_null("Background") current_background = event["background"] if background != null: background.name = "BackgroundFadingOut" if not value: background.fade_out(fade_time) else : background.remove_with_delay(fade_time) background = null if value != "": background = Background.instance() add_child(background) if (event["background"].ends_with(".tscn")): var bg_scene = load(event["background"]) bg_scene = bg_scene.instance() background.modulate = Color(1, 1, 1, 0) background.add_child(bg_scene) background.fade_in(fade_time) else : background.texture = load(value) background.fade_in(fade_time) call_deferred("resize_main") _load_next_event() "dialogic_022": emit_signal("event_start", "close_dialog", event) set_state(state.ANIMATING) var transition_duration = event.get("transition_duration", 1.0) insert_animation_data(event, "leave", "fade_out_down") characters_leave_all(event["animation"], event["animation_length"]) var background = get_node_or_null("Background") if background != null: background.name = "BackgroundFadingOut" background.fade_out(transition_duration) if transition_duration != 0: var tween = Tween.new() add_child(tween) tween.interpolate_property($TextBubble, "modulate", $TextBubble.modulate, Color("#00ffffff"), transition_duration, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) tween.start() yield (tween, "tween_all_completed") on_timeline_end() queue_free() "dialogic_023": emit_signal("event_start", "wait", event) if event.get("hide_dialogbox", true): $TextBubble.visible = false set_state(state.WAITING) var timer = get_tree().create_timer(event["wait_seconds"]) if event.get("waiting_skippable", false): event["waiting_timer_skippable"] = timer yield (timer, "timeout") event.erase("waiting_timer_skippable") set_state(state.IDLE) $TextBubble.visible = true emit_signal("event_end", "wait") _load_next_event() "dialogic_024": emit_signal("event_start", "set_theme", event) if event["set_theme"] != "": current_theme = load_theme(event["set_theme"]) current_default_theme = event["set_theme"] resize_main() _load_next_event() "dialogic_025": emit_signal("event_start", "set_glossary", event) if event["glossary_id"]: Dialogic.set_glossary_from_id(event["glossary_id"], event["title"], event["text"], event["extra"]) _load_next_event() "dialogic_026": emit_signal("event_start", "save", event) var custom_slot:String = event.get("custom_slot", "").strip_edges() if event.get("use_default_slot", true) or custom_slot == "": var slot = Dialogic.get_variable("SaveSlotName"); Dialogic.save(slot) SceneManagerSingleton.TakeScreenShot(slot) else : if custom_slot.begins_with("[") and custom_slot.ends_with("]"): custom_slot = custom_slot.trim_prefix("[").trim_suffix("]") var saved = false for definition in definitions["variables"]: if definition["name"] == custom_slot: Dialogic.save(definition["value"]) saved = true if not saved: print("[D] Tried to access value definition '" + custom_slot + "' for saving, but it didn't exist.") else : Dialogic.save(custom_slot) _load_next_event() "dialogic_030": emit_signal("event_start", "audio", event) if event["audio"] == "play" and "file" in event.keys() and not event["file"].empty(): var audio = get_node_or_null("AudioEvent") if audio == null: audio = AudioStreamPlayer.new() audio.name = "AudioEvent" add_child(audio) if event.has("audio_bus"): if AudioServer.get_bus_index(event["audio_bus"]) >= 0: audio.bus = event["audio_bus"] if event.has("volume"): audio.volume_db = event["volume"] audio.stream = load(event["file"]) audio.play() else : var audio = get_node_or_null("AudioEvent") if audio != null: audio.stop() audio.queue_free() _load_next_event() "dialogic_031": emit_signal("event_start", "background-music", event) if event["background-music"] == "play" and "file" in event.keys() and not event["file"].empty(): $FX / BackgroundMusic.crossfade_to(event["file"], event.get("audio_bus", "Master"), event.get("volume", 0), event.get("fade_length", 1)) else : $FX / BackgroundMusic.fade_out(event.get("fade_length", 1)) _load_next_event() "dialogic_040": emit_signal("dialogic_signal", event["emit_signal"]) if event["emit_signal"] == "NormalSave": if int(Dialogic.get_variable("EasyMode")) != 1: Dialogic.set_variable("DialogIndex", dialog_index) Dialogic.save("AutosaveNormal") SceneManagerSingleton.TakeScreenShot("AutosaveNormal") elif int(Dialogic.get_variable("EasyMode")) == 1: Dialogic.set_variable("DialogIndex", dialog_index) if event["emit_signal"] == "EasySave": if int(Dialogic.get_variable("EasyMode")) == 1: Dialogic.save("AutosaveCasual") SceneManagerSingleton.TakeScreenShot("AutosaveCasual") _load_next_event() "dialogic_041": if event.has("scene"): get_tree().change_scene(event["scene"]) elif event.has("change_scene"): get_tree().change_scene(event["change_scene"]) "dialogic_042": emit_signal("event_start", "call_node", event) $TextBubble.visible = false set_state(state.WAITING) var target = get_node_or_null(event["call_node"]["target_node_path"]) if not target: target = get_tree().root.get_node_or_null(event["call_node"]["target_node_path"]) var method_name = event["call_node"]["method_name"] var args = event["call_node"]["arguments"] if ( not args is Array): args = [] var waitBackground = false; if is_instance_valid(target): if target.has_method(method_name): var func_result = target.callv(method_name, args) if (func_result is GDScriptFunctionState): yield (func_result, "completed") waitBackground = func_result; if (event["call_node"]["method_name"] == "setBackground"): if waitBackground: var game = get_tree().root.get_node("Root"); yield (game, "backgroundLoaded") if (event["call_node"]["method_name"] == "AlternativeChoices"): var game = get_tree().root.get_node("Root"); yield (game, "alt_choice_completed") $Map.DisableAllPlaces(); if (event["call_node"]["method_name"] == "Investigation"): var game = get_tree().root.get_node("Root"); yield (game, "alt_choice_completed") set_state(state.IDLE) $TextBubble.visible = true _load_next_event() _: if event["event_id"] in $CustomEvents.handlers.keys(): var handler = $CustomEvents.handlers[event["event_id"]] handler.handle_event(event, self) else : visible = false func change_timeline(timeline): dialog_script = set_current_dialog(timeline) var timelineDict = DialogicUtil.get_timeline_dict() var timelineRealName = timelineDict[timeline]["name"]; Dialogic.set_variable("TimelineSave", timelineRealName) Dialogic.set_variable("DialogIndex", 0) if int(Dialogic.get_variable("DoNotSave")) != 1 and Dialogic.get_variable("EasyMode") != "1": Dialogic.save("AutosaveNormal") SceneManagerSingleton.TakeScreenShot("AutosaveNormal") _init_dialog() func update_name(character)->void : if character.has("name"): var parsed_name = character["name"] if character["data"].get("display_name_bool", false): if character["display_name"] != "": parsed_name = character["display_name"] parsed_name = DialogicParser.parse_definitions(self, parsed_name, true, false) $TextBubble.update_name(parsed_name, character.get("color", Color.white), current_theme.get_value("name", "auto_color", true)) else : $TextBubble.update_name("") func update_text(text:String, seen:bool, text_code:String)->String: if settings.get_value("dialog", "translations", false): text = tr(text) var final_text = DialogicParser.parse_definitions(self, DialogicParser.parse_alignment(self, text)) final_text = final_text.replace("[br]", "\n") if $TextBubble.modulate.a == 0.0: $TextBubble.call_deferred("set_modulate", Color(1, 1, 1, 1)); $TextBubble.update_text(final_text, seen) return final_text func _on_letter_written(): play_audio("typing") func answer_question(i, event_idx, question_idx): if int(Dialogic.get_variable("DoNotSave")) != 1 and Dialogic.get_variable("EasyMode") == "1": Dialogic.save("AutosaveCasual") SceneManagerSingleton.TakeScreenShot("AutosaveCasual") for button in button_container.get_children(): if button.disabled == true: return ; (i as Button).disabled = true; if $Poll.inited: $Poll.Disconnect(); $Poll / Timer.disconnect("timeout", self, "TwitchPollTimeout") if $Poll / SizeTimer.is_connected("timeout", self, "forTwitchResize"): $Poll / SizeTimer.disconnect("timeout", self, "forTwitchResize") play_audio("selecting") clear_options() questions[question_idx]["answered"] = true _load_event_at_index(event_idx + 1) if record_history: HistoryTimeline.add_answer_to_question(str(i.text)) if last_mouse_mode != null: Input.set_mouse_mode(last_mouse_mode) last_mouse_mode = null func clear_options(): for option in button_container.get_children(): option.queue_free() var numberKeys = [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9]; func add_choice_button(option:Dictionary, index:int)->Button: var labelText = str("#", index + 1, " ", option["label"]); var button = get_classic_choice_button(labelText) button_container.set("custom_constants/separation", current_theme.get_value("buttons", "gap", 20)) if SettingsSingleton.GetTwitchEnabled(): button.disabled = true; button_container.add_child(button) var hotkey var buttonCount = button_container.get_child_count() var hotkeyOption = settings.get_value("input", str("choice_hotkey_", buttonCount), "") var key = numberKeys[index]; hotkey = InputEventKey.new(); hotkey.scancode = key; if hotkeyOption != "[None]" or settings.get_value("input", "enable_default_shortcut", false) == true: var shortcut = ShortCut.new() shortcut.set_shortcut(hotkey) button.set_shortcut(shortcut) button.shortcut_in_tooltip = false button.connect("focus_entered", self, "_on_option_hovered", [button]) button.connect("mouse_entered", self, "_on_option_focused") button.set_meta("event_idx", option["event_idx"]) button.set_meta("question_idx", option["question_idx"]) if Input.get_mouse_mode() != Input.MOUSE_MODE_VISIBLE: last_mouse_mode = Input.get_mouse_mode() Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) return button func _should_add_choice_button(option:Dictionary): if not option["definition"].empty(): var def_value = null for d in definitions["variables"]: if d["id"] == option["definition"]: def_value = d["value"] return def_value != null and DialogicUtil.compare_definitions(def_value, option["value"], option["condition"]); else : return true func get_custom_choice_button(label:String): var theme = current_theme var custom_path = current_theme.get_value("buttons", "custom_path", "") var CustomChoiceButton = load(custom_path) var button = CustomChoiceButton.instance() button.text = label return button func get_classic_choice_button(label:String): var theme = current_theme var button:Button = ChoiceButton.instance() var spaceText = "" for i in range(0, label.length() / 7): spaceText += " "; button.text = spaceText + label + spaceText button.set("custom_styles/focus", StyleBoxEmpty.new()) if theme.get_value("buttons", "fixed", false): var size = theme.get_value("buttons", "fixed_size", Vector2(130, 40)) button.rect_min_size = size button.rect_size = size button_container.set("custom_constants/separation", theme.get_value("buttons", "gap", 20)) var default_background = "res://addons/dialogic/Example Assets/backgrounds/background-2.png" var default_style = [ false, Color.white, false, Color.black, true, default_background, false, Color.white, ] var hover_style = [true, Color(0.698039, 0.698039, 0.698039, 1), false, Color.black, true, default_background, false, Color.white] var style_normal = theme.get_value("buttons", "normal", default_style) var style_hover = theme.get_value("buttons", "hover", hover_style) var style_pressed = theme.get_value("buttons", "pressed", default_style) var style_disabled = theme.get_value("buttons", "disabled", default_style) var default_color:Color if SettingsSingleton.GetDefaultTheme(): default_color = Color(theme.get_value("text", "color", "#ffffffff")) else : var newcolor = SettingsSingleton.GetTextColor() default_color = Color(newcolor) button.set("custom_colors/font_color", default_color) button.set("custom_colors/font_color_hover", default_color.lightened(0.2)) button.set("custom_colors/font_color_pressed", default_color.darkened(0.2)) button.set("custom_colors/font_color_disabled", default_color.darkened(0.8)) if style_normal[0]: button.set("custom_colors/font_color", style_normal[1]) if style_hover[0]: button.set("custom_colors/font_color_hover", style_hover[1]) if style_pressed[0]: button.set("custom_colors/font_color_pressed", style_pressed[1]) if style_disabled[0]: button.set("custom_colors/font_color_disabled", style_disabled[1]) button_style_setter("normal", style_normal, button, theme) button_style_setter("hover", style_hover, button, theme) button_style_setter("pressed", style_pressed, button, theme) button_style_setter("disabled", style_disabled, button, theme) windowSize = SettingsSingleton.GetCurrectScreenResolutionVector2() var font = button.get_font("font"); var fontSize = round(0.0333333 * windowSize.y - 0.333333); font.size = fontSize; button.add_font_override("font", font); return button func UpdateButtonTheme(): if current_theme == null: return ; var theme = current_theme var default_background = "res://addons/dialogic/Example Assets/backgrounds/background-2.png" var default_style = [ false, Color.white, false, Color.black, true, default_background, false, Color.white, ] var hover_style = [true, Color(0.698039, 0.698039, 0.698039, 1), false, Color.black, true, default_background, false, Color.white] var style_normal = theme.get_value("buttons", "normal", default_style) var style_hover = theme.get_value("buttons", "hover", hover_style) var style_pressed = theme.get_value("buttons", "pressed", default_style) var style_disabled = theme.get_value("buttons", "disabled", default_style) var default_color:Color if SettingsSingleton.GetDefaultTheme(): default_color = Color(theme.get_value("text", "color", "#ffffffff")) for i in $Options.get_child(0).get_children(): button_style_setter("normal", style_normal, i, theme) button_style_setter("hover", style_hover, i, theme) button_style_setter("pressed", style_pressed, i, theme) button_style_setter("disabled", style_disabled, i, theme) i.set("custom_colors/font_color", default_color) i.set("custom_colors/font_color_hover", default_color.lightened(0.2)) i.set("custom_colors/font_color_pressed", default_color.darkened(0.2)) i.set("custom_colors/font_color_disabled", default_color.darkened(0.8)) else : var newcolor = SettingsSingleton.GetTextColor() default_color = Color(newcolor) for i in $Options.get_child(0).get_children(): button_style_setter("normal", style_normal, i, theme) button_style_setter("hover", style_hover, i, theme) button_style_setter("pressed", style_pressed, i, theme) button_style_setter("disabled", style_disabled, i, theme) i.set("custom_colors/font_color", default_color) i.set("custom_colors/font_color_hover", default_color.lightened(0.2)) i.set("custom_colors/font_color_pressed", default_color.darkened(0.2)) i.set("custom_colors/font_color_disabled", default_color.darkened(0.8)) func button_style_setter(section, data, button, theme): var style_box = StyleBoxTexture.new() if data[2]: style_box.set("texture", DialogicUtil.path_fixer_load("res://addons/dialogic/Images/Plugin/white-texture.png")) style_box.set("modulate_color", data[3]) else : if data[4]: style_box.set("texture", DialogicUtil.path_fixer_load(data[5])) if SettingsSingleton.GetDefaultTheme(): style_box.set("modulate_color", Color("#ffffffff")) else : style_box.set("texture", load("res://resources/graphics/GUI/ChoiceButtonTextureWhite.webp")) style_box.set("modulate_color", SettingsSingleton.GetBackgroundColor()) if data[6]: style_box.set("modulate_color", data[7]) var padding = theme.get_value("buttons", "padding", Vector2(5, 5)) style_box.set("margin_left", padding.x) style_box.set("margin_right", padding.x) style_box.set("margin_top", padding.y) style_box.set("margin_bottom", padding.y) button.set("custom_styles/" + section, style_box) func _on_option_hovered(button): button.grab_focus() func _on_option_focused(): play_audio("hovering") func _on_OptionsDelayedInput_timeout(): for button in button_container.get_children(): if button.is_connected("pressed", self, "answer_question") == false: button.connect("pressed", self, "answer_question", [button, button.get_meta("event_idx"), button.get_meta("question_idx")]) func handle_voice(event): var settings_file = DialogicResources.get_settings_config() if not settings_file.get_value("dialog", "text_event_audio_enable", false): return if Engine.is_editor_hint(): return if event.has("voice_data"): var voice_data = event["voice_data"] if voice_data.has("0"): if current_event["event_id"] == "dialogic_010": $FX / CharacterVoice.play_voice(voice_data["0"]) elif not (current_event["seen"] and SettingsSingleton.GetSkipSeen()): $FX / CharacterVoice.play_voice(voice_data["0"]) return $FX / CharacterVoice.stop_voice() func grab_portrait_focus(character_data, event:Dictionary = {})->bool: var exists = false for portrait in $Portraits.get_children(): if portrait.character_data.get("file", "something") == character_data.get("file", "none"): exists = true portrait.focus() if event.has("portrait"): portrait.set_portrait(get_portrait_name(event)) if settings.get_value("dialog", "recenter_portrait", true): portrait.move_to_position(portrait.direction) else : portrait.focusout(Color(current_theme.get_value("animation", "dim_color", "#ff808080"))) return exists func portrait_exists(character_data)->bool: var exists = false for portrait in $Portraits.get_children(): if portrait.character_data.get("file", true) == character_data.get("file", false): exists = true return exists func get_character_position(positions)->String: if positions["0"]: return "left" if positions["1"]: return "center_left" if positions["2"]: return "center" if positions["3"]: return "center_right" if positions["4"]: return "right" return "left" func get_portrait_name(event_data): var char_portrait = event_data["portrait"] if char_portrait == "": char_portrait = "(Don't change)" if char_portrait == "[Definition]" and event_data.has("port_defn"): var portrait_definition = event_data["port_defn"] if portrait_definition != "": for d in Dialogic._get_definitions()["variables"]: if d["id"] == portrait_definition: char_portrait = d["value"] break return char_portrait func insert_animation_data(event_data, type = "join", default = "fade_in_up"): var animation = event_data.get("animation", "[Default]") var length = event_data.get("animation_length", 0.5) if animation == "[Default]": animation = DialogicResources.get_settings_value("animations", "default_" + type + "_animation", default) length = DialogicResources.get_settings_value("animations", "default_" + type + "_animation_length", 0.5) event_data["animation"] = animation event_data["animation_length"] = length return event_data func characters_leave_all(animation, time): var portraits = get_node_or_null("Portraits") if portraits != null: for p in portraits.get_children(): p.animate(animation, time, 1, true) func get_portrait_z_index_point(z_index): for i in range($Portraits.get_child_count()): if $Portraits.get_child(i).z_index >= z_index: return i return $Portraits.get_child_count() func _should_show_glossary(): if current_theme != null: return current_theme.get_value("definitions", "show_glossary", true) return true func _on_RichTextLabel_meta_hover_started(meta): var correct_type = false for d in definitions["glossary"]: if d["id"] == meta: $DefinitionInfo.load_preview({ "title":d["title"], "body":DialogicParser.parse_definitions(self, d["text"], true, false), "extra":d["extra"], }) correct_type = true if correct_type: definition_visible = true $DefinitionInfo.visible = definition_visible $DefinitionInfo / Timer.stop() func _on_RichTextLabel_meta_hover_ended(meta): $DefinitionInfo / Timer.start(0.1) func _hide_definition_popup(): definition_visible = false $DefinitionInfo.visible = definition_visible func _on_Definition_Timer_timeout(): definition_visible = false $DefinitionInfo.visible = definition_visible func _hide_dialog(): $TextBubble.clear() $TextBubble.modulate = Color(1, 1, 1, 0) dialog_faded_in_already = false func fade_in_dialog(time = 0.5): visible = true time = current_theme.get_value("animation", "show_time", 0.5) var has_tween = false if Engine.is_editor_hint() == false: if dialog_faded_in_already == false and do_fade_in: var tween = Tween.new() add_child(tween) tween.name = "fade_in_tween_show_time" $TextBubble.modulate.a = 0 tween.interpolate_property($TextBubble, "modulate", $TextBubble.modulate, Color(1, 1, 1, 1), time, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) tween.start() tween.connect("tween_completed", self, "finished_fade_in_dialog", [tween]) has_tween = true if has_tween: set_state(state.ANIMATING) dialog_faded_in_already = true return true return false func finished_fade_in_dialog(object, key, node): node.queue_free() if not current_event.has("options"): set_state(state.IDLE) dialog_faded_in_already = true func get_current_state_info(): var state = {} state["portraits"] = [] for portrait in $Portraits.get_children(): state["portraits"].append(portrait.current_state) state["portraits"][ - 1]["z_index"] = portrait.z_index state["background_music"] = $FX / BackgroundMusic.get_current_info() state["timeline"] = current_timeline state["event_idx"] = dialog_index state["background"] = current_background return state func resume_state_from_info(state_info): do_fade_in = false yield (self, "ready") for saved_portrait in state_info["portraits"]: var event = saved_portrait var character_data = DialogicUtil.get_character(event["character"]) if portrait_exists(character_data): for portrait in $Portraits.get_children(): if portrait.character_data == character_data: portrait.move_to_position(get_character_position(event["position"])) portrait.set_mirror(event.get("mirror", false)) else : var p = Portrait.instance() var char_portrait = event["portrait"] if char_portrait == "": char_portrait = "Default" if char_portrait == "[Definition]" and event.has("port_defn"): var portrait_definition = event["port_defn"] if portrait_definition != "": for d in DialogicResources.get_default_definitions()["variables"]: if d["id"] == portrait_definition: char_portrait = d["value"] break if current_theme.get_value("settings", "single_portrait_mode", false): p.single_portrait_mode = true p.dim_time = current_theme.get_value("animation", "dim_time", 0.5) p.character_data = character_data p.init(char_portrait) p.set_mirror(event.get("mirror", false)) $Portraits.add_child(p) $Portraits.move_child(p, get_portrait_z_index_point(saved_portrait.get("z_index", 0))) p.move_to_position(get_character_position(event["position"])) p.current_state["character"] = event["character"] p.current_state["position"] = event["position"] if state_info["background_music"] != null: $FX / BackgroundMusic.crossfade_to(state_info["background_music"]["file"], state_info["background_music"]["audio_bus"], state_info["background_music"]["volume"], 1) if state_info["background"]: current_background = state_info["background"] var background = Background.instance() call_deferred("resize_main") add_child(background) if (current_background.ends_with(".tscn")): var bg_scene = load(current_background) if (bg_scene): bg_scene = bg_scene.instance() background.add_child(bg_scene) elif (current_background != ""): background.texture = load(current_background) set_current_dialog(state_info["timeline"]) for event_index in range(0, state_info["event_idx"]): if dialog_script["events"][event_index]["event_id"] == "dialogic_010": dialog_script["events"][event_index]["answered"] = true _load_event_at_index(state_info["event_idx"]) func set_state(new_state): var state_string = ["IDLE", "READY", "TYPING", "WAITING", "WAITING_INPUT", "ANIMATING", ] _state = new_state return _state func is_state(check_state): if _state == check_state: return true return false var catOnHover; var showMenu; func ToggleMenu(state): if $History / HistoryPopup.visible: $History._on_toggle_history(); $BackButton.visible = true; var game = get_tree().root.get_node("Root"); game.isMenuVisible = not game.isMenuVisible; return ; showMenu = state; $Menu.visible = showMenu; if showMenu: $TextBubble / WritingTimer.stop(); $Menu / Blur.material.set_shader_param("blur_amount", 1.5); $Poll.StopTimerInMenu(); else : $TextBubble / WritingTimer.start(); $Menu / Blur.material.set_shader_param("blur_amount", 0); block_dialog_for_back_button = false; $Poll.ResumeTimerInMenu(); for i in $History.get_children(): if i is Button: i.disabled = showMenu; for i in $Options.get_child(0).get_children(): (i as Button).disabled = showMenu; $Menu / MenuLayer / Settings.RemoveHover(); $Menu / MenuLayer / Settings.LoadTextSettings(); func ToggleKarmaMenu(state): if $History / HistoryPopup.visible: $History._on_toggle_history(); $BackButton.visible = true; var game = get_tree().root.get_node("Root"); game.isMenuVisible = not game.isMenuVisible; return ; showMenu = state; $Karma.visible = showMenu; if showMenu: $TextBubble / WritingTimer.stop(); $Karma / Blur.material.set_shader_param("blur_amount", 1.5); $Karma / KarmaLayer / KarmaScene.Update() $Poll.StopTimerInMenu(); else : $TextBubble / WritingTimer.start(); $Karma / Blur.material.set_shader_param("blur_amount", 0); block_dialog_for_back_button = false; $Karma / KarmaLayer / KarmaScene.RemoveHover(); $Poll.ResumeTimerInMenu(); for i in $History.get_children(): if i is Button: i.disabled = showMenu; for i in $Options.get_child(0).get_children(): (i as Button).disabled = showMenu; $Menu / MenuLayer / Settings.RemoveHover(); $Menu / MenuLayer / Settings.LoadTextSettings(); func backFromSettings(): Dialogic.reset_saves(); get_node("/root/BgmScene").SetBGM("99") get_node("/root/BgmScene").StopSFX() get_node("/root/BgmScene").StartMenuMusic() if not SceneLoader.is_connected("on_scene_loaded", self, "MenuLoaded"): SceneLoader.connect("on_scene_loaded", self, "MenuLoaded"); SceneLoader.load_scene("res://scenes/MainMenu.tscn") func MenuLoaded(obj): if obj.path != "res://scenes/MainMenu.tscn": return if obj.instance != null: get_tree().root.add_child(obj.instance); SceneLoader.disconnect("on_scene_loaded", self, "MenuLoaded"); get_tree().root.get_node("Root").DisconnectBackground(); for i in get_tree().root.get_children(): if i.name == "Root": get_tree().root.remove_child(i); break; func returnToGame(): var game = get_tree().root.get_node("Root"); game.ToggleMenu(); func returnToGameFromKarma(): var game = get_tree().root.get_node("Root"); game.ToggleKarmaMenu(); var isSpriteChoice; func ToggleSpriteChoice(value): isSpriteChoice = value; if (value): var game = get_tree().root.get_node("Root"); isSpriteChoice = false; var backButtonLocked; func _on_BackButton_pressed(): if not backButtonLocked: if $BackButton / FastForwardButton.pressed: $BackButton / FastForwardButton.pressed = false; backButtonLocked = true; var previous_event = dialog_script["events"][dialog_index - 1] if previous_event["event_id"] == "dialogic_001": _load_previous_event() else : var labelString = Dialogic.get_variable("CurrentLabel") var labelInt = int(labelString.substr(1)) if (labelInt > 1): labelInt -= 1 Dialogic.set_variable("BackPressed", 1) labelString = "a" + str(labelInt) Dialogic.set_variable("CurrentLabel", labelString) if (_state == state.TYPING): $TextBubble.skip() Dialogic.change_timeline(Dialogic.get_variable("TimelineSave")) yield (get_tree().create_timer(1), "timeout"); backButtonLocked = false; func _on_HistoryButton_pressed(): if get_node("Map").visible == true: _on_MapButton_button_down(); if $History / HistoryPopup.visible == false: $Poll.StopTimerInMenu(); else : $Poll.ResumeTimerInMenu(); $History._on_toggle_history() var isFastForwarding; var isAutoRead; func _on_LoadButton_button_down(): if get_node("Map").visible == true: _on_MapButton_button_down(); if $History / HistoryPopup.visible: $History._on_toggle_history(); get_tree().root.get_node("Root").isSaveLoadVisible = true; SettingsSingleton.SetSkipSeen(false); SettingsSingleton.SetAutoRead(false); $TextBubble / WritingTimer.stop(); if $BackButton / FastForwardButton.pressed: isFastForwarding = true; if $BackButton / AutoReadButton.pressed: isAutoRead = true; $BackButton.visible = false $LoadSaveMenu.visible = true $Options.get_child(0).visible = false; for i in $Options.get_child(0).get_children(): (i as Button).disabled = true; $LoadSaveMenu / LoadSaveLayer / LoadSaveScene.LoadPressed() $Poll.StopTimerInMenu(); yield (get_tree().create_timer(0.1), "timeout") $TextBubble / WritingTimer.stop(); func _on_SaveButton_button_down(): if get_node("Map").visible == true: _on_MapButton_button_down(); if $History / HistoryPopup.visible: $History._on_toggle_history(); get_tree().root.get_node("Root").isSaveLoadVisible = true; SettingsSingleton.SetSkipSeen(false); SettingsSingleton.SetAutoRead(false); $TextBubble / WritingTimer.stop(); if $BackButton / FastForwardButton.pressed: isFastForwarding = true; if $BackButton / AutoReadButton.pressed: isAutoRead = true; $BackButton.visible = false $LoadSaveMenu.visible = true $Options.get_child(0).visible = false; for i in $Options.get_child(0).get_children(): (i as Button).disabled = true; $LoadSaveMenu / LoadSaveLayer / LoadSaveScene.SavePressed() $Poll.StopTimerInMenu(); yield (get_tree().create_timer(0.1), "timeout") $TextBubble / WritingTimer.stop(); func _on_MapButton_button_down(): if $History / HistoryPopup.visible: $History._on_toggle_history(); if not $Map.visible: Input.set_custom_mouse_cursor(load("res://resources/cursors/arrow2.webp")); $Map.ProcessMapFromMenuButton(); $TextBubble / WritingTimer.stop(); for i in $Options.get_child(0).get_children(): (i as Button).disabled = true; yield (get_tree().create_timer(0.1), "timeout") $TextBubble / WritingTimer.stop(); $Map.visible = true; get_tree().root.get_node("Root").isSaveLoadVisible = true; showMenu = true; $Poll.StopTimerInMenu(); else : $TextBubble / WritingTimer.start(); for i in $Options.get_child(0).get_children(): (i as Button).disabled = false; $Map.visible = false; get_tree().root.get_node("Root").isSaveLoadVisible = false; showMenu = false; $Poll.ResumeTimerInMenu(); func BackFromSaveLoad(): get_tree().root.get_node("Root").isSaveLoadVisible = false; $BackButton.visible = true $TextBubble / WritingTimer.start(); if isFastForwarding: SettingsSingleton.SetSkipSeen(true); if isAutoRead: SettingsSingleton.SetAutoRead(true); block_dialog_for_back_button = false $LoadSaveMenu.visible = false $Options.get_child(0).visible = true; for i in $Options.get_child(0).get_children(): (i as Button).disabled = false; $Poll.ResumeTimerInMenu(); func _on_OptionsButton_pressed(): if get_node("Map").visible == true: _on_MapButton_button_down(); if $History / HistoryPopup.visible: $History._on_toggle_history(); get_parent().get_parent().get_parent().ToggleMenu() func _on_AutoReadButton_pressed(): var value = $BackButton / AutoReadButton.pressed; if value and $BackButton / FastForwardButton.pressed: $BackButton / FastForwardButton.pressed = false; SettingsSingleton.SetSkipSeen(false); SettingsSingleton.SetAutoRead(value); SettingsSingleton.SaveSettings() func _on_FastForwardButton_pressed(): var value = $BackButton / FastForwardButton.pressed; if value and $BackButton / AutoReadButton.pressed: SettingsSingleton.SetAutoRead(false); $BackButton / AutoReadButton.pressed = false; SettingsSingleton.SetSkipSeen(value); SettingsSingleton.SaveSettings() func _on_KarmaButton_pressed(): if get_node("Map").visible == true: _on_MapButton_button_down(); if $History / HistoryPopup.visible: $History._on_toggle_history(); get_parent().get_parent().get_parent().ToggleKarmaMenu() var disableMapScenes = ["Scene8", "PoliceStation", "Train_video", "Scene4", ] func CheckMap(): var background = Dialogic.get_variable("CurrentBackground"); var lol = background in disableMapScenes; $BackButton / MapButton.disabled = lol; func _on_FullView_mouse_exited(): var controlsToShow = [$Portraits, $Poll]; if not (alternativeChoicesOn or investigationOn): controlsToShow.push_back($TextBubble) for i in $BackButton.get_children(): controlsToShow.push_back(i); for i in $Options.get_children(): controlsToShow.push_back(i); $BackButton / FullView.HoverOff(controlsToShow); $TextBubble / WritingTimer.start() $Poll.ResumeTimerInMenu(); $Poll.AppearTimer(); func _on_FullView_button_up(): var controlsToHide = [ $Portraits, $TextBubble, $Poll, ] for i in $BackButton.get_children(): controlsToHide.push_back(i) for i in $Options.get_children(): controlsToHide.push_back(i) $BackButton / FullView.HoverOn(controlsToHide); $TextBubble / WritingTimer.stop() $Poll.StopTimerInMenu(); func SetTooltips(): $BackButton / SaveButton.hint_tooltip = tr("ui_save") $BackButton / LoadButton.hint_tooltip = tr("ui_load"); $BackButton / AutoReadButton.hint_tooltip = tr("ui_autoread") $BackButton / FastForwardButton.hint_tooltip = tr("ui_skipseen") $BackButton / HistoryButton.hint_tooltip = tr("ui_history") $BackButton / MapButton.hint_tooltip = tr("ui_map") $BackButton / OptionsButton.hint_tooltip = tr("ui_menu") $BackButton / KarmaButton.hint_tooltip = tr("ui_relationship") func forTwitchResize(): var widest = - 1; var startY = - 1; $Poll / SizeTimer.disconnect("timeout", self, "forTwitchResize") for i in button_container.get_child_count(): var button:Button = button_container.get_child(i); button.disabled = false; if i == 0: startY = button.rect_global_position.y var right = button.rect_global_position.x + button.rect_size.x if right > widest: widest = right $Poll / VBoxContainer.rect_global_position = Vector2(50 + widest, startY); $Poll.StartTimer(); func TwitchPollTimeout(): var number = $Poll.Result(); var button:Button = button_container.get_child(number - 1) button.modulate = Color(0.5, 0.5, 0.5, 1) $Poll / SizeTimer.connect("timeout", self, "TwichButtonPress", [button]) $Poll / SizeTimer.start(0.25); func TwichButtonPress(button): $Poll / SizeTimer.disconnect("timeout", self, "TwichButtonPress") button.emit_signal("pressed"); # Steam.set_achievement("Twitch"); const CharacterKarmas:Array = [ "1655845108-146", "1655479805-366", "1655481153-146", "1655846105-918", "1655846767-483", "1655848122-970", "1655481311-918", "1655479174-787", "1655311631-648", "1655848676-713", "1655478573-648", ]; const KarmaStructure:Array = [ {"charId":"1655845108-146", "charName":"ui_name_pink", "color":"#ffaedd", "sex":false, "isDead":"Is_Pink_Dead", "seen":"Pink"}, {"charId":"1655479805-366", "charName":"ui_name_gray", "color":"#ffaebad7", "sex":true, "isDead":"Is_Gray_Dead", "seen":"Gray"}, {"charId":"1655481153-146", "charName":"ui_name_red", "color":"#ff2f2f", "sex":true, "isDead":"Is_Red_Dead", "seen":"Red"}, {"charId":"1655846105-918", "charName":"ui_name_purple", "color":"#8353c1", "sex":false, "isDead":"Is_Purple_Dead", "seen":"Purple"}, {"charId":"1655846767-483", "charName":"ui_name_green", "color":"#66ff55", "sex":false, "isDead":"Is_Green_Dead", "seen":"Green"}, {"charId":"1655848122-970", "charName":"ui_name_black", "color":"#ff0f0a0a", "sex":false, "isDead":"Is_Black_Dead", "seen":"Black"}, {"charId":"1655481311-918", "charName":"ui_name_blue_m", "color":"#ff00ffff", "sex":true, "isDead":"Is_Blue_M_Dead", "seen":"Blue_M"}, {"charId":"1655479174-787", "charName":"ui_name_white", "color":"#ffffffff", "sex":true, "isDead":"Is_White_Dead", "seen":"White"}, {"charId":"1655311631-648", "charName":"ui_name_orange", "color":"#ffffad00", "sex":false, "isDead":"Is_Orange_Dead", "seen":"Orange"}, {"charId":"1655848676-713", "charName":"ui_name_blue_f", "color":"#4064ff", "sex":false, "isDead":"Is_Blue_F_Dead", "seen":"Blue_F"}, {"charId":"1655478573-648", "charName":"ui_name_yellow", "color":"#fffff500", "sex":true, "isDead":"Is_Yellow_Dead", "seen":"Yellow"}, ]; func KarmaChanged(event): if Dialogic.get_variable("EasyMode") == "0": return ; if Dialogic.get_variable("DisplayKarma") == "0": return ; if not event["definition"] in CharacterKarmas: return ; var structure = null; for i in KarmaStructure: if i["charId"] == event["definition"]: structure = i; break; if Dialogic.get_variable(structure["isDead"]) == "1": return ; if ProgressAchievementsSingleton.IsInSeenCharacters(structure["seen"]) == false: return ; $BackButton / KarmaButton.StartBlinking(); $ChangeKarmaContainer.AddText(event, structure); func DemonstrateKarma(): $BackButton / KarmaButton.StartBlinking();