feat: 两级存档系统——无限组/槽,建组建槽/自动档/两级浏览/读档还原/删除组与槽/游戏内加载(D8 完成)
This commit is contained in:
+162
-4
@@ -1,10 +1,168 @@
|
||||
## 存档选择面板(挂载于 SavePanel.tscn)。
|
||||
## 显示存档列表与"新建存档"入口,通过信号通知外部结果。
|
||||
## 存档面板(挂载于 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:
|
||||
back_pressed.emit()
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user