class_name DialogPlayer extends Node signal dialog_finished(dialog_key: StringName) static var active_dialog_playbacks: Array[DialogEntryPlayback] = [] @export_node_path("AudioStreamPlayer", "AudioStreamPlayer2D", "AudioStreamPlayer3D") var audio_player: NodePath: set = set_audio_player @export var dialogs: Dictionary[StringName, DialogEntry] = {} var _audio_player: Node var _current_dialog_key: StringName var _current_dialog_playback: DialogEntryPlayback func _ready() -> void: set_process(false) set_audio_player(audio_player) func _process(_delta: float) -> void: if is_instance_valid(_audio_player) and is_instance_valid(_current_dialog_playback): _current_dialog_playback.playback_position = _audio_player.get_playback_position() func set_audio_player(audio_player_path: NodePath) -> void: if is_instance_valid(_audio_player): _audio_player.finished.disconnect(_on_dialog_finished) audio_player = audio_player_path _audio_player = get_node_or_null(audio_player) if not is_instance_valid(_audio_player): set_process(false) return if ( not _audio_player.has_method(&"play") or not _audio_player.has_method(&"get_playback_position") or not &"playing" in _audio_player or not _audio_player.has_signal(&"finished") ): _audio_player = null push_error("audio_player is not a valid player.") return set_process(_audio_player.playing) if not _audio_player.finished.is_connected(_on_dialog_finished): _audio_player.finished.connect(_on_dialog_finished) func play(entry_key: StringName, from_position: float = 0.0) -> void: var dialog_entry: DialogEntry = dialogs.get(entry_key) if is_instance_valid(dialog_entry): stop_dialog() _audio_player.stream = dialog_entry.get_voiceline_stream() _current_dialog_key = entry_key _current_dialog_playback = DialogEntryPlayback.new(dialog_entry) active_dialog_playbacks.append(_current_dialog_playback) _audio_player.play(from_position) set_process(true) func get_current_dialog_entry() -> DialogEntry: return dialogs.get(_current_dialog_key) func get_playback_position() -> float: return _audio_player.get_playback_position() if is_instance_valid(_audio_player) else 0.0 func stop_dialog() -> void: active_dialog_playbacks.erase(_current_dialog_playback) _current_dialog_key = &"" _current_dialog_playback = null _audio_player.stream = null func _on_dialog_finished() -> void: var entry: DialogEntry = dialogs.get(_current_dialog_key) stop_dialog() set_process(false) dialog_finished.emit(entry)