dialogue_ui.gd 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. # 对话UI脚本
  2. extends CanvasLayer
  3. # 节点引用
  4. @onready var panel: Panel = $Panel
  5. @onready var npc_name_label: Label = $Panel/NPCName
  6. @onready var npc_title_label: Label = $Panel/NPCTitle
  7. @onready var dialogue_text: RichTextLabel = $Panel/DialogueText
  8. @onready var player_input: LineEdit = $Panel/PlayerInput
  9. @onready var send_button: Button = $Panel/SendButton
  10. @onready var close_button: Button = $Panel/CloseButton
  11. # 当前对话的NPC
  12. var current_npc_name: String = ""
  13. # API客户端引用
  14. var api_client: Node = null
  15. func _ready():
  16. # 添加到对话系统组
  17. add_to_group("dialogue_system")
  18. # 初始隐藏
  19. visible = false
  20. # 连接按钮信号
  21. send_button.pressed.connect(_on_send_button_pressed)
  22. close_button.pressed.connect(_on_close_button_pressed)
  23. player_input.text_submitted.connect(_on_text_submitted)
  24. # 获取API客户端
  25. api_client = get_node_or_null("/root/APIClient")
  26. if api_client:
  27. api_client.chat_response_received.connect(_on_chat_response_received)
  28. api_client.chat_error.connect(_on_chat_error)
  29. print("[INFO] 对话UI初始化完成")
  30. # ⭐ 处理对话框快捷键
  31. func _input(event: InputEvent):
  32. # 如果对话框不可见,不处理
  33. if not visible:
  34. return
  35. if event is InputEventKey and event.pressed and not event.echo:
  36. # ESC键 - 关闭对话框
  37. if event.keycode == KEY_ESCAPE:
  38. hide_dialogue()
  39. get_viewport().set_input_as_handled()
  40. print("[DEBUG] ESC键关闭对话框")
  41. return
  42. # 回车键 - 发送消息 (仅当输入框有焦点时)
  43. # 注意: LineEdit的text_submitted信号已经处理了回车,这里只是额外保险
  44. if event.keycode == KEY_ENTER or event.keycode == KEY_KP_ENTER:
  45. # 如果输入框有焦点,让LineEdit自己处理
  46. if player_input.has_focus():
  47. return
  48. # 否则手动发送
  49. send_message()
  50. get_viewport().set_input_as_handled()
  51. print("[DEBUG] 回车键发送消息")
  52. return
  53. # 屏蔽移动键和交互键,防止触发游戏操作 ⭐ WASD键
  54. if event.keycode in [KEY_E, KEY_SPACE, KEY_W, KEY_A, KEY_S, KEY_D]:
  55. get_viewport().set_input_as_handled()
  56. # 只在第一次屏蔽时打印,避免刷屏
  57. match event.keycode:
  58. KEY_E:
  59. print("[DEBUG] 对话框中屏蔽了E键输入")
  60. KEY_SPACE:
  61. print("[DEBUG] 对话框中屏蔽了空格键输入")
  62. KEY_W:
  63. print("[DEBUG] 对话框中屏蔽了W键输入")
  64. KEY_A:
  65. print("[DEBUG] 对话框中屏蔽了A键输入")
  66. KEY_S:
  67. print("[DEBUG] 对话框中屏蔽了S键输入")
  68. KEY_D:
  69. print("[DEBUG] 对话框中屏蔽了D键输入")
  70. func start_dialogue(npc_name: String):
  71. """开始与NPC对话"""
  72. current_npc_name = npc_name
  73. # 通知NPC进入交互状态 (停止移动)
  74. var npc = get_npc_by_name(npc_name)
  75. if npc and npc.has_method("set_interacting"):
  76. npc.set_interacting(true)
  77. # 设置NPC信息
  78. npc_name_label.text = npc_name
  79. npc_title_label.text = Config.NPC_TITLES.get(npc_name, "")
  80. # 清空对话内容
  81. dialogue_text.clear()
  82. dialogue_text.append_text("[color=gray]与 " + npc_name + " 的对话开始...[/color]\n")
  83. # 清空输入框
  84. player_input.text = ""
  85. # 显示对话框
  86. show_dialogue()
  87. # 聚焦输入框
  88. player_input.grab_focus()
  89. print("[INFO] 开始对话: ", npc_name)
  90. func show_dialogue():
  91. """显示对话框"""
  92. visible = true
  93. # 通知玩家进入交互状态 (禁用移动)
  94. var player = get_tree().get_first_node_in_group("player")
  95. if player and player.has_method("set_interacting"):
  96. player.set_interacting(true)
  97. func hide_dialogue():
  98. """隐藏对话框"""
  99. visible = false
  100. # 通知NPC退出交互状态 (恢复移动)
  101. if current_npc_name != "":
  102. var npc = get_npc_by_name(current_npc_name)
  103. if npc and npc.has_method("set_interacting"):
  104. npc.set_interacting(false)
  105. current_npc_name = ""
  106. # 通知玩家退出交互状态 (启用移动)
  107. var player = get_tree().get_first_node_in_group("player")
  108. if player and player.has_method("set_interacting"):
  109. player.set_interacting(false)
  110. func _on_send_button_pressed():
  111. """发送按钮点击"""
  112. send_message()
  113. func _on_text_submitted(_text: String):
  114. """输入框回车"""
  115. send_message()
  116. func send_message():
  117. """发送消息"""
  118. var message = player_input.text.strip_edges()
  119. if message.is_empty():
  120. return
  121. if current_npc_name.is_empty():
  122. print("[ERROR] 没有选择NPC")
  123. return
  124. # 显示玩家消息
  125. dialogue_text.append_text("\n[color=cyan]玩家:[/color] " + message + "\n")
  126. # 清空输入框
  127. player_input.text = ""
  128. # 显示等待提示
  129. dialogue_text.append_text("[color=gray]等待回复...[/color]\n")
  130. # 发送API请求
  131. if api_client:
  132. api_client.send_chat(current_npc_name, message)
  133. else:
  134. print("[ERROR] API客户端未找到")
  135. func _on_chat_response_received(npc_name: String, message: String):
  136. """收到NPC回复"""
  137. if npc_name != current_npc_name:
  138. return
  139. # 移除"等待回复..."
  140. var text = dialogue_text.get_parsed_text()
  141. if text.ends_with("等待回复...\n"):
  142. # 清除最后一行
  143. dialogue_text.clear()
  144. var lines = text.split("\n")
  145. for i in range(lines.size() - 2):
  146. dialogue_text.append_text(lines[i] + "\n")
  147. # 显示NPC回复
  148. dialogue_text.append_text("[color=yellow]" + npc_name + ":[/color] " + message + "\n")
  149. # 滚动到底部
  150. dialogue_text.scroll_to_line(dialogue_text.get_line_count() - 1)
  151. func _on_chat_error(error_message: String):
  152. """对话错误"""
  153. dialogue_text.append_text("[color=red]错误: " + error_message + "[/color]\n")
  154. func _on_close_button_pressed():
  155. """关闭按钮点击"""
  156. hide_dialogue()
  157. # ⭐ 根据名字获取NPC节点
  158. func get_npc_by_name(npc_name: String) -> Node:
  159. """根据名字获取NPC节点"""
  160. var npcs = get_tree().get_nodes_in_group("npcs")
  161. for npc in npcs:
  162. if npc.npc_name == npc_name:
  163. return npc
  164. return null