extends Control
# 加上的炫酷的打字机效果
<NolebasePageProperties />
const TYPING_SPEED: float = 0.05 # 每个字符显示间隔(秒)
var typewriter_timer: float = 0.0 # 计时器
var typewriter_char_index: int = 0 # 当前显示到第几个字符
var typewriter_completed: bool = true # 当前文字是否已完全显示
var full_text: String = "" # 当前行的完整文本(用于跳过)
# 从 JSON 加载对话数据
var dialogues: Dictionary = {} # 这是一个字典,存储整个 JSON 的字典
var current_section: String = "start" # 当前节名
var line_index: int = 0 # 当前节内的行号
# UI 节点引用
@onready var background: TextureRect = $Background
@onready var left_char: TextureRect = $Characters/LeftChar
@onready var right_char: TextureRect = $Characters/RightChar
@onready var name_label: Label = $NameLabel
@onready var text_label: Label = $TextLabel
@onready var choice_container: VBoxContainer = $ChoiceContainer
var waiting_for_choice := false
func _ready():
# 加载第一篇章的json文件
dialogues = load_dialogue("res://dialogues/chapter1.json")
if dialogues.is_empty():
return
start_section("start")
func load_dialogue(path: String) -> Dictionary:
var file = FileAccess.open(path, FileAccess.READ)
if file == null:
print("无法打开文件:", path)
return {}
var json_str = file.get_as_text()
var json = JSON.new()
var error = json.parse(json_str)
if error != OK:
print("JSON 解析错误:", json.get_error_message())
return {}
return json.get_data()
func start_section(section_name: String):
current_section = section_name
line_index = 0
waiting_for_choice = false
show_line()
func show_line():
var section = dialogues.get(current_section)
if section == null or line_index >= section.size():
# ---- 重置打字机状态 ----
typewriter_completed = true
full_text = ""
typewriter_char_index = 0
typewriter_timer = 0.0
name_label.text = ""
text_label.text = "(对话结束)"
return
var line = section[line_index]
# 突变(不变)
if line.has("mutation"):
handle_mutation(line.mutation)
if line.type == "dialogue":
name_label.text = line.speaker
# ---- 打字机初始化 ----
full_text = line.text
text_label.text = "" # 清空,准备逐字显示
typewriter_char_index = 0
typewriter_timer = 0.0
typewriter_completed = false
# --------------------
waiting_for_choice = false
choice_container.visible = false
elif line.type == "choice":
name_label.text = ""
text_label.text = ""
# 选项不涉及打字机,直接标记完成
typewriter_completed = true
show_choices(line.choices)
# 处理突变
func handle_mutation(mut: Dictionary):
if mut.has("background"):
var tex = load(mut.background)
if tex: background.texture = tex
if mut.has("show_left"):
var tex = load(mut.show_left)
if tex: left_char.texture = tex
left_char.visible = true
if mut.has("show_right"):
var tex = load(mut.show_right)
if tex: right_char.texture = tex
right_char.visible = true
if mut.has("hide_left"):
left_char.visible = false
if mut.has("hide_right"):
right_char.visible = false
# 显示选项
func show_choices(choices: Array):
waiting_for_choice = true
choice_container.visible = true
for child in choice_container.get_children():
child.queue_free()
for choice in choices:
var btn := Button.new()
btn.text = choice.text
choice_container.add_child(btn)
btn.pressed.connect(func():
# 清除按钮
for c in choice_container.get_children():
c.queue_free()
choice_container.visible = false
waiting_for_choice = false
typewriter_completed = true # 标记为已完成,避免 _process 继续驱动旧文本
full_text = ""
text_label.text = ""
typewriter_char_index = 0
typewriter_timer = 0.0
# 如果这个选项有突变,也执行
if choice.has("mutation"):
handle_mutation(choice.mutation)
# 修正:用 start_section 跳转到新节(choice.next 是节名)
start_section(choice.next)
)
func _process(delta):
# 只在未完成且不是选择状态时驱动打字机
if typewriter_completed or waiting_for_choice:
return
typewriter_timer += delta
while typewriter_timer >= TYPING_SPEED and typewriter_char_index < len(full_text):
typewriter_timer -= TYPING_SPEED
typewriter_char_index += 1
text_label.text = full_text.substr(0, typewriter_char_index)
# 字符全部显示完毕,标记完成
if typewriter_char_index >= len(full_text):
typewriter_completed = true
# 显示继续提示(可选)
# next_indicator.visible = true
# 非选择状态时翻页、玩家输入
func _input(event):
if not (event.is_action_pressed("ui_accept") or event.is_action_pressed("ui_left_mouse")):
return
# 有选项时,不处理翻页(选项按钮自己处理)
if waiting_for_choice:
return
# 打字机未完成 → 立即显示全文
if not typewriter_completed:
text_label.text = full_text
typewriter_char_index = len(full_text)
typewriter_completed = true
# 并显示继续提示(可选)
# next_indicator.visible = true
return
# 打字已完成 → 翻页到下一句
line_index += 1
# 重置打字机状态(下一句会重新初始化)
typewriter_completed = true
show_line()