419 lines
11 KiB
GDScript3
419 lines
11 KiB
GDScript3
|
extends Control
|
||
|
|
||
|
# load from dialog vm
|
||
|
const Dialog = preload("res://dialog_vm.gd")
|
||
|
const DialogController = preload("res://dialog_controller.gd")
|
||
|
|
||
|
# classes from there
|
||
|
const DialogSegment = Dialog.DialogSegment
|
||
|
const DialogWithSubjectSegment = Dialog.DialogWithSubjectSegment
|
||
|
const DialogWithSubjectExpressionSegment = Dialog.DialogWithSubjectExpressionSegment
|
||
|
const QuestionSegment = Dialog.QuestionSegment
|
||
|
const ChoiceSegment = Dialog.ChoiceSegment
|
||
|
const ChoiceWithJump = Dialog.ChoiceWithJump
|
||
|
const Choice = Dialog.Choice
|
||
|
|
||
|
const WaitSegment = Dialog.YieldCallSegment
|
||
|
|
||
|
# resources specific to this dialogy thingy
|
||
|
const DialogChoices = preload("res://ui/dialog_choices.tscn")
|
||
|
const DialogChoice = preload("res://ui/dialog_choice.tscn")
|
||
|
|
||
|
var IS_DEBUG = false
|
||
|
|
||
|
var current_dialog
|
||
|
var current_dialog_controller
|
||
|
|
||
|
# ui elements
|
||
|
onready var advance_button = get_node("top_panel/auto_btn")
|
||
|
onready var fastforward_button = get_node("top_panel/ffwd_btn")
|
||
|
|
||
|
onready var title_label = get_node("body_container/bottom_panel/subject_margin/subject_panel/subject_label")
|
||
|
onready var body_container = get_node("body_container")
|
||
|
onready var body_label = get_node("body_container/bottom_panel/body_margin/body_label")
|
||
|
|
||
|
# clickable regions, for accessibility
|
||
|
onready var top_panel = get_node("top_panel")
|
||
|
|
||
|
onready var audio_player = get_node("audio_player")
|
||
|
|
||
|
# params to set from outside
|
||
|
var dialog_path
|
||
|
|
||
|
# activeness
|
||
|
var active
|
||
|
|
||
|
# tween to fade in/out
|
||
|
var tween
|
||
|
|
||
|
# timer
|
||
|
var timer
|
||
|
|
||
|
# anim player, if any
|
||
|
var anim_player
|
||
|
|
||
|
# linkin in ur get_node relative to where you want
|
||
|
var get_node_fnc
|
||
|
|
||
|
# various main dialog related constants
|
||
|
|
||
|
# signals
|
||
|
signal entered_dialog
|
||
|
signal exited_frame
|
||
|
|
||
|
# character related signals
|
||
|
signal on_speaker_expression_changed(spkr, expr)
|
||
|
|
||
|
signal on_dialog_init(d)
|
||
|
signal on_dialog_completed(d)
|
||
|
|
||
|
# setters
|
||
|
func set_auto_advance(v):
|
||
|
current_dialog_controller.set_advance_state(v)
|
||
|
advance_button.pressed = v
|
||
|
|
||
|
# signals from dialog controller
|
||
|
func _on_printing_changed(v):
|
||
|
pass
|
||
|
# next_arrow.visible = v
|
||
|
|
||
|
func _on_reading_speed_changed(new_value):
|
||
|
current_dialog_controller.set_reading_speed(new_value)
|
||
|
|
||
|
func set_advance_state():
|
||
|
current_dialog_controller.set_advance_state(advance_button.is_pressed())
|
||
|
|
||
|
func set_fastforward_state():
|
||
|
var ffwd_pressed = fastforward_button.is_pressed()
|
||
|
current_dialog_controller.set_fastforward_state(fastforward_button.is_pressed())
|
||
|
advance_button.set_disabled(ffwd_pressed)
|
||
|
|
||
|
# init functions, must be called before use
|
||
|
func set_dialog_path(path):
|
||
|
dialog_path = path
|
||
|
|
||
|
func set_dialog_vm(vm):
|
||
|
current_dialog = vm
|
||
|
|
||
|
func set_dialog_controller(c):
|
||
|
current_dialog_controller = c
|
||
|
|
||
|
func set_dialog_anim_player(p):
|
||
|
anim_player = p
|
||
|
|
||
|
func set_get_character_function(fnc):
|
||
|
get_node_fnc = fnc
|
||
|
|
||
|
func _ready():
|
||
|
|
||
|
advance_button.connect("pressed", self, "set_advance_state")
|
||
|
fastforward_button.connect("pressed", self, "set_fastforward_state")
|
||
|
|
||
|
# clicking to advance
|
||
|
body_container.connect("gui_input", self, "_on_body_click")
|
||
|
top_panel.connect("gui_input", self, "_on_body_click")
|
||
|
|
||
|
Game.connect("on_reading_speed_changed", self, "_on_reading_speed_changed")
|
||
|
Game.connect("on_settings_enter", self, "_on_settings_enter")
|
||
|
Game.connect("on_settings_exit", self, "_on_settings_exit")
|
||
|
|
||
|
tween = Tween.new()
|
||
|
add_child(tween)
|
||
|
|
||
|
timer = Timer.new()
|
||
|
add_child(timer)
|
||
|
|
||
|
|
||
|
func _on_body_click(ev):
|
||
|
current_dialog_controller._on_body_click(is_active(), ev)
|
||
|
|
||
|
func _register_func(r, reg_name, func_name):
|
||
|
if r.has(reg_name):
|
||
|
r[reg_name].set_instance(self)
|
||
|
else:
|
||
|
r[reg_name] = funcref(self, func_name)
|
||
|
|
||
|
func _register_functions():
|
||
|
var r = current_dialog.get_local_registry()
|
||
|
|
||
|
# normal functions
|
||
|
_register_func(r, "SET_EXPR", "_set_expr_fnc")
|
||
|
_register_func(r, "FLIP", "_flip_char")
|
||
|
_register_func(r, "HIDE", "_hide_char")
|
||
|
_register_func(r, "SHOW", "_show_char")
|
||
|
|
||
|
# yieldy function
|
||
|
_register_func(r, "WAIT", "_wait_dialog_fnc")
|
||
|
|
||
|
# anim shite, also yieldy
|
||
|
_register_func(r, "PLAY_ANIM", "_play_anim_fnc")
|
||
|
|
||
|
|
||
|
func _play_anim_fnc(args):
|
||
|
if args.size() == 1:
|
||
|
var anim_name = args[0]
|
||
|
if anim_player:
|
||
|
anim_player.play(anim_name)
|
||
|
return [anim_player, "animation_finished"]
|
||
|
|
||
|
func _wait_dialog_fnc(args):
|
||
|
if args.size() == 1:
|
||
|
var time = float(args[0])
|
||
|
print("Dialog: waiting for: %f seconds" % time)
|
||
|
timer.wait_time = time
|
||
|
timer.one_shot = true
|
||
|
timer.start()
|
||
|
return [timer, "timeout"]
|
||
|
|
||
|
func _set_expr_fnc(args):
|
||
|
if args.size() == 2:
|
||
|
var speaker = args[0]
|
||
|
var expr = args[1]
|
||
|
_set_speaker_expression(speaker, expr)
|
||
|
|
||
|
func _show_char(args):
|
||
|
var char_name = args[0]
|
||
|
var character = get_node_fnc.call(char_name)
|
||
|
_do_show_char(character)
|
||
|
|
||
|
func _do_show_char(c):
|
||
|
tween.interpolate_property(
|
||
|
c, "modulate:a", c.modulate.a, 1.0,
|
||
|
1.0, Tween.TRANS_QUAD,
|
||
|
Tween.EASE_IN_OUT
|
||
|
)
|
||
|
tween.start()
|
||
|
|
||
|
func _hide_char(args):
|
||
|
var char_name = args[0]
|
||
|
var character = get_node_fnc.call(char_name)
|
||
|
_do_hide_char(character)
|
||
|
|
||
|
func _do_hide_char(c):
|
||
|
tween.interpolate_property(
|
||
|
c, "modulate:a", c.modulate.a, 0.0,
|
||
|
1.0, Tween.TRANS_QUAD,
|
||
|
Tween.EASE_IN_OUT
|
||
|
)
|
||
|
tween.start()
|
||
|
|
||
|
func _flip_char(args):
|
||
|
var char_name = args[0]
|
||
|
var character = get_node_fnc.call(char_name)
|
||
|
_do_flip_char(character)
|
||
|
|
||
|
func _do_flip_char(c):
|
||
|
c.flip_h = !c.flip_h
|
||
|
|
||
|
func _on_settings_enter(s):
|
||
|
if is_active():
|
||
|
visible = false
|
||
|
|
||
|
func _on_settings_exit(s):
|
||
|
if is_active():
|
||
|
visible = true
|
||
|
|
||
|
func reload_dialog():
|
||
|
initialize_dialog()
|
||
|
|
||
|
func get_local_registry():
|
||
|
return current_dialog.get_local_registry()
|
||
|
|
||
|
func initialize_dialog():
|
||
|
|
||
|
assert(dialog_path != null)
|
||
|
|
||
|
var dialog_file = File.new()
|
||
|
dialog_file.open(dialog_path, File.READ)
|
||
|
var text = dialog_file.get_as_text()
|
||
|
dialog_file.close()
|
||
|
|
||
|
var dialog = Dialog.dialog_from_text(text)
|
||
|
current_dialog = dialog
|
||
|
|
||
|
# also setup kv store for dialog variables
|
||
|
current_dialog.set_registry(Game.current_registry)
|
||
|
|
||
|
# set up controller too yes
|
||
|
current_dialog_controller = DialogController.new(dialog, audio_player)
|
||
|
|
||
|
# register functions
|
||
|
_register_functions()
|
||
|
|
||
|
# signals
|
||
|
current_dialog_controller.connect("on_update_dialog", self, "_on_update_dialog")
|
||
|
current_dialog_controller.connect("on_dialog_completed", self, "_on_dialog_completed")
|
||
|
current_dialog_controller.connect("on_printing_changed", self, "_on_printing_changed")
|
||
|
|
||
|
emit_signal("on_dialog_init", self)
|
||
|
|
||
|
func _on_dialog_completed(d):
|
||
|
emit_signal("on_dialog_completed", self)
|
||
|
|
||
|
func _on_update_dialog(s):
|
||
|
_update_dialog(s)
|
||
|
|
||
|
func _fade_in():
|
||
|
var FADE_TIME = 1.0
|
||
|
tween.interpolate_property(
|
||
|
self,
|
||
|
"modulate:a", 0.0, 1.0, FADE_TIME,
|
||
|
Tween.TRANS_QUAD, Tween.EASE_IN
|
||
|
)
|
||
|
tween.start()
|
||
|
|
||
|
func _fade_out():
|
||
|
var FADE_TIME = 1.0
|
||
|
tween.interpolate_property(
|
||
|
self,
|
||
|
"modulate:a", 1.0, 0.0, FADE_TIME,
|
||
|
Tween.TRANS_QUAD, Tween.EASE_OUT
|
||
|
)
|
||
|
tween.start()
|
||
|
|
||
|
yield(tween, "tween_completed")
|
||
|
visible = false
|
||
|
|
||
|
func enter_frame():
|
||
|
|
||
|
_fade_in()
|
||
|
|
||
|
var r = Game.current_registry
|
||
|
r["IS_IN_DIALOG"] = true
|
||
|
|
||
|
# reload if is debug
|
||
|
if IS_DEBUG: reload_dialog()
|
||
|
|
||
|
active = true
|
||
|
visible = true
|
||
|
current_dialog.reset()
|
||
|
# next_arrow.visible = false
|
||
|
emit_signal("entered_dialog")
|
||
|
|
||
|
# dialogs
|
||
|
var first_segment = current_dialog.advance()
|
||
|
_update_dialog(first_segment)
|
||
|
|
||
|
|
||
|
func is_active():
|
||
|
return active
|
||
|
|
||
|
func exit_frame():
|
||
|
|
||
|
_fade_out()
|
||
|
|
||
|
# HACK: cludge to reset state
|
||
|
advance_button.set_pressed(false)
|
||
|
fastforward_button.set_pressed(false)
|
||
|
advance_button.emit_signal("pressed")
|
||
|
fastforward_button.emit_signal("pressed")
|
||
|
|
||
|
current_dialog_controller.reset()
|
||
|
active = false
|
||
|
|
||
|
var r = Game.current_registry
|
||
|
r["IS_IN_DIALOG"] = false
|
||
|
emit_signal("exited_frame")
|
||
|
|
||
|
|
||
|
func _set_speaker_subject(subject):
|
||
|
if Game.Characters.has(subject):
|
||
|
var data = Game.Characters[subject]
|
||
|
var subject_name = data.name
|
||
|
var subject_colour = data.name_colour
|
||
|
var subject_voice = data.voice
|
||
|
current_dialog_controller.chars_per_second = Game.DEFAULT_CHARS_PER_SECOND * data.talking_speed
|
||
|
_set_speaker_name(subject_name, subject_colour)
|
||
|
if subject_voice != null:
|
||
|
_set_speaker_voice(subject_voice)
|
||
|
else:
|
||
|
var subject_name = subject
|
||
|
current_dialog_controller.chars_per_second = Game.DEFAULT_CHARS_PER_SECOND
|
||
|
_set_speaker_name(subject_name, Color(1, 1, 1))
|
||
|
|
||
|
func _get_speaker_name():
|
||
|
return title_label.text
|
||
|
|
||
|
func _set_speaker_name(speaker_name, colour = Color(1, 1, 1)):
|
||
|
title_label.set_text(speaker_name)
|
||
|
title_label.modulate = colour
|
||
|
|
||
|
func _set_speaker_voice(voice_sample):
|
||
|
audio_player.stream = voice_sample
|
||
|
|
||
|
func _set_speaker_expression(speaker, expression):
|
||
|
emit_signal("on_speaker_expression_changed", speaker, expression)
|
||
|
|
||
|
func _choice_made(choice_modal, choice):
|
||
|
if IS_DEBUG: print("chose: ", choice.get_choice())
|
||
|
# Game.add_history_entry("you", choice.get_choice())
|
||
|
remove_child(choice_modal)
|
||
|
if choice is ChoiceWithJump:
|
||
|
# HACK: fix this later or make this unnecessary
|
||
|
var current_segment = current_dialog.jump(choice.target)
|
||
|
call_deferred("_update_dialog", current_segment)
|
||
|
else:
|
||
|
_update_dialog(choice)
|
||
|
current_dialog_controller.waiting_for_answer = false
|
||
|
|
||
|
# loop de loop
|
||
|
func _update_dialog(segment):
|
||
|
|
||
|
if segment is ChoiceSegment:
|
||
|
|
||
|
var choices = segment.get_choices()
|
||
|
|
||
|
# create new dialog choices and shit
|
||
|
var new_choices = DialogChoices.instance()
|
||
|
var vbox = new_choices.get_node("vbox")
|
||
|
|
||
|
for choice in choices:
|
||
|
var new_choice = DialogChoice.instance()
|
||
|
new_choice.text = choice.get_choice()
|
||
|
new_choice.connect("pressed", self, "_choice_made", [new_choices, choice])
|
||
|
vbox.add_child(new_choice)
|
||
|
|
||
|
add_child(new_choices)
|
||
|
|
||
|
elif segment is Choice:
|
||
|
var new_content = segment.get_response()
|
||
|
if typeof(new_content) == TYPE_OBJECT and (new_content is DialogWithSubjectSegment or new_content is DialogWithSubjectExpressionSegment):
|
||
|
_update_dialog(new_content)
|
||
|
else:
|
||
|
body_label.text = new_content
|
||
|
# Game.add_history_entry(_get_speaker_name(), new_content)
|
||
|
|
||
|
elif segment is DialogSegment or segment is DialogWithSubjectSegment or segment is DialogWithSubjectExpressionSegment:
|
||
|
var new_content = segment.get_content()
|
||
|
body_label.visible_characters = 0
|
||
|
body_label.text = new_content
|
||
|
|
||
|
if segment is DialogWithSubjectSegment:
|
||
|
_set_speaker_subject(segment.subject)
|
||
|
# Game.add_history_entry(_get_speaker_name(), new_content)
|
||
|
elif segment is DialogWithSubjectExpressionSegment:
|
||
|
_set_speaker_subject(segment.subject)
|
||
|
_set_speaker_expression(segment.subject, segment.expression)
|
||
|
# Game.add_history_entry("%s (%s)" % [_get_speaker_name(), segment.expression], new_content)
|
||
|
else:
|
||
|
pass
|
||
|
# Game.add_history_entry(_get_speaker_name(), new_content)
|
||
|
|
||
|
elif segment is WaitSegment:
|
||
|
var new_content = segment.get_content()
|
||
|
body_label.visible_characters = 0
|
||
|
body_label.text = new_content
|
||
|
|
||
|
# whatever subject you want during functions to wait on, animations and such
|
||
|
_set_speaker_subject("")
|
||
|
|
||
|
else:
|
||
|
print("encountered unknown segment: " + segment.get_content())
|
||
|
|
||
|
current_dialog_controller.update_logic_dialog(segment)
|
||
|
|
||
|
func _process(delta):
|
||
|
if is_active():
|
||
|
current_dialog_controller.tick(delta, body_label)
|
||
|
|