## 存档面板(挂载于 SavePanel.tscn)。 ## 两级浏览:左侧存档组列表 → 右侧该组槽位列表。 ## 存档模式(游戏内):点"新建存档"每次创建新槽位;读档模式(主菜单):点槽位读档。 extends PanelContainer ## 面板模式。 enum Mode { SAVE, LOAD } ## 请求关闭面板(返回上级界面)。 signal back_pressed ## 槽位条目场景。 const SAVE_SLOT_SCENE := preload("res://src/UI/Panels/SaveSlot.tscn") @onready var title_label: Label = %TitleLabel @onready var group_container: VBoxContainer = %GroupContainer @onready var slot_list: VBoxContainer = %SlotList @onready var new_slot_button: Button = %Button_NewSlot ## 当前模式。 var mode: Mode = Mode.LOAD ## 当前浏览的存档组 ID。 var _current_profile: int = 0 ## 打开面板前的暂停状态(存档模式关闭时恢复)。 var _was_paused := false ## 读档模式打开(主菜单,不暂停时间,自动选中第一个存档组)。 func open_load() -> void: mode = Mode.LOAD new_slot_button.visible = false _current_profile = 0 var profiles := SaveSystem.get_profiles() if profiles.size() > 0: _current_profile = int(profiles[0]["id"]) _show_profiles() visible = true ## 存档模式打开(游戏内,暂停时间,当前组固定高亮)。 ## 点槽位 = 回到该进度点;点"新建存档" = 保存当前进度为新槽。 func open_save() -> void: mode = Mode.SAVE new_slot_button.visible = true _current_profile = GameState.profile_id _was_paused = TimeSystem.paused TimeSystem.set_paused(true) _show_profiles() visible = true ## 关闭面板(恢复打开前的暂停状态)。 func close_panel() -> void: visible = false TimeSystem.set_paused(_was_paused) ## 点击"返回"。 func _on_button_esc_button_up() -> void: close_panel() if mode == Mode.LOAD: back_pressed.emit() ## 点击"新建存档":在所属组下创建新槽位并刷新列表。 func _on_button_new_slot_button_up() -> void: if SaveSystem.save_game(_current_profile) != "": _show_slots() ## 左列:存档组列表(当前组高亮;存档模式下当前组固定)。 func _show_profiles() -> void: title_label.text = "选择存档(存档模式)" if mode == Mode.SAVE else "选择存档" _clear(group_container) var profiles := SaveSystem.get_profiles() if profiles.is_empty(): var label := Label.new() label.text = "暂无存档组" group_container.add_child(label) _show_slots() return for p in profiles: var row := HBoxContainer.new() row.add_theme_constant_override("separation", 6) var btn := Button.new() btn.text = "%s %s" % [p["name"], p["last_date"]] btn.toggle_mode = true var is_current: bool = int(p["id"]) == _current_profile btn.set_pressed_no_signal(is_current) btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL if mode == Mode.SAVE and is_current: btn.disabled = true else: btn.button_up.connect(_on_group_pressed.bind(int(p["id"]))) row.add_child(btn) # 删除按钮:存档模式下当前组禁用(正在使用) var del_btn := Button.new() del_btn.text = "删" del_btn.disabled = mode == Mode.SAVE and is_current del_btn.button_up.connect(_on_profile_delete_requested.bind(int(p["id"]))) row.add_child(del_btn) group_container.add_child(row) _show_slots() ## 请求删除存档组:直接删除并刷新(当前浏览组删除后自动切到第一组)。 ## @param profile_id: 存档组 ID func _on_profile_delete_requested(profile_id: int) -> void: if not SaveSystem.delete_profile(profile_id): return if _current_profile == profile_id: _current_profile = 0 var profiles := SaveSystem.get_profiles() if profiles.size() > 0: _current_profile = int(profiles[0]["id"]) _show_profiles() ## 点击存档组:切换浏览。 ## @param profile_id: 存档组 ID func _on_group_pressed(profile_id: int) -> void: _current_profile = profile_id _show_profiles() ## 右列:当前组槽位列表。 func _show_slots() -> void: _clear(slot_list) if _current_profile == 0: return var slots := SaveSystem.get_slots(_current_profile) if slots.is_empty(): var label := Label.new() label.text = "(暂无存档,点上方\"新建存档\")" if mode == Mode.SAVE else "(该组暂无存档)" slot_list.add_child(label) return for i in slots.size(): _add_slot_entry(i + 1, slots[i]) ## 添加一条槽位条目。 ## @param index: 序号(1 起) ## @param slot: 槽位元数据 {name, date_text, disciple_count, spirit_stones} func _add_slot_entry(index: int, slot: Dictionary) -> void: var entry: PanelContainer = SAVE_SLOT_SCENE.instantiate() slot_list.add_child(entry) var slot_name := str(slot["name"]) var index_text := "自动" if slot_name == "auto" else str(index) var name_text := "自动存档" if slot_name == "auto" else "存档 %d" % index entry.setup(slot_name, index_text, name_text, "%s 弟子%d人" % [slot["date_text"], slot["disciple_count"]], "灵石 %d" % slot["spirit_stones"]) entry.slot_pressed.connect(_on_slot_pressed) entry.delete_requested.connect(_on_slot_delete_requested) ## 点击槽位:读档模式进入游戏;存档模式回到该进度点(当前未保存进度丢失)。 ## @param slot_name: 槽文件名(不含 .json) func _on_slot_pressed(slot_name: String) -> void: if mode == Mode.LOAD: GameState.request_load_game(_current_profile, slot_name) get_tree().change_scene_to_file("res://src/Core/MainGame.tscn") return # 存档模式:直接加载所选槽位(回到该进度点) if SaveSystem.load_game(_current_profile, slot_name): close_panel() ## 请求删除槽位:直接删除并刷新(确认弹窗后续接入)。 ## @param slot_name: 槽文件名(不含 .json) func _on_slot_delete_requested(slot_name: String) -> void: if SaveSystem.delete_slot(_current_profile, slot_name): _show_slots() ## 清空容器。 ## @param container: 目标容器 func _clear(container: VBoxContainer) -> void: for child in container.get_children(): child.queue_free()