extends UIPage # 预加载 TypewriterLogic 类 (请替换为你的实际路径!) const _TypewriterLogic = preload("res://UI/class/TypewriterLogic.gd") @export var dialogues: Array @export var default_npc_name = '秘书' @onready var npc_icon: TextureRect = $Part_2/npc_icon var npc_icon_png_folder = "res://Entity/NPC/art/" var typing_speed: float = 20.0 # 每秒显示的字符数 var label_node_path: NodePath = "Part_2/Part_3/Label" # Label节点的路径 # --- 基础变量 --- var node_name: String var dialogue_queue = [] var current_dialogue_index = -1 # Initialize to -1, so first advance makes it 0 # --- 节点和模块引用 --- var label: Label = null var typewriter: TypewriterLogic = null func _ready() -> void: super._ready() node_name = str(self.name) print(node_name + ": _ready executed.") label = get_node_or_null(label_node_path) if label == null: printerr(node_name + ": Failed to find Label node at path: ", label_node_path) else: print(node_name + ": Label found. Creating TypewriterLogic instance.") typewriter = _TypewriterLogic.new(label, typing_speed) if typewriter != null: if not typewriter.is_connected("finished", Callable(self, "_on_dialogue_typing_finished")): typewriter.finished.connect(Callable(self, "_on_dialogue_typing_finished")) print(node_name + ": TypewriterLogic instance created and signal connected.") else: printerr(node_name + ": Failed to create TypewriterLogic instance.") func _on_page_activated(): super._on_page_activated() if GameState.get_value("dialogue_npc"): var npc_name = GameState.get_value("dialogue_npc") npc_icon.texture = load(npc_icon_png_folder+npc_name+'.png') GameState.set_value("dialogue_npc",null) else: npc_icon.texture = load(npc_icon_png_folder+default_npc_name+'.png') print(node_name + ": Page activated.") _start(dialogues) func _on_page_deactivated(): super._on_page_deactivated() print(node_name + ": Page deactivated.") if typewriter != null: typewriter.stop_typing() set_process_input(false) func _start(p_dialogues) -> void: print(node_name + ": _start function called.") if p_dialogues is String: dialogue_queue = [p_dialogues] elif p_dialogues is Array: dialogue_queue = p_dialogues.duplicate() else: printerr(node_name + ": Invalid dialogues data type.") dialogue_queue = [] current_dialogue_index = -1 # Reset index # Advance to the first dialogue automatically when starting _advance_dialogue_or_finish() set_process_input(true) # Renamed and repurposed: Tries to show next dialogue or finishes the page func _advance_dialogue_or_finish() -> void: current_dialogue_index += 1 print(node_name + ": Attempting to advance. New index: ", current_dialogue_index) if current_dialogue_index < dialogue_queue.size(): _show_current_dialogue_line() else: # All dialogues in this page's queue are finished print(node_name + ": Dialogue queue for this page finished. Calling go_next().") self.hide() go_next() # This will eventually call _close_dialogue and hide the page # Renamed to reflect it shows a single line func _show_current_dialogue_line() -> void: if not (current_dialogue_index >= 0 and current_dialogue_index < dialogue_queue.size()): printerr(node_name + ": Invalid current_dialogue_index: ", current_dialogue_index) return if typewriter == null: printerr(node_name + ": TypewriterLogic instance is null. Cannot display text with effect.") if label != null: label.text = dialogue_queue[current_dialogue_index] label.visible_characters = label.text.length() # Manually call finished if no typewriter, as there's no typing to finish _on_dialogue_typing_finished() return var full_text = dialogue_queue[current_dialogue_index] print(node_name + ": Requesting typewriter to start typing: '", full_text, "'") typewriter.start_typing(full_text) func _close_dialogue(): # This is typically called by go_next() via UIPage's hide_page -> _on_page_deactivated print(node_name + ": Closing dialogue. Stopping typewriter if active.") if typewriter != null: typewriter.stop_typing() set_process_input(false) print(node_name + ": Dialogue UI processing stopped.") func _on_dialogue_typing_finished(): print(node_name + ": Received 'finished' signal from TypewriterLogic for line: '", dialogue_queue[current_dialogue_index] if current_dialogue_index < dialogue_queue.size() and current_dialogue_index >=0 else "N/A" , "'") # Current line finished typing. Ready for next input. # You could show a "continue" prompt icon here if desired. pass func _input(event: InputEvent) -> void: if not visible: # Only process if page is visible return var relevant_click = false if event is InputEventMouseButton and event.is_pressed(): if event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT: relevant_click = true if relevant_click: get_viewport().set_input_as_handled() # Consume the click event early print(node_name + ": Relevant mouse click detected (Left or Right).") # Behavior 1: If typewriter is currently typing, skip to the end of the current line. # Requires TypewriterLogic to have is_typing() and skip_to_end() methods. if typewriter != null and typewriter.is_typing(): print(node_name + ": Typewriter is typing. Calling skip_typing().") typewriter.skip_typing() # This should make it emit 'finished' signal return # Wait for next click to advance dialogue # Behavior 2: If typewriter is NOT typing (or no typewriter), advance to the next dialogue or finish. print(node_name + ": Typewriter not typing or no typewriter. Advancing dialogue or finishing.") _advance_dialogue_or_finish() return # If not a relevant click for dialogue, pass to super class (e.g., for other UI interactions) # Note: If you want right-click to *only* advance dialogue and not trigger parent's go_back, # this structure is correct because we consume the event. # If UIPage's _input handles other specific mouse buttons (e.g. middle mouse) or other event types, # they will still be processed if not handled above. if not relevant_click: # Only call super if we didn't handle it super._input(event)