169 lines
5.9 KiB
GDScript
169 lines
5.9 KiB
GDScript
## 存档面板(挂载于 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()
|