759 lines
25 KiB
GDScript
759 lines
25 KiB
GDScript
tool
|
|
extends Control
|
|
|
|
var last_mouse_mode = null
|
|
var input_next: String = 'ui_accept'
|
|
var dialog_index: int = 0
|
|
var finished: bool = false
|
|
var text_speed = 0.02 # Higher = lower speed
|
|
var waiting_for_answer: bool = false
|
|
var waiting_for_input: bool = false
|
|
var waiting = false
|
|
var preview = false
|
|
var definitions
|
|
var definition_visible = false
|
|
|
|
var settings
|
|
var current_theme
|
|
|
|
export(String, "TimelineDropdown") var timeline: String
|
|
export(bool) var reset_saves = true
|
|
export(bool) var debug_mode = true
|
|
signal event_start(type, event)
|
|
signal event_end(type)
|
|
signal dialogic_signal(value)
|
|
|
|
var dialog_resource
|
|
var characters
|
|
|
|
onready var ChoiceButton = load("res://addons/dialogic/Nodes/ChoiceButton.tscn")
|
|
onready var Portrait = load("res://addons/dialogic/Nodes/Portrait.tscn")
|
|
var dialog_script = {}
|
|
var questions #for keeping track of the questions answered
|
|
|
|
func _ready():
|
|
# Loading the config files
|
|
load_config_files()
|
|
|
|
# Make sure saves are ready
|
|
DialogicResources.init_definitions_saves(reset_saves)
|
|
|
|
# Checking if the dialog should read the code from a external file
|
|
if timeline != '':
|
|
dialog_script = set_current_dialog(timeline + '.json')
|
|
|
|
# Connecting resize signal
|
|
get_viewport().connect("size_changed", self, "resize_main")
|
|
resize_main()
|
|
|
|
# Setting everything up for the node to be default
|
|
$TextBubble/NameLabel.text = ''
|
|
$Background.visible = false
|
|
$TextBubble/RichTextLabel.meta_underlined = false
|
|
$DefinitionInfo.visible = false
|
|
|
|
# Getting the character information
|
|
characters = DialogicUtil.get_character_list()
|
|
|
|
if Engine.is_editor_hint() == false:
|
|
load_dialog()
|
|
|
|
|
|
func load_config_files():
|
|
definitions = DialogicUtil.get_definition_list()
|
|
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_theme = load_theme(theme_file)
|
|
|
|
|
|
func resize_main():
|
|
# This function makes sure that the dialog is displayed at the correct
|
|
# size and position in the screen.
|
|
if Engine.is_editor_hint() == false:
|
|
set_global_position(Vector2(0,0))
|
|
if ProjectSettings.get_setting("display/window/stretch/mode") != '2d':
|
|
set_deferred('rect_size', get_viewport().size)
|
|
dprint("Viewport", get_viewport().size)
|
|
$TextBubble.rect_position.x = (rect_size.x / 2) - ($TextBubble.rect_size.x / 2)
|
|
$TextBubble.rect_position.y = (rect_size.y) - ($TextBubble.rect_size.y) - current_theme.get_value('box', 'bottom_gap', 40)
|
|
|
|
|
|
func set_current_dialog(dialog_path):
|
|
var dialog_script = DialogicResources.get_timeline_json(dialog_path)
|
|
# All this parse events should be happening in the same loop ideally
|
|
# But until performance is not an issue I will probably stay lazy
|
|
# And keep adding different functions for each parsing operation.
|
|
if settings.has_section_key('dialog', 'auto_color_names'):
|
|
if settings.get_value('dialog', 'auto_color_names'):
|
|
dialog_script = parse_characters(dialog_script)
|
|
else:
|
|
dialog_script = parse_characters(dialog_script)
|
|
|
|
dialog_script = parse_text_lines(dialog_script)
|
|
dialog_script = parse_branches(dialog_script)
|
|
return dialog_script
|
|
|
|
|
|
func parse_characters(dialog_script):
|
|
var names = DialogicUtil.get_character_list()
|
|
# I should use regex here, but this is way easier :)
|
|
if names.size() > 0:
|
|
var index = 0
|
|
for t in dialog_script['events']:
|
|
if t.has('text'):
|
|
for n in names:
|
|
if n.has('name'):
|
|
dialog_script['events'][index]['text'] = t['text'].replace(n['name'],
|
|
'[color=#' + n['color'].to_html() + ']' + n['name'] + '[/color]'
|
|
)
|
|
index += 1
|
|
return dialog_script
|
|
|
|
|
|
func parse_text_lines(unparsed_dialog_script: Dictionary) -> Dictionary:
|
|
var parsed_dialog: Dictionary = unparsed_dialog_script
|
|
var new_events: Array = []
|
|
var alignment = 'Left'
|
|
var split_new_lines = true
|
|
var remove_empty_messages = true
|
|
|
|
# Return the same thing if it doesn't have events
|
|
if unparsed_dialog_script.has('events') == false:
|
|
return unparsed_dialog_script
|
|
|
|
# Getting extra settings
|
|
if settings.has_section_key('dialog', 'remove_empty_messages'):
|
|
remove_empty_messages = settings.get_value('dialog', 'remove_empty_messages')
|
|
if settings.has_section_key('dialog', 'new_lines'):
|
|
split_new_lines = settings.get_value('dialog', 'new_lines')
|
|
|
|
if current_theme != null:
|
|
alignment = current_theme.get_value('text', 'alignment', 'Left')
|
|
|
|
dprint('preview ', preview)
|
|
# Parsing
|
|
for event in unparsed_dialog_script['events']:
|
|
if event.has('text') and event.has('character') and event.has('portrait'):
|
|
if event['text'] == '' and remove_empty_messages == true:
|
|
pass
|
|
elif '\n' in event['text'] and preview == false and split_new_lines == true:
|
|
var lines = event['text'].split('\n')
|
|
var i = 0
|
|
for line in lines:
|
|
var text = lines[i]
|
|
if alignment == 'Center':
|
|
text = '[center]' + lines[i] + '[/center]'
|
|
elif alignment == 'Right':
|
|
text = '[right]' + lines[i] + '[/right]'
|
|
var _e = {
|
|
'text': text,
|
|
'character': event['character'],
|
|
'portrait': event['portrait']
|
|
}
|
|
new_events.append(_e)
|
|
i += 1
|
|
else:
|
|
var text = event['text']
|
|
if alignment == 'Center':
|
|
event['text'] = '[center]' + text + '[/center]'
|
|
elif alignment == 'Right':
|
|
event['text'] = '[right]' + text + '[/right]'
|
|
new_events.append(event)
|
|
else:
|
|
new_events.append(event)
|
|
|
|
parsed_dialog['events'] = new_events
|
|
|
|
return parsed_dialog
|
|
|
|
|
|
func parse_branches(dialog_script: Dictionary) -> Dictionary:
|
|
questions = [] # Resetting the questions
|
|
|
|
# Return the same thing if it doesn't have events
|
|
if dialog_script.has('events') == false:
|
|
return dialog_script
|
|
|
|
var parser_queue = [] # This saves the last question opened, and it gets removed once it was consumed by a endbranch event
|
|
var event_id: int = 0 # The current id for jumping later on
|
|
var question_id: int = 0 # identifying the questions to assign options to it
|
|
for event in dialog_script['events']:
|
|
if event.has('question'):
|
|
event['event_id'] = event_id
|
|
event['question_id'] = question_id
|
|
event['answered'] = false
|
|
question_id += 1
|
|
questions.append(event)
|
|
parser_queue.append(event)
|
|
|
|
if event.has('condition'):
|
|
event['event_id'] = event_id
|
|
event['question_id'] = question_id
|
|
event['answered'] = false
|
|
question_id += 1
|
|
questions.append(event)
|
|
parser_queue.append(event)
|
|
|
|
if event.has('choice'):
|
|
var opened_branch = parser_queue.back()
|
|
dialog_script['events'][opened_branch['event_id']]['options'].append({
|
|
'question_id': opened_branch['question_id'],
|
|
'label': event['choice'],
|
|
'event_id': event_id,
|
|
})
|
|
event['question_id'] = opened_branch['question_id']
|
|
|
|
if event.has('endbranch'):
|
|
event['event_id'] = event_id
|
|
var opened_branch = parser_queue.pop_back()
|
|
event['end_branch_of'] = opened_branch['question_id']
|
|
dialog_script['events'][opened_branch['event_id']]['end_id'] = event_id
|
|
event_id += 1
|
|
|
|
return dialog_script
|
|
|
|
|
|
func parse_definitions(text: String):
|
|
var words = []
|
|
var definition_list = DialogicUtil.get_definition_list()
|
|
if Engine.is_editor_hint():
|
|
# Loading variables again to avoid issues in the preview dialog
|
|
load_config_files()
|
|
|
|
var final_text: String;
|
|
final_text = _insert_variable_definitions(text)
|
|
final_text = _insert_glossary_definitions(final_text)
|
|
return final_text
|
|
|
|
func _insert_variable_definitions(text: String):
|
|
var final_text := text;
|
|
for d in definitions:
|
|
if d['type'] == 0:
|
|
var name : String = d['name'];
|
|
var value = DialogicUtil.get_var(name)
|
|
final_text = final_text.replace('[' + name + ']', value)
|
|
return final_text;
|
|
|
|
|
|
func _insert_glossary_definitions(text: String):
|
|
var color = self.current_theme.get_value('definitions', 'color', '#ffbebebe')
|
|
var final_text := text;
|
|
# I should use regex here, but this is way easier :)
|
|
for d in definitions:
|
|
if d['type'] == 1:
|
|
final_text = final_text.replace(d['name'],
|
|
'[url=' + d['section'] + ']' +
|
|
'[color=' + color + ']' + d['name'] + '[/color]' +
|
|
'[/url]'
|
|
)
|
|
return final_text;
|
|
|
|
|
|
func _process(_delta):
|
|
$TextBubble/NextIndicator.visible = finished
|
|
if Engine.is_editor_hint() == false:
|
|
# Multiple choices
|
|
if waiting_for_answer:
|
|
$Options.visible = finished
|
|
else:
|
|
$Options.visible = false
|
|
|
|
if Input.is_action_just_pressed(input_next) and waiting == false:
|
|
if $TextBubble/Tween.is_active():
|
|
# Skip to end if key is pressed during the text animation
|
|
$TextBubble/Tween.seek(999)
|
|
finished = true
|
|
else:
|
|
if waiting_for_answer == false and waiting_for_input == false:
|
|
load_dialog()
|
|
|
|
|
|
func show_dialog():
|
|
visible = true
|
|
|
|
|
|
func start_text_tween():
|
|
# This will start the animation that makes the text appear letter by letter
|
|
var tween_duration = text_speed * $TextBubble/RichTextLabel.get_total_character_count()
|
|
$TextBubble/Tween.interpolate_property(
|
|
$TextBubble/RichTextLabel, "percent_visible", 0, 1, tween_duration,
|
|
Tween.TRANS_LINEAR, Tween.EASE_IN_OUT
|
|
)
|
|
$TextBubble/Tween.start()
|
|
|
|
|
|
func update_name(character, color='#FFFFFF'):
|
|
if character.has('name'):
|
|
var parsed_name = character['name']
|
|
if character.has('display_name'):
|
|
if character['display_name'] != '':
|
|
parsed_name = character['display_name']
|
|
if character.has('color'):
|
|
color = '#' + character['color'].to_html()
|
|
$TextBubble/NameLabel.bbcode_text = '[color=' + color + ']' + parsed_name + '[/color]'
|
|
else:
|
|
$TextBubble/NameLabel.bbcode_text = ''
|
|
return true
|
|
|
|
|
|
func update_text(text):
|
|
# Updating the text and starting the animation from 0
|
|
$TextBubble/RichTextLabel.bbcode_text = self.parse_definitions(text)
|
|
$TextBubble/RichTextLabel.percent_visible = 0
|
|
|
|
# The call to this function needs to be deferred.
|
|
# More info: https://github.com/godotengine/godot/issues/36381
|
|
call_deferred("start_text_tween")
|
|
return true
|
|
|
|
|
|
func load_dialog(skip_add = false):
|
|
# Emitting signals
|
|
if dialog_script.has('events'):
|
|
if dialog_index == 0:
|
|
emit_signal("event_start", "timeline", timeline)
|
|
elif dialog_index == dialog_script['events'].size():
|
|
emit_signal("event_end", "timeline")
|
|
|
|
# Hiding definitions popup
|
|
definition_visible = false
|
|
$DefinitionInfo.visible = definition_visible
|
|
|
|
# This will load the next entry in the dialog_script array.
|
|
if dialog_script.has('events'):
|
|
if dialog_index < dialog_script['events'].size():
|
|
event_handler(dialog_script['events'][dialog_index])
|
|
else:
|
|
if Engine.is_editor_hint() == false:
|
|
queue_free()
|
|
if skip_add == false:
|
|
dialog_index += 1
|
|
|
|
|
|
func reset_dialog_extras():
|
|
$TextBubble/NameLabel.bbcode_text = ''
|
|
|
|
|
|
func get_character(character_id):
|
|
for c in characters:
|
|
if c['file'] == character_id:
|
|
return c
|
|
return {}
|
|
|
|
|
|
func event_handler(event: Dictionary):
|
|
# Handling an event and updating the available nodes accordingly.
|
|
reset_dialog_extras()
|
|
# Updating the settings and definitions in case that they were modified by a timelien
|
|
load_config_files()
|
|
|
|
dprint('[D] Current Event: ', event)
|
|
match event:
|
|
{'text', 'character', 'portrait'}:
|
|
emit_signal("event_start", "text", event)
|
|
show_dialog()
|
|
finished = false
|
|
var character_data = get_character(event['character'])
|
|
update_name(character_data)
|
|
grab_portrait_focus(character_data, event)
|
|
update_text(event['text'])
|
|
{'question', 'question_id', 'options', ..}:
|
|
emit_signal("event_start", "question", event)
|
|
show_dialog()
|
|
finished = false
|
|
waiting_for_answer = true
|
|
if event.has('name'):
|
|
update_name(event['name'])
|
|
update_text(event['question'])
|
|
if event.has('options'):
|
|
for o in event['options']:
|
|
add_choice_button(o)
|
|
{'choice', 'question_id'}:
|
|
emit_signal("event_start", "choice", event)
|
|
for q in questions:
|
|
if q['question_id'] == event['question_id']:
|
|
if q['answered']:
|
|
# If the option is for an answered question, skip to the end of it.
|
|
dialog_index = q['end_id']
|
|
load_dialog(true)
|
|
{'input', ..}:
|
|
emit_signal("event_start", "input", event)
|
|
show_dialog()
|
|
finished = false
|
|
waiting_for_input = true
|
|
update_text(event['input'])
|
|
$TextInputDialog.window_title = event['window_title']
|
|
$TextInputDialog.popup_centered()
|
|
$TextInputDialog.connect("confirmed", self, "_on_input_set", [event['variable']])
|
|
{'action', ..}:
|
|
emit_signal("event_start", "action", event)
|
|
if event['action'] == 'leaveall':
|
|
if event['character'] == '[All]':
|
|
for p in $Portraits.get_children():
|
|
p.fade_out()
|
|
else:
|
|
for p in $Portraits.get_children():
|
|
if p.character_data['file'] == event['character']:
|
|
p.fade_out()
|
|
|
|
go_to_next_event()
|
|
elif event['action'] == 'join':
|
|
if event['character'] == '':
|
|
go_to_next_event()
|
|
else:
|
|
var character_data = get_character(event['character'])
|
|
var exists = grab_portrait_focus(character_data)
|
|
if exists == false:
|
|
var p = Portrait.instance()
|
|
var char_portrait = event['portrait']
|
|
if char_portrait == '':
|
|
char_portrait = 'Default'
|
|
p.character_data = character_data
|
|
p.init(char_portrait, get_character_position(event['position']))
|
|
$Portraits.add_child(p)
|
|
p.fade_in()
|
|
go_to_next_event()
|
|
{'scene'}:
|
|
get_tree().change_scene(event['scene'])
|
|
{'background'}:
|
|
emit_signal("event_start", "background", event)
|
|
$Background.visible = true
|
|
$Background.texture = load(event['background'])
|
|
go_to_next_event()
|
|
{'audio'}, {'audio', 'file'}:
|
|
emit_signal("event_start", "audio", event)
|
|
if event['audio'] == 'play':
|
|
$FX/AudioStreamPlayer.stream = load(event['file'])
|
|
$FX/AudioStreamPlayer.play()
|
|
# Todo: audio stop
|
|
go_to_next_event()
|
|
{'endbranch', ..}:
|
|
emit_signal("event_start", "endbranch", event)
|
|
go_to_next_event()
|
|
{'change_scene'}:
|
|
get_tree().change_scene(event['change_scene'])
|
|
{'emit_signal', ..}:
|
|
dprint('[!] Emitting signal: dialogic_signal(', event['emit_signal'], ')')
|
|
emit_signal("dialogic_signal", event['emit_signal'])
|
|
go_to_next_event()
|
|
{'close_dialog'}:
|
|
emit_signal("event_start", "close_dialog", event)
|
|
queue_free()
|
|
{'set_theme'}:
|
|
emit_signal("event_start", "set_theme", event)
|
|
if event['set_theme'] != '':
|
|
current_theme = load_theme(event['set_theme'])
|
|
go_to_next_event()
|
|
{'wait_seconds'}:
|
|
emit_signal("event_start", "wait", event)
|
|
wait_seconds(event['wait_seconds'])
|
|
waiting = true
|
|
{'change_timeline'}:
|
|
dialog_script = set_current_dialog(event['change_timeline'])
|
|
dialog_index = -1
|
|
go_to_next_event()
|
|
{'condition', 'definition', 'value', 'question_id', ..}:
|
|
# Treating this conditional as an option on a regular question event
|
|
var def_value = null
|
|
var current_question = questions[event['question_id']]
|
|
for d in definitions:
|
|
if d['section'] == event['definition']:
|
|
def_value = DialogicUtil.get_var(d['name'])
|
|
|
|
var condition_met = self._compare_definitions(def_value, event['value'], event['condition']);
|
|
|
|
current_question['answered'] = !condition_met
|
|
if !condition_met:
|
|
# condition not met, skipping branch
|
|
dialog_index = current_question['end_id']
|
|
load_dialog(true)
|
|
else:
|
|
# condition met, entering branch
|
|
go_to_next_event()
|
|
{'set_value', 'definition'}:
|
|
emit_signal("event_start", "set_value", event)
|
|
DialogicResources.set_saved_definition_variable_value(event['definition'], event['set_value'])
|
|
go_to_next_event()
|
|
_:
|
|
visible = false
|
|
dprint('Other event. ', event)
|
|
|
|
|
|
func _on_input_set(variable):
|
|
var input_value = $TextInputDialog/LineEdit.text
|
|
if input_value == '':
|
|
$TextInputDialog.popup_centered()
|
|
else:
|
|
dialog_resource.custom_variables[variable] = input_value
|
|
waiting_for_input = false
|
|
$TextInputDialog/LineEdit.text = ''
|
|
$TextInputDialog.disconnect("confirmed", self, '_on_input_set')
|
|
$TextInputDialog.visible = false
|
|
load_dialog()
|
|
dprint('[!] Input selected: ', input_value)
|
|
dprint('[!] dialog variables: ', dialog_resource.custom_variables)
|
|
|
|
|
|
func reset_options():
|
|
# Clearing out the options after one was selected.
|
|
for option in $Options.get_children():
|
|
option.queue_free()
|
|
|
|
|
|
func add_choice_button(option):
|
|
var theme = current_theme
|
|
|
|
var button = ChoiceButton.instance()
|
|
button.text = option['label']
|
|
# Text
|
|
button.set('custom_fonts/font', load(theme.get_value('text', 'font', "res://addons/dialogic/Fonts/DefaultFont.tres")))
|
|
|
|
var text_color = Color(theme.get_value('text', 'color', "#ffffffff"))
|
|
button.set('custom_colors/font_color', text_color)
|
|
button.set('custom_colors/font_color_hover', text_color)
|
|
button.set('custom_colors/font_color_pressed', text_color)
|
|
|
|
if theme.get_value('buttons', 'text_color_enabled', true):
|
|
var button_text_color = Color(theme.get_value('buttons', 'text_color', "#ffffffff"))
|
|
button.set('custom_colors/font_color', button_text_color)
|
|
button.set('custom_colors/font_color_hover', button_text_color)
|
|
button.set('custom_colors/font_color_pressed', button_text_color)
|
|
|
|
# Background
|
|
button.get_node('ColorRect').color = Color(theme.get_value('buttons', 'background_color', '#ff000000'))
|
|
button.get_node('ColorRect').visible = theme.get_value('buttons', 'use_background_color', false)
|
|
|
|
button.get_node('TextureRect').visible = theme.get_value('buttons', 'use_image', true)
|
|
if theme.get_value('buttons', 'use_image', true):
|
|
button.get_node('TextureRect').texture = load(theme.get_value('buttons', 'image', "res://addons/dialogic/Images/background/background-2.png"))
|
|
|
|
var padding = theme.get_value('buttons', 'padding', Vector2(5,5))
|
|
button.get_node('ColorRect').set('margin_left', -1 * padding.x)
|
|
button.get_node('ColorRect').set('margin_right', padding.x)
|
|
button.get_node('ColorRect').set('margin_top', -1 * padding.y)
|
|
button.get_node('ColorRect').set('margin_bottom', padding.y)
|
|
|
|
button.get_node('TextureRect').set('margin_left', -1 * padding.x)
|
|
button.get_node('TextureRect').set('margin_right', padding.x)
|
|
button.get_node('TextureRect').set('margin_top', -1 * padding.y)
|
|
button.get_node('TextureRect').set('margin_bottom', padding.y)
|
|
|
|
$Options.set('custom_constants/separation', theme.get_value('buttons', 'gap', 20) + (padding.y*2))
|
|
|
|
button.connect("pressed", self, "answer_question", [button, option['event_id'], option['question_id']])
|
|
|
|
$Options.add_child(button)
|
|
|
|
if Input.get_mouse_mode() != Input.MOUSE_MODE_VISIBLE:
|
|
last_mouse_mode = Input.get_mouse_mode()
|
|
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) # Make sure the cursor is visible for the options selection
|
|
|
|
|
|
func answer_question(i, event_id, question_id):
|
|
dprint('[!] Going to ', event_id + 1, i, 'question_id:', question_id)
|
|
dprint('')
|
|
waiting_for_answer = false
|
|
dialog_index = event_id + 1
|
|
questions[question_id]['answered'] = true
|
|
dprint(' dialog_index = ', dialog_index)
|
|
reset_options()
|
|
load_dialog()
|
|
if last_mouse_mode != null:
|
|
Input.set_mouse_mode(last_mouse_mode) # Revert to last mouse mode when selection is done
|
|
last_mouse_mode = null
|
|
|
|
|
|
func _on_option_selected(option, variable, value):
|
|
dialog_resource.custom_variables[variable] = value
|
|
waiting_for_answer = false
|
|
reset_options()
|
|
load_dialog()
|
|
#print(dialog_resource.custom_variables)
|
|
dprint('[!] Option selected: ', option.text, ' value= ' , value)
|
|
|
|
|
|
func _on_Tween_tween_completed(object, key):
|
|
#$TextBubble/RichTextLabel.meta_underlined = true
|
|
finished = true
|
|
|
|
|
|
func _on_TextInputDialog_confirmed():
|
|
pass # Replace with function body.
|
|
|
|
|
|
func go_to_next_event():
|
|
# The entire event reading system should be refactored... but not today!
|
|
dialog_index += 1
|
|
load_dialog(true)
|
|
|
|
|
|
func grab_portrait_focus(character_data, event: Dictionary = {}) -> bool:
|
|
var exists = false
|
|
for portrait in $Portraits.get_children():
|
|
if portrait.character_data == character_data:
|
|
exists = true
|
|
portrait.focus()
|
|
if event.has('portrait'):
|
|
if event['portrait'] != '':
|
|
portrait.set_portrait(event['portrait'])
|
|
else:
|
|
portrait.focusout()
|
|
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 deferred_resize(current_size, result):
|
|
#var result = theme.get_value('box', 'size', Vector2(910, 167))
|
|
$TextBubble.rect_size = result
|
|
if current_size != $TextBubble.rect_size:
|
|
resize_main()
|
|
|
|
func load_theme(filename):
|
|
var theme = DialogicResources.get_theme_config(filename)
|
|
|
|
# Box size
|
|
call_deferred('deferred_resize', $TextBubble.rect_size, theme.get_value('box', 'size', Vector2(910, 167)))
|
|
|
|
# Text
|
|
var theme_font = load(theme.get_value('text', 'font', 'res://addons/dialogic/Fonts/DefaultFont.tres'))
|
|
$TextBubble/RichTextLabel.set('custom_fonts/normal_font', theme_font)
|
|
$TextBubble/NameLabel.set('custom_fonts/normal_font', theme_font)
|
|
|
|
var text_color = Color(theme.get_value('text', 'color', '#ffffffff'))
|
|
$TextBubble/RichTextLabel.set('custom_colors/default_color', text_color)
|
|
$TextBubble/NameLabel.set('custom_colors/default_color', text_color)
|
|
|
|
$TextBubble/RichTextLabel.set('custom_colors/font_color_shadow', Color('#00ffffff'))
|
|
$TextBubble/NameLabel.set('custom_colors/font_color_shadow', Color('#00ffffff'))
|
|
|
|
if theme.get_value('text', 'shadow', false):
|
|
var text_shadow_color = Color(theme.get_value('text', 'shadow_color', '#9e000000'))
|
|
$TextBubble/RichTextLabel.set('custom_colors/font_color_shadow', text_shadow_color)
|
|
$TextBubble/NameLabel.set('custom_colors/font_color_shadow', text_shadow_color)
|
|
|
|
var shadow_offset = theme.get_value('text', 'shadow_offset', Vector2(2,2))
|
|
$TextBubble/RichTextLabel.set('custom_constants/shadow_offset_x', shadow_offset.x)
|
|
$TextBubble/NameLabel.set('custom_constants/shadow_offset_x', shadow_offset.x)
|
|
$TextBubble/RichTextLabel.set('custom_constants/shadow_offset_y', shadow_offset.y)
|
|
$TextBubble/NameLabel.set('custom_constants/shadow_offset_y', shadow_offset.y)
|
|
|
|
# Text speed
|
|
text_speed = theme.get_value('text','speed', 2) * 0.01
|
|
|
|
# Margin
|
|
var text_margin = theme.get_value('text', 'margin', Vector2(20, 10))
|
|
$TextBubble/RichTextLabel.set('margin_left', text_margin.x)
|
|
$TextBubble/RichTextLabel.set('margin_right', text_margin.x * -1)
|
|
$TextBubble/RichTextLabel.set('margin_top', text_margin.y)
|
|
$TextBubble/RichTextLabel.set('margin_bottom', text_margin.y * -1)
|
|
|
|
# Backgrounds
|
|
$TextBubble/TextureRect.texture = load(theme.get_value('background','image', "res://addons/dialogic/Images/background/background-2.png"))
|
|
$TextBubble/ColorRect.color = Color(theme.get_value('background','color', "#ff000000"))
|
|
|
|
$TextBubble/ColorRect.visible = theme.get_value('background', 'use_color', false)
|
|
$TextBubble/TextureRect.visible = theme.get_value('background', 'use_image', true)
|
|
|
|
# Next image
|
|
$TextBubble/NextIndicator.texture = load(theme.get_value('next_indicator', 'image', 'res://addons/dialogic/Images/next-indicator.png'))
|
|
input_next = theme.get_value('settings', 'action_key', 'ui_accept')
|
|
|
|
# Definitions
|
|
var definitions_font = load(theme.get_value('definitions', 'font', 'res://addons/dialogic/Fonts/GlossaryFont.tres'))
|
|
$DefinitionInfo/VBoxContainer/Title.set('custom_fonts/normal_font', definitions_font)
|
|
$DefinitionInfo/VBoxContainer/Content.set('custom_fonts/normal_font', definitions_font)
|
|
$DefinitionInfo/VBoxContainer/Extra.set('custom_fonts/normal_font', definitions_font)
|
|
|
|
return theme
|
|
|
|
|
|
func _on_RichTextLabel_meta_hover_started(meta):
|
|
var correct_type = false
|
|
for d in definitions:
|
|
if d['section'] == meta:
|
|
if d['type'] == 1:
|
|
$DefinitionInfo.load_preview({
|
|
'title': d['config'].get_value(d['section'], 'extra_title', ''),
|
|
'body': d['config'].get_value(d['section'], 'extra_text', ''),
|
|
'extra': d['config'].get_value(d['section'], 'extra_extra', ''),
|
|
'color': current_theme.get_value('definitions', 'color', '#ffbebebe'),
|
|
})
|
|
correct_type = true
|
|
print(d)
|
|
|
|
if correct_type:
|
|
definition_visible = true
|
|
$DefinitionInfo.visible = definition_visible
|
|
# Adding a timer to avoid a graphical glitch
|
|
$DefinitionInfo/Timer.stop()
|
|
|
|
|
|
func _on_RichTextLabel_meta_hover_ended(meta):
|
|
# Adding a timer to avoid a graphical glitch
|
|
|
|
$DefinitionInfo/Timer.start(0.1)
|
|
|
|
|
|
func _on_Definition_Timer_timeout():
|
|
# Adding a timer to avoid a graphical glitch
|
|
definition_visible = false
|
|
$DefinitionInfo.visible = definition_visible
|
|
|
|
|
|
func wait_seconds(seconds):
|
|
$WaitSeconds.start(seconds)
|
|
$TextBubble.visible = false
|
|
|
|
|
|
func _on_WaitSeconds_timeout():
|
|
emit_signal("event_end", "wait")
|
|
waiting = false
|
|
$WaitSeconds.stop()
|
|
$TextBubble.visible = true
|
|
load_dialog()
|
|
|
|
|
|
func dprint(string, arg1='', arg2='', arg3='', arg4='' ):
|
|
# HAHAHA if you are here wondering what this is...
|
|
# I ask myself the same question :')
|
|
if debug_mode:
|
|
print(str(string) + str(arg1) + str(arg2) + str(arg3) + str(arg4))
|
|
|
|
|
|
func _compare_definitions(def_value: String, event_value: String, condition: String):
|
|
var condition_met = false;
|
|
if def_value != null and event_value != null:
|
|
var converted_def_value = def_value
|
|
var converted_event_value = event_value
|
|
if def_value.is_valid_float() and event_value.is_valid_float():
|
|
converted_def_value = float(def_value)
|
|
converted_event_value = float(event_value)
|
|
match condition:
|
|
"==":
|
|
condition_met = converted_def_value == converted_event_value
|
|
"!=":
|
|
condition_met = converted_def_value != converted_event_value
|
|
">":
|
|
condition_met = converted_def_value > converted_event_value
|
|
">=":
|
|
condition_met = converted_def_value >= converted_event_value
|
|
"<":
|
|
condition_met = converted_def_value < converted_event_value
|
|
"<=":
|
|
condition_met = converted_def_value <= converted_event_value
|
|
return condition_met
|