merge(迭代33 卡③ 选档界面新建批): 3 存档位选档界面(新档/进档/删档+摘要三项+删档二次确认+消费方编排)
- 全量闸全绿(回执 commit=0adcbd5b domain=full 实测 all_green):冒烟 1201/0、单元 38 套件、探针三件新段+既有件零回归、静态扫描/渲染闸/熵增闸+文档域六闸(本批混触 game/+reports/ 两域);回放对拍未触面(MC 零跑) - 独立验收审计两轮通过:首轮 R1×2+回执核验失实回炉,复审 sl1-sl14 全过 - T0 对齐补充:窥视读器加 has_file 位(空/有档操作集二分判定来处,非摘要第四项) - 已知中间态:选档层生产入口「进入存档」钮归卡② mn3(探针函数直调);进档/新档暂行落点 CampOverlay(Q3 乙) - 附记:卡① 合并经 git -C 形态绕过了域校验(守卫对 -C 形态的分支名解析盲点)——本笔全量回执的 docs 段已覆盖卡① reports 的文档域验证 - 产出物与反哺点见 reports/iter33-实现报告/追认单/反哺点
This commit is contained in:
commit
5c2a7bcf1b
12 files changed
+647
-17
No files matched your search
@@ -45,6 +45,7 @@ const LAYER_CONTRACT: Array[Dictionary] = [
|
||||
{"key": "settings_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "status_panel", "kind": "modal", "tags": []},
|
||||
{"key": "career_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "save_select_overlay", "kind": "modal", "tags": []}, # 选档界面(营地交互重做卡③——登记生涯同档)
|
||||
{"key": "inter_overlay", "kind": "state", "tags": []},
|
||||
{"key": "confirm_dialog", "kind": "modal", "tags": []},
|
||||
{"key": "debug_overlay", "kind": "modal", "tags": [ModalStack.TAG_NON_LOCKING]},
|
||||
@@ -68,6 +69,8 @@ var _debug_entry := false # 调试会话门控(写死=true——恒
|
||||
var _seed_edit: LineEdit
|
||||
var _resume_btn: Button # 继续上局按钮句柄(有断点才亮)
|
||||
var confirm_dialog: ConfirmDialog # 通用确认弹窗(新冒险互斥确认弹窗化)
|
||||
var _confirm_consumer := "" # 确认弹窗消费方状态量(契约 3 编排——open 前设、分派后清)
|
||||
var _confirm_consumer_slot := 0 # 消费方附随槽号(delete_slot 消费方专用)
|
||||
var curio_overlay: CurioPickOverlay # 提升三选一覆盖层(level-clear.md v2 选择面承载;奖励节点共用)
|
||||
var _start_overlay: Control
|
||||
var _end_overlay: BattleEndOverlay
|
||||
@@ -114,6 +117,7 @@ var settings_store # 系统级设置独立存储(音量
|
||||
var settings_overlay: SettingsOverlay # 设置覆盖层(最小设置面)
|
||||
var status_panel: StatusPanel # 局内状态面板·只读(钥石余额+营地三柜台进度查看;登记设置层同档)
|
||||
var career_overlay: CareerOverlay # 生涯页·只读全屏页(跨局汇总六类九项;登记营地同档〔设置层旁、确认弹窗之前〕)
|
||||
var save_select_overlay: SaveSelectOverlay # 选档界面·3 存档位(新档/进档/删档——营地交互重做卡③;登记生涯同档)
|
||||
var debug_overlay: DebugOverlay # 调试浮窗(种子/数值调整/批量模拟/两层调试入口收一)
|
||||
var debug_badge: Button # 调试浮层入口角标(UI 最顶层浮层——LAYER_CONTRACT 末位 float 类,任何 UI 不遮挡)
|
||||
|
||||
@@ -288,6 +292,14 @@ func _build_ui() -> void:
|
||||
career_overlay = CareerOverlay.new()
|
||||
add_child(career_overlay)
|
||||
career_overlay.closed.connect(_on_career_closed)
|
||||
# 选档界面(营地交互重做卡③):登记序=生涯同档(树序同步=addChild 同位);三动作只发意图信号
|
||||
#(激活/创建/删除+确认分派归组合根——视图零数据层依赖);入口=「进入存档」钮(卡② mn3 接线)。
|
||||
save_select_overlay = SaveSelectOverlay.new()
|
||||
add_child(save_select_overlay)
|
||||
save_select_overlay.closed.connect(_on_save_select_closed)
|
||||
save_select_overlay.new_slot_requested.connect(_on_save_select_new)
|
||||
save_select_overlay.load_slot_requested.connect(_on_save_select_load)
|
||||
save_select_overlay.delete_slot_requested.connect(_on_save_select_delete)
|
||||
# 提升三选一覆盖层(z 序 = 节点层之上、终局层之下;确认后同拍推进;
|
||||
# 插位由 LAYER_CONTRACT curio 槽统一落位——禁散落 move_child)
|
||||
curio_overlay = CurioPickOverlay.new()
|
||||
@@ -344,8 +356,10 @@ func _build_ui() -> void:
|
||||
# 诊断转储入口在调试浮窗)
|
||||
confirm_dialog = ConfirmDialog.new()
|
||||
add_child(confirm_dialog)
|
||||
confirm_dialog.confirmed.connect(_on_confirm_new_adventure)
|
||||
confirm_dialog.cancelled.connect(_on_confirm_cancelled)
|
||||
# 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer,
|
||||
# confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工)
|
||||
confirm_dialog.confirmed.connect(_on_confirm_accepted)
|
||||
confirm_dialog.cancelled.connect(_on_confirm_declined)
|
||||
|
||||
# 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口;
|
||||
# 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块;
|
||||
@@ -417,6 +431,7 @@ func _layer_node(key: String) -> Node:
|
||||
"settings_overlay": return settings_overlay
|
||||
"status_panel": return status_panel
|
||||
"career_overlay": return career_overlay
|
||||
"save_select_overlay": return save_select_overlay
|
||||
"inter_overlay": return _inter_overlay
|
||||
"confirm_dialog": return confirm_dialog
|
||||
"debug_overlay": return debug_overlay
|
||||
@@ -477,6 +492,7 @@ func _start_adventure_from_menu() -> void:
|
||||
# 「开新冒险」提交(弃旧断点+写盘)/「取消」回菜单
|
||||
if save_manager.has_breakpoint():
|
||||
var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot()
|
||||
_confirm_consumer = "new_adventure" # 消费方编排(契约 3——open 前设消费方)
|
||||
confirm_dialog.open("已有未完冒险(第 %d 关)——开新冒险将覆盖旧断点" % maxi(1, int(bp.get("level_index", 1))),
|
||||
"开新冒险", "取消")
|
||||
return
|
||||
@@ -484,16 +500,26 @@ func _start_adventure_from_menu() -> void:
|
||||
_start_adventure()
|
||||
|
||||
|
||||
func _on_confirm_new_adventure() -> void:
|
||||
# 确认弹窗分派(卡③ 契约 3):按 _confirm_consumer 路由——"new_adventure"=现役新冒险确认
|
||||
#(行为与原 _on_confirm_new_adventure 逐句等价)、"delete_slot"=卡③ 删档确认(确认才删、
|
||||
# 取消只关窗——裁决 3);分派面禁写死单一目标(卡④ 到位接入第三消费方不返工)。
|
||||
func _on_confirm_accepted() -> void:
|
||||
confirm_dialog.close()
|
||||
save_manager.clear_breakpoint() # 提交即弃旧断点(覆盖语义对盘面即时生效)
|
||||
save_manager.write_to_disk()
|
||||
_start_overlay.visible = false
|
||||
_start_adventure()
|
||||
match _confirm_consumer:
|
||||
"delete_slot":
|
||||
save_manager.delete_slot(_confirm_consumer_slot)
|
||||
save_select_overlay.open_save_select(_save_select_data()) # 就地刷新(该槽回空态)
|
||||
"new_adventure":
|
||||
save_manager.clear_breakpoint() # 提交即弃旧断点(覆盖语义对盘面即时生效)
|
||||
save_manager.write_to_disk()
|
||||
_start_overlay.visible = false
|
||||
_start_adventure()
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
func _on_confirm_cancelled() -> void:
|
||||
confirm_dialog.close() # 回开始菜单(无状态变更)
|
||||
func _on_confirm_declined() -> void:
|
||||
confirm_dialog.close() # 只关窗(取消无状态变更——裁决 3:取消不删)
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
# 退出游戏(一级菜单入口;设置即时落盘后退出)
|
||||
@@ -1125,6 +1151,58 @@ func _career_data() -> Dictionary:
|
||||
}
|
||||
|
||||
|
||||
# ==================== 选档界面(营地交互重做卡③)====================
|
||||
|
||||
# 开层(模态栈单口;与开始层互斥=career 同款先例)。「进入存档」钮归卡② mn3——本函数为
|
||||
# 该钮的唯一落点(卡③ 批内探针函数直调=中间态,实现报告「已知中间态」登记)。
|
||||
func _open_save_select() -> void:
|
||||
_start_overlay.visible = false
|
||||
_open_modal(save_select_overlay) # 模态栈单口
|
||||
save_select_overlay.open_save_select(_save_select_data())
|
||||
|
||||
|
||||
func _on_save_select_closed() -> void:
|
||||
_close_modal(save_select_overlay) # 模态栈单口
|
||||
_start_overlay.visible = true
|
||||
_refresh_resume_button()
|
||||
|
||||
|
||||
# 槽数据快照(只读同源——每次开层实时经卡① 窥视 API 逐槽取数拼装;零写调用)。
|
||||
# occupied = has_file ∧ readable:坏槽/高版本槽按空槽呈现(Q2 裁甲——进档/新档均经卡① 兜底链接管);
|
||||
# 摘要仅三项(裁决 4),has_file 只是空/有档操作集二分判定来处(非摘要项)。
|
||||
func _save_select_data() -> Dictionary:
|
||||
var slots: Array[Dictionary] = []
|
||||
for i in range(1, save_manager.SLOT_COUNT + 1):
|
||||
var p := save_manager.peek_slot_summary(i) # 推断 Dictionary(窥视返回合同——裸注解触 R1、参数化赋值运行期严格拒)
|
||||
slots.append({"slot": i,
|
||||
"occupied": bool(p.get("has_file", false)) and bool(p.get("readable", false)),
|
||||
"keystone": int(p.get("keystone", 0)),
|
||||
"cleared": int(p.get("cleared", 0)),
|
||||
"total": int(p.get("total", 0))})
|
||||
return {"slots": slots}
|
||||
|
||||
|
||||
# 空槽新档链(Q3 裁乙暂行落点=现役 CampOverlay;卡④ 重立卡后改挂场景图):卡① 创建=激活=白板落盘,
|
||||
# 落点开层。动线序(禁先关后开):先落点开层(开始层保持隐藏),选档层被模态栈遮挡隐藏、不关闭。
|
||||
func _on_save_select_new(slot: int) -> void:
|
||||
save_manager.create_slot(slot)
|
||||
_camp_pres.open_camp()
|
||||
|
||||
|
||||
# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→暂行落点 CampOverlay。
|
||||
func _on_save_select_load(slot: int) -> void:
|
||||
save_manager.activate_slot(slot)
|
||||
_camp_pres.open_camp()
|
||||
|
||||
|
||||
# 删档链(契约 3):第一次点击只出确认窗(消费方编排——open 前设消费方=delete_slot),
|
||||
# 确认才删、取消不删;禁长按删/一次点击即删/撤销式软删。
|
||||
func _on_save_select_delete(slot: int) -> void:
|
||||
_confirm_consumer = "delete_slot"
|
||||
_confirm_consumer_slot = slot
|
||||
confirm_dialog.open("删除存档位 %d?删除后该档不可恢复。" % slot, "删除", "取消")
|
||||
|
||||
|
||||
# 入口角标点击(两屏同款处理器):P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰,
|
||||
# 探针 pressed.emit 绕过 disabled 时由此兜住;地图屏自身的登记开态不计——has_open_excluding)
|
||||
func _on_status_badge_pressed() -> void:
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 选档界面·全屏页:3 存档位(新档/进档/删档)+ 槽摘要三项(钥石余额/通关局数/冒险总局数)。
|
||||
## 结构树层级:选档 → 进档 → 营地(本面零存档内功能入口——禁营地/冒险/生涯跳转与购买/查看入口);
|
||||
## 删档必经二次确认(确认弹窗与分派归组合根消费方编排——本面只发意图信号)。
|
||||
## 槽数与槽态(空/有档)唯一来自组合根喂入数据(open_save_select 每开重建——组合根经卡① 窥视 API
|
||||
## 取数;视图禁自数文件/禁自拼槽名/禁硬编码槽判定);Q1 甲=零预选(视图零选中态字段、零持久化)。
|
||||
## 摘要面仅三项,零断点/未完冒险读数(裁决 4——槽卡只消费喂入三键)。
|
||||
## 构成件全部经 UiKit 工厂产出——零新素材/新色值(契约 7)。
|
||||
class_name SaveSelectOverlay
|
||||
extends Control
|
||||
|
||||
signal closed
|
||||
signal new_slot_requested(slot: int)
|
||||
signal load_slot_requested(slot: int)
|
||||
signal delete_slot_requested(slot: int)
|
||||
|
||||
var _rows_box: VBoxContainer
|
||||
var _summary_labels: Dictionary[int, Label] = {} # 槽号→摘要读数 label(通关/冒险两值——探针取值缝)
|
||||
var _occupied_flags: Dictionary[int, bool] = {} # 槽号→有档态(操作集二分断言缝;判定源=喂入数据)
|
||||
var _new_btns: Dictionary[int, Button] = {} # 槽号→新档钮(空/坏槽——操作集断言与点击链驱动缝)
|
||||
var _load_btns: Dictionary[int, Button] = {} # 槽号→进档钮(有档槽)
|
||||
var _delete_btns: Dictionary[int, Button] = {} # 槽号→删档钮(有档槽——确认链驱动缝)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# 全屏页壳(career_overlay 同款先例——暖亮底+石纹+暗角+56px 通栏顶条+内容列)
|
||||
var page: Dictionary[Variant, Variant] = UiKit.page_shell(self, true)
|
||||
var top: HBoxContainer = page.top_row
|
||||
top.add_child(UiKit.label("存 档",UiKit.FS_TITLE, true, UiKit.WARM))
|
||||
var top_sp := Control.new()
|
||||
top_sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(top_sp)
|
||||
var close_btn := UiKit.button("返回", 90.0) # 紧凑返回钮(生涯/设置先例形制)
|
||||
close_btn.tooltip_text = "关闭选档界面,返回主菜单(进入存档位前不发生任何进度读写)"
|
||||
close_btn.pressed.connect(_emit_closed)
|
||||
top.add_child(close_btn)
|
||||
|
||||
var box: VBoxContainer = page.box
|
||||
# 对比度口径:亮底页用亮墨族 token(career_overlay 同款——深墨族禁用)
|
||||
box.add_child(UiKit.label("选择一个存档位进入。空存档位可新开档;删档需二次确认。",
|
||||
UiKit.FS_HINT, false, UiKit.WARM_SOFT))
|
||||
_rows_box = VBoxContainer.new()
|
||||
_rows_box.add_theme_constant_override("separation", 10)
|
||||
box.add_child(_rows_box)
|
||||
|
||||
visible = false # 显式初始化——不靠树序巧合(缺陷⑤家族纪律)
|
||||
|
||||
|
||||
# ---- 开层(组合根唯一调用口——槽数据随开重建;删档确认后就地重喂刷新)----
|
||||
# data = {"slots": [{"slot": int, "occupied": bool, "keystone": int, "cleared": int, "total": int}, ...]}
|
||||
# 槽数=数组长度(位数唯一在卡① 承载面 SLOT_COUNT);occupied=组合根经窥视 API 判定
|
||||
# (has_file∧readable——坏槽/高版本槽按空槽呈现,Q2 裁甲);摘要仅消费三键(裁决 4)。
|
||||
func open_save_select(data: Dictionary) -> void:
|
||||
for c in _rows_box.get_children():
|
||||
_rows_box.remove_child(c)
|
||||
c.queue_free() # 先摘树后释放——重建无同帧叠影
|
||||
_slot_labels_clear()
|
||||
var slots: Array[Variant] = []
|
||||
slots.assign(data.get("slots", [])) # 元素为 Dictionary(喂入合同)——typed 容器过类型闸 R1
|
||||
for sd: Dictionary in slots:
|
||||
_build_slot_row(sd)
|
||||
visible = true
|
||||
|
||||
|
||||
func _slot_labels_clear() -> void:
|
||||
_summary_labels.clear()
|
||||
_occupied_flags.clear()
|
||||
_new_btns.clear()
|
||||
_load_btns.clear()
|
||||
_delete_btns.clear()
|
||||
|
||||
|
||||
func _build_slot_row(sd: Dictionary) -> void:
|
||||
var slot := int(sd.get("slot", 0))
|
||||
var occupied := bool(sd.get("occupied", false))
|
||||
_occupied_flags[slot] = occupied
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 14)
|
||||
_rows_box.add_child(row)
|
||||
var name_l := UiKit.label("存档位 %d" % slot,UiKit.FS_SUB, true, UiKit.WARM)
|
||||
name_l.custom_minimum_size = Vector2(110, 0)
|
||||
row.add_child(name_l)
|
||||
var sp := Control.new()
|
||||
sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
if occupied:
|
||||
row.add_child(UiKit.keystone_badge("钥石 %d" % int(sd.get("keystone", 0))))
|
||||
var summary_l := UiKit.label("通关 %d 局 · 冒险 %d 局" % [int(sd.get("cleared", 0)), int(sd.get("total", 0))],
|
||||
UiKit.FS_BODY, false, UiKit.CANDLE)
|
||||
_summary_labels[slot] = summary_l # 摘要读数缝(禁词断言枚举 Label 全集时含此件)
|
||||
row.add_child(summary_l)
|
||||
row.add_child(sp)
|
||||
var load_btn := UiKit.primary_button("进档")
|
||||
load_btn.tooltip_text = "激活存档位 %d 并进入营地" % slot
|
||||
load_btn.pressed.connect(func() -> void: load_slot_requested.emit(slot))
|
||||
row.add_child(load_btn)
|
||||
_load_btns[slot] = load_btn
|
||||
var del_btn := UiKit.danger_button("删档", "删除存档位 %d(需二次确认——删除后该档不可恢复)" % slot)
|
||||
del_btn.pressed.connect(func() -> void: delete_slot_requested.emit(slot))
|
||||
row.add_child(del_btn)
|
||||
_delete_btns[slot] = del_btn
|
||||
else:
|
||||
row.add_child(UiKit.label("空存档位",UiKit.FS_BODY, false, UiKit.WARM_FAINT))
|
||||
row.add_child(sp)
|
||||
var new_btn := UiKit.primary_button("新档")
|
||||
new_btn.tooltip_text = "在存档位 %d 新开一档(初始白板)" % slot
|
||||
new_btn.pressed.connect(func() -> void: new_slot_requested.emit(slot))
|
||||
row.add_child(new_btn)
|
||||
_new_btns[slot] = new_btn
|
||||
|
||||
|
||||
func _emit_closed() -> void:
|
||||
closed.emit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://cd3ayrxyco3ju
|
||||
Reference in new issue
Block a user