248 lines
7.0 KiB
GDScript
248 lines
7.0 KiB
GDScript
extends CanvasLayer
|
|
|
|
@onready var _audio_player := $'AudioStreamPlayer' as AudioStreamPlayer
|
|
var _sound_close := (
|
|
preload("res://audio/menu_close.ogg") as AudioStream
|
|
)
|
|
var _sound_confirm := (
|
|
preload("res://audio/menu_confirm.ogg") as AudioStream
|
|
)
|
|
var _sound_open := (
|
|
preload("res://audio/menu_open.ogg") as AudioStream
|
|
)
|
|
var _sound_select := (
|
|
preload("res://audio/menu_select.ogg") as AudioStream
|
|
)
|
|
|
|
class StackFrame:
|
|
var node: Control
|
|
var args: Dictionary
|
|
|
|
var _stack: Array[StackFrame]
|
|
var _viewport: Viewport
|
|
|
|
signal returned(value: Variant)
|
|
|
|
func _push(scene: PackedScene, args: Dictionary = {}) -> bool:
|
|
var node := scene.instantiate()
|
|
_link_up_buttons_to_audio(node)
|
|
if node is Control:
|
|
if args.has(&'ui_borrow_instead_of_pausing'):
|
|
PlayerControl.borrow()
|
|
else:
|
|
PlayerControl.pause()
|
|
if not _stack.is_empty():
|
|
var previous := _stack[_stack.size() - 1]
|
|
if node is Notice:
|
|
previous.node.process_mode = PROCESS_MODE_DISABLED
|
|
else:
|
|
remove_child(previous.node)
|
|
node.process_mode = PROCESS_MODE_ALWAYS
|
|
var frame := StackFrame.new()
|
|
frame.node = node
|
|
frame.args = args
|
|
_stack.push_back(frame)
|
|
add_child(node)
|
|
var focus := node.find_next_valid_focus() as Control
|
|
if focus: focus.grab_focus.call_deferred()
|
|
if args.has(&'ui_open_sound_override'):
|
|
_audio_player.stream = args.ui_open_sound_override
|
|
else:
|
|
_audio_player.stream = _sound_open
|
|
if _audio_player.stream:
|
|
_audio_player.play()
|
|
return true
|
|
else:
|
|
node.queue_free()
|
|
push_error("UI._push() called on non-Control PackedScene %s" % node)
|
|
return false
|
|
|
|
func _pop() -> bool:
|
|
if _stack.is_empty():
|
|
push_error("UI._pop() called with empty stack")
|
|
return false
|
|
else:
|
|
var frame := _stack[_stack.size() - 1]
|
|
_stack.pop_back()
|
|
remove_child(frame.node)
|
|
frame.node.queue_free()
|
|
if frame.args.has(&'ui_borrow_instead_of_pausing'):
|
|
PlayerControl.restore()
|
|
else:
|
|
PlayerControl.unpause()
|
|
if not _stack.is_empty():
|
|
var previous := _stack[_stack.size() - 1]
|
|
if frame.node is Notice:
|
|
previous.node.process_mode = PROCESS_MODE_INHERIT
|
|
else:
|
|
if not previous.node.get_parent():
|
|
add_child(previous.node)
|
|
if frame.args.has(&'ui_close_sound_override'):
|
|
_audio_player.stream = frame.args.ui_close_sound_override
|
|
else:
|
|
_audio_player.stream = _sound_close
|
|
if _audio_player.stream:
|
|
_audio_player.play()
|
|
return true
|
|
|
|
func Call(scene: PackedScene, args: Dictionary = {}) -> Variant:
|
|
var caller_depth: int = _stack.size()
|
|
if _push(scene, args):
|
|
var _retval: Variant
|
|
while _stack.size() != caller_depth:
|
|
_retval = await returned
|
|
return _retval
|
|
else:
|
|
return null
|
|
|
|
func Return(whom: Control = null, what: Variant = null) -> bool:
|
|
if _stack.is_empty():
|
|
push_error(
|
|
"UI.Return() called with empty stack (whom: %s; what: %s)" % [
|
|
whom, what
|
|
]
|
|
)
|
|
return false
|
|
elif whom and (_stack[_stack.size() - 1].node != whom):
|
|
push_error("UI.Return() called with wrong whom (%s) (what: %s)" % [
|
|
whom, what
|
|
])
|
|
return false
|
|
elif _pop():
|
|
returned.emit(what)
|
|
return true
|
|
else:
|
|
return false
|
|
|
|
func Args(whom: Control = null) -> Dictionary:
|
|
if _stack.is_empty():
|
|
push_error("UI.Args() called with empty stack (whom: %s)" % whom)
|
|
return {}
|
|
else:
|
|
var frame := _stack[_stack.size() - 1]
|
|
if (frame.node == whom) or not whom:
|
|
return frame.args
|
|
else:
|
|
push_error("UI.Args() called with wrong whom (%s)" % whom)
|
|
return {}
|
|
|
|
func context() -> Control:
|
|
return null if _stack.is_empty() else _stack[_stack.size() - 1].node
|
|
|
|
func _process(_delta: float) -> void:
|
|
if not _stack.is_empty():
|
|
var frame := _stack[_stack.size() - 1]
|
|
if (
|
|
(not frame.args.has(&'no_autofocus')) or
|
|
(not frame.args.no_autofocus)
|
|
):
|
|
var focus := get_viewport().gui_get_focus_owner()
|
|
if not focus:
|
|
focus = frame.node.find_next_valid_focus()
|
|
if focus:
|
|
focus.grab_focus()
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
var frame: StackFrame = null
|
|
if not _stack.is_empty():
|
|
frame = _stack[_stack.size() - 1]
|
|
var focus := get_viewport().gui_get_focus_owner()
|
|
if frame and event.is_action_released(&'pause') and (
|
|
frame.args.has(&'on_confirm') or
|
|
not frame.args.get(&'no_dismiss', false)
|
|
):
|
|
if frame.args.has(&'on_confirm'):
|
|
frame.args.on_confirm.call()
|
|
if not frame.args.get(&'no_dismiss', false):
|
|
Return(null, true)
|
|
get_viewport().set_input_as_handled()
|
|
elif frame and event.is_action_released(&'cancel') and (
|
|
frame.args.has(&'on_cancel') or
|
|
not frame.args.get(&'no_dismiss', false)
|
|
):
|
|
if frame.args.has(&'on_cancel'):
|
|
frame.args.on_cancel.call()
|
|
if not frame.args.get(&'no_dismiss', false):
|
|
Return(null, true)
|
|
get_viewport().set_input_as_handled()
|
|
elif event.is_action_released(&'confirm'):
|
|
if focus is BaseButton:
|
|
get_viewport().set_input_as_handled()
|
|
var press := InputEventAction.new()
|
|
press.action = &'ui_accept'
|
|
press.pressed = true
|
|
var release := InputEventAction.new()
|
|
release.action = &'ui_accept'
|
|
release.pressed = false
|
|
Input.parse_input_event(press)
|
|
Input.call_deferred(&'parse_input_event', release)
|
|
|
|
func say(message: Variant, options: Dictionary = {}) -> Variant:
|
|
if message is Array:
|
|
var retval: Variant
|
|
var leadup_options := options.duplicate()
|
|
if leadup_options.has(&'choices'):
|
|
leadup_options.erase(&'choices')
|
|
for i in message.size():
|
|
retval = await say(
|
|
message[i],
|
|
options if i == message.size() - 1 else leadup_options
|
|
)
|
|
return retval
|
|
else:
|
|
options = options.duplicate()
|
|
options[&'message'] = message
|
|
options[&'ui_borrow_instead_of_pausing'] = true
|
|
options[&'no_dismiss'] = true
|
|
options[&'ui_open_sound_override'] = null
|
|
options[&'ui_close_sound_override'] = null
|
|
options[&'ui_confirm_sound_override'] = null
|
|
options[&'ui_select_sound_override'] = null
|
|
return await Call(
|
|
preload('res://ui/Conversation/ConversationUI.tscn'),
|
|
options
|
|
)
|
|
|
|
func _play_sound_confirm() -> void:
|
|
var frame: StackFrame = null
|
|
if not _stack.is_empty():
|
|
frame = _stack[_stack.size() - 1]
|
|
if frame and frame.args.has(&'ui_confirm_sound_override'):
|
|
_audio_player.stream = frame.args.ui_confirm_sound_override
|
|
else:
|
|
_audio_player.stream = _sound_confirm
|
|
if _audio_player.stream:
|
|
_audio_player.play()
|
|
|
|
func _play_sound_select(_dont_care) -> void:
|
|
if not _audio_player.playing:
|
|
var frame: StackFrame = null
|
|
if not _stack.is_empty():
|
|
frame = _stack[_stack.size() - 1]
|
|
if frame and frame.args.has(&'ui_select_sound_override'):
|
|
_audio_player.stream = frame.args.ui_select_sound_override
|
|
else:
|
|
_audio_player.stream = _sound_select
|
|
if _audio_player.stream:
|
|
_audio_player.play()
|
|
|
|
func _link_up_buttons_to_audio(what: Node) -> void:
|
|
if what is BaseButton:
|
|
what.pressed.connect(_play_sound_confirm)
|
|
for child in what.get_children():
|
|
_link_up_buttons_to_audio(child)
|
|
|
|
func _link_up_viewport_to_audio() -> void:
|
|
_viewport.gui_focus_changed.connect(_play_sound_select)
|
|
|
|
func _unlink_viewport_from_audio() -> void:
|
|
_viewport.gui_focus_changed.disconnect(_play_sound_select)
|
|
|
|
func _enter_tree() -> void:
|
|
_viewport = get_viewport()
|
|
_link_up_viewport_to_audio()
|
|
|
|
func _exit_tree() -> void:
|
|
_unlink_viewport_from_audio()
|
|
_viewport = null
|