One_Eleven_Android/addons/dialogic/Nodes/DialogNode.gd
2024-11-10 03:34:28 +03:00

2510 lines
70 KiB
GDScript

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();