iter49-卡2:模态句柄分域批(句柄册三域化):UiModals 14→3纯字段容器+run/menu两新子容器+ModalFlow升格注册面直驻+全仓寻址换代+V1结构断言

This commit is contained in:
DKChan committed 2026-11-02 07:30:33 +08:00
1 parent 11beb4fc59
commit 240f1a1769
28 files changed
+552 -513

No files matched your search

+2 -2
View File
@@ -71,5 +71,5 @@ func _check_main_scene(main: Node) -> void:
check(start_overlay != null and start_overlay.visible, "启动态菜单可见(boot 后玩家见菜单)")
check(main.get("settings_store") != null, "系统设置存储已装配(settings_store)")
check(main.get("ui").screens.debug_badge != null, "调试入口角标已装配(debug_badge)")
check(main.get("ui").modals.legend_overlay != null, "图例覆盖层已装配(legend_overlay)")
check(main.get("ui").modals.status_panel != null, "局内状态面板已装配(status_panel)")
check(main.get("ui").modals.run.legend_overlay != null, "图例覆盖层已装配(legend_overlay)")
check(main.get("ui").modals.run.status_panel != null, "局内状态面板已装配(status_panel)")
+21 -21
View File
@@ -105,7 +105,7 @@ func _smoke_scene_split() -> void:
chain_prev = node
check(chain_ok, "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕〔句柄子容器批 V2〕" % chain_detail)
# ③ 探针挂载假设句柄在座(main.X 一级路径——探针迁移面收口证明)
check(main.ui.screens.start_overlay == main.ui.screens.menu_screen.start_overlay and main.ui.modals.camp.scene == main.ui.screens.camp_screen.camp_scene and main.ui.modals.level_screen == main.ui.screens.adventure_screen.level_screen and main.ui.screens.battle_root == main.ui.screens.adventure_screen.battle_root and main.ui.battle.board_zone.board_view == main.ui.screens.adventure_screen.board_view,
check(main.ui.screens.start_overlay == main.ui.screens.menu_screen.start_overlay and main.ui.modals.camp.scene == main.ui.screens.camp_screen.camp_scene and main.ui.modals.run.level_screen == main.ui.screens.adventure_screen.level_screen and main.ui.screens.battle_root == main.ui.screens.adventure_screen.battle_root and main.ui.battle.board_zone.board_view == main.ui.screens.adventure_screen.board_view,
"V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)〔句柄子容器批 V1〕")
check(main.run_scope != null and not main.run_scope.is_adventure,
"V2 闲时会话自 _ready 即在(永非 null;迭代 27 T3——gp 影子账退役无账本成员)")
@@ -117,9 +117,9 @@ func _smoke_scene_split() -> void:
# camp 壳 boot 态 vis=true 但成员恒隐,裸树遍历自顶向下会先命中透明壳)
func _topmost_visible_layer(main: Node) -> Node:
var candidates: Array[Node] = [main.ui.screens.start_overlay, main.ui.screens.battle_root, main.ui.screens.inter_overlay,
main.ui.modals.legend_overlay, main.ui.modals.node_overlay, main.ui.modals.level_screen, main.ui.modals.curio_overlay,
main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp.scene, main.ui.modals.settings_overlay,
main.ui.modals.status_panel, main.ui.modals.career_overlay, main.ui.modals.confirm_dialog, main.ui.modals.debug_overlay]
main.ui.modals.run.legend_overlay, main.ui.modals.run.node_overlay, main.ui.modals.run.level_screen, main.ui.modals.run.curio_overlay,
main.ui.modals.run.end_overlay, main.ui.modals.menu.shop_overlay, main.ui.modals.camp.scene, main.ui.modals.menu.settings_overlay,
main.ui.modals.run.status_panel, main.ui.modals.menu.career_overlay, main.ui.modals.menu.confirm_dialog, main.ui.modals.menu.debug_overlay]
var best: Node = null
var best_x := -1
var best_y := -1
@@ -199,7 +199,7 @@ func _smoke_boot_chain() -> void:
"V2e run_scope 非空且为闲时会话(is_adventure=false+is_inside_tree)〔装配生命批 V2〕")
# ⑥ 调试浮窗 boot 不开+菜单角标在(菜单优先——boot 不自动开浮窗,
# 入口=开始屏右下角「调试」角标+对局中两屏角标)
check(not main.ui.modals.debug_overlay.visible,
check(not main.ui.modals.menu.debug_overlay.visible,
"V2f 调试浮窗 boot 不自动开(菜单优先)〔2026-10-16 用户令〕")
check(main.ui.screens.debug_badge != null and not main.ui.screens.debug_badge.visible,
"V2f 调试浮层角标发布态恒隐(2026-10-25 删写死行批)")
@@ -223,7 +223,7 @@ func _smoke_run_lifecycle() -> void:
sm.slots.attach_path("user://smoke_run_lifecycle.json")
sm.meta_state.mark_first_adventure_done()
# ---- 第一局:开局→注脏→放弃→终局出口→会话收束 ----
main.ui.modals.debug_overlay.seed_edit.text = "42"
main.ui.modals.menu.debug_overlay.seed_edit.text = "42"
main._start_adventure_from_menu()
await process_frame
check(main.run_scope != null and main.run_scope.is_adventure and main.run_scope.adventure != null,
@@ -243,7 +243,7 @@ func _smoke_run_lifecycle() -> void:
break
await process_frame
check(finished.done, "V3 冒险终局到达(abandon→adventure_finished 同步链)")
check(main.ui.modals.end_overlay.is_open(), "V3 终局面板弹起")
check(main.ui.modals.run.end_overlay.is_open(), "V3 终局面板弹起")
main._on_battle_end_continue() # 终局出口(回菜单+会话收束——手工置空清单已消亡)
await process_frame
check(not main.run_scope.is_adventure and main.run_scope.adventure == null and main.run_scope.runner == null,
@@ -253,7 +253,7 @@ func _smoke_run_lifecycle() -> void:
check(main.ui.screens.start_overlay.visible, "V3 回开始菜单(终局出口路径)")
# ---- 第二局:放弃终局无断点残留→直进开局→全新会话 ----
check(not sm.has_breakpoint(), "V3 放弃终局清断点(无残留断点)")
main.ui.modals.debug_overlay.seed_edit.text = "43"
main.ui.modals.menu.debug_overlay.seed_edit.text = "43"
main._start_adventure_from_menu()
await process_frame
check(main.run_scope.is_adventure and main.run_scope.adventure != null and main.run_scope.hints_shown.is_empty(),
@@ -288,7 +288,7 @@ func _smoke_sim_guards() -> void:
"模拟运行态下调试两层入口守卫早退(开始层不收起)〔wd6〕")
main._tune_sim._running = false
# wd7:真实冒险关间态(is_adventure=true、settlement==null)→ 模拟入口守卫早退
main.ui.modals.debug_overlay.seed_edit.text = "42"
main.ui.modals.menu.debug_overlay.seed_edit.text = "42"
main._start_adventure_from_menu()
await process_frame
check(main.run_scope.is_adventure and main.run_scope.settlement == null,
@@ -313,7 +313,7 @@ func _face_snapshot(main: Node) -> Dictionary:
var tu = main._treasure_ui
var bo = main._bonus
var cp = main._camp_pres
var mf = main.ui.modals.flow
var mf = main.ui.flow
var rp = main.ui.screens.row_pacer
return {
"treasure_settlement": tu._settlement_get.call(),
@@ -381,10 +381,10 @@ func _stage_faces(main: Node, tag: String, adv: String, run: String, set: String
if bool(snap["mf_rows_empty"]) != bool(extra[k]):
bad += " modal_flow.rows_empty_get 应=%s" % str(extra[k])
"level_vis":
if main.ui.modals.level_screen.visible != bool(extra[k]):
if main.ui.modals.run.level_screen.visible != bool(extra[k]):
bad += " level_screen.visible 应=%s" % str(extra[k])
"end_open":
if main.ui.modals.end_overlay.is_open() != bool(extra[k]):
if main.ui.modals.run.end_overlay.is_open() != bool(extra[k]):
bad += " _end_overlay.is_open 应=%s" % str(extra[k])
"start_vis":
if main.ui.screens.start_overlay.visible != bool(extra[k]):
@@ -419,7 +419,7 @@ func _smoke_lifecycle_matrix() -> void:
if FileAccess.file_exists(p):
DirAccess.remove_absolute(p)
sm.slots.attach_path("user://smoke_lifecycle.json")
main.ui.modals.debug_overlay.seed_edit.text = "42"
main.ui.modals.menu.debug_overlay.seed_edit.text = "42"
# 阶段①闲时
await _stage_faces(main, "V3a 闲时", "null", "null", "null",
{"mf_start": true, "mf_inter": false, "mf_rows_empty": true, "start_vis": true})
@@ -430,7 +430,7 @@ func _smoke_lifecycle_matrix() -> void:
await _stage_faces(main, "V3b 开局停点", "alive", "alive", "null",
{"mf_start": false, "level_vis": true})
# 阶段③地图选路→战斗中(真实选路提交 press_candidate)
main.ui.modals.level_screen.press_candidate(0)
main.ui.modals.run.level_screen.press_candidate(0)
for i in range(10):
await process_frame
await _stage_faces(main, "V3c 战斗中", "alive", "alive", "alive", {"mf_start": false})
@@ -467,7 +467,7 @@ func _smoke_lifecycle_matrix() -> void:
await process_frame
main.ui.screens.row_pacer.flush()
for i in range(12):
if main.ui.modals.end_overlay.is_open():
if main.ui.modals.run.end_overlay.is_open():
break
await process_frame
# 分支甲:战斗胜利停点(结算后)→确认进选路→放弃→终局面
@@ -484,11 +484,11 @@ func _smoke_lifecycle_matrix() -> void:
await process_frame
main.ui.screens.row_pacer.flush()
for i in range(8):
if main.ui.modals.end_overlay.is_open():
if main.ui.modals.run.end_overlay.is_open():
break
await process_frame
# 阶段⑤终局面(败北/放弃皆达)
check(finished.done and main.ui.modals.end_overlay.is_open(),
check(finished.done and main.ui.modals.run.end_overlay.is_open(),
"V3e 终局面到达(adventure_finished+终局面板开)〔装配生命批 V3〕")
await _stage_faces(main, "V3e 终局面", "alive", "alive", "null", {"mf_start": false, "end_open": true})
# 阶段⑥回菜单收束(旧局消亡+新会话即驻+无真空窗)
@@ -515,7 +515,7 @@ func _smoke_lifecycle_matrix() -> void:
main._open_save_select()
for i in range(2):
await process_frame
var ssel_new: Button = main.ui.modals.save_select_overlay._new_btns.get(1)
var ssel_new: Button = main.ui.modals.menu.save_select_overlay._new_btns.get(1)
if ssel_new == null:
check(false, "V3g 营地入口缺失(选档层新档钮驱动中断)〔装配生命批 V3〕")
continue
@@ -551,7 +551,7 @@ func _smoke_lifecycle_matrix() -> void:
DirAccess.remove_absolute(slot_p)
# 阶段⑧两连局(第二局全新会话——旧局对象不复用、脏态零残留)
var first_adv = main.run_scope.adventure
main.ui.modals.debug_overlay.seed_edit.text = "43"
main.ui.modals.menu.debug_overlay.seed_edit.text = "43"
main._start_adventure_from_menu()
for i in range(6):
await process_frame
@@ -560,12 +560,12 @@ func _smoke_lifecycle_matrix() -> void:
await _stage_faces(main, "V3h 两连局·开局停点", "alive", "alive", "null", {"mf_start": false, "level_vis": true})
check(main.run_scope.adventure.abandon(), "V3h 放弃受理(第二局)〔装配生命批 V3〕")
for i in range(12):
if main.ui.modals.end_overlay.is_open():
if main.ui.modals.run.end_overlay.is_open():
break
await process_frame
main.ui.screens.row_pacer.flush()
for i in range(8):
if main.ui.modals.end_overlay.is_open():
if main.ui.modals.run.end_overlay.is_open():
break
await process_frame
await _stage_faces(main, "V3h 两连局·终局面", "alive", "alive", "null", {"mf_start": false, "end_open": true})
@@ -58,7 +58,7 @@ func _init() -> void:
main._start_adventure_from_menu()
await process_frame
# 入口停点选路进首战(真实停点链)
if not main.ui.modals.level_screen.visible:
if not main.ui.modals.run.level_screen.visible:
_problems.append("入口停点未呈现(前置失守)")
_finish()
return
@@ -716,7 +716,7 @@ func _smoke_diag_button_retired() -> void:
Kit.check("V6 主区无 build_diag_button 调用残留(旧实现同批清理)",
not main_src.contains("build_diag_button"))
Kit.check("V6 转储入口连接随迁(组合根经注册面接浮窗 diagnose_requested 信号)〔注册面 V2〕",
main_src.contains("ui.modals.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)"))
main_src.contains("ui.modals.menu.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)"))
Kit.check("V6 转储编排承接函数在位(dump_diagnosis 卡死取证保留)",
main_src.contains("func _on_diagnose_pressed()"))
var asm_src: String = FileAccess.get_file_as_string("res://presentation/ui_assembler.gd")