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

+118 -118
View File
@@ -51,7 +51,7 @@ func _ready() -> void:
var t_ui_ready := Time.get_ticks_msec() - t0
# 调试浮窗 boot 不开(菜单优先——入口=UI 最顶层全局浮层角标,
# 任何 UI 不遮挡;浮层角标遮挡判据见设置面段)
if main.ui.modals.debug_overlay.visible:
if main.ui.modals.menu.debug_overlay.visible:
_problems.append("调试浮窗 boot 自动开(菜单优先失守)")
# ---- 营地购买合成点击路径(选档进档开层→购买→关闭全走真实按钮信号路径;
# 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json);陈旧档段首清理)----
@@ -204,11 +204,11 @@ func _ready() -> void:
f_c2.close() # 槽2 有档异值(进档链夹具);槽1/3 空
main._open_save_select()
await get_tree().process_frame
if not main.ui.modals.save_select_overlay.visible:
if not main.ui.modals.menu.save_select_overlay.visible:
_problems.append("[savesel] 选档层未打开(click 段前置)")
# sl12 动线序(新档链同拍采样):逐帧可见性次序——首帧选档隐藏时营地层必须已可见、开始层保持隐藏
#(禁先关后开——_close_modal 会恢复开始层=回闪一帧)
var new_btn: Button = main.ui.modals.save_select_overlay._new_btns.get(1)
var new_btn: Button = main.ui.modals.menu.save_select_overlay._new_btns.get(1)
if new_btn == null:
_problems.append("[savesel] 槽1 新档钮缺失(sl5/sl12 驱动中断)")
else:
@@ -216,7 +216,7 @@ func _ready() -> void:
new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp
for fi in 3:
await get_tree().process_frame
samples.append([main.ui.modals.save_select_overlay.visible, main.ui.modals.camp.scene.visible, main.ui.screens.start_overlay.visible])
samples.append([main.ui.modals.menu.save_select_overlay.visible, main.ui.modals.camp.scene.visible, main.ui.screens.start_overlay.visible])
if samples[0][0]:
_problems.append("[savesel] sl12 新档后选档层仍可见(落点开层未发生)")
elif not samples[0][1]:
@@ -225,8 +225,8 @@ func _ready() -> void:
_problems.append("[savesel] sl12 动线序违约:开始层在落点帧恢复(回闪一帧)")
if main.save_manager.slots.active_slot() != 1 or not FileAccess.file_exists(csel_tpl % 1):
_problems.append("[savesel] sl5 新档链失守(active=%d 应=1 且槽1 白板应落盘)" % main.save_manager.slots.active_slot())
var occluded: Array = main.ui.modals.flow.stack._occluded.get(main.ui.modals.camp.scene, [])
if not occluded.has(main.ui.modals.save_select_overlay):
var occluded: Array = main.ui.flow.stack._occluded.get(main.ui.modals.camp.scene, [])
if not occluded.has(main.ui.modals.menu.save_select_overlay):
_problems.append("[savesel] sl12 选档层被关闭而非栈记账挂起(先关后开形态——遮挡挂起中间态钉死)")
if int((JSON.parse_string(FileAccess.get_file_as_string(csel_tpl % 1)) as Dictionary).get("meta", {}).get("keystone_balance", -1)) != 0:
_problems.append("[savesel] sl5 新档非白板(槽1 落盘应全零)")
@@ -235,7 +235,7 @@ func _ready() -> void:
# sl6 进档链:槽2 进档 → 激活=2(camp 数据随激活槽)
main._open_save_select()
await get_tree().process_frame
var load_btn: Button = main.ui.modals.save_select_overlay._load_btns.get(2)
var load_btn: Button = main.ui.modals.menu.save_select_overlay._load_btns.get(2)
if load_btn == null:
_problems.append("[savesel] 槽2 进档钮缺失(sl6 驱动中断——夹具应有档)")
else:
@@ -243,51 +243,51 @@ func _ready() -> void:
await get_tree().process_frame
if main.save_manager.slots.active_slot() != 2:
_problems.append("[savesel] sl6 进档链失守(active=%d 应=2——激活该槽)" % main.save_manager.slots.active_slot())
if not main.ui.modals.camp.scene.visible or main.ui.modals.save_select_overlay.visible:
if not main.ui.modals.camp.scene.visible or main.ui.modals.menu.save_select_overlay.visible:
_problems.append("[savesel] sl6 落点未开营地层或选档未隐藏")
main._on_camp_closed()
await get_tree().process_frame
# sl7 删档二次确认双路径:一击只开窗(文件仍在)→取消只关窗(文件仍在)→再开窗确认才删(槽回空)
main._open_save_select()
await get_tree().process_frame
var del_btn: Button = main.ui.modals.save_select_overlay._delete_btns.get(2)
var del_btn: Button = main.ui.modals.menu.save_select_overlay._delete_btns.get(2)
if del_btn == null:
_problems.append("[savesel] 槽2 删档钮缺失(sl7 驱动中断)")
else:
del_btn.pressed.emit()
await get_tree().process_frame
if not main.ui.modals.confirm_dialog.is_open():
if not main.ui.modals.menu.confirm_dialog.is_open():
_problems.append("[savesel] sl7 一击未只开确认窗(直删形态)")
elif not FileAccess.file_exists(csel_tpl % 2):
_problems.append("[savesel] sl7 确认前文件已被删(一次点击即删违契)")
else:
if not main.ui.modals.flow.stack._occluded.has(main.ui.modals.confirm_dialog):
if not main.ui.flow.stack._occluded.has(main.ui.modals.menu.confirm_dialog):
_problems.append("[savesel] sl7 确认窗未经模态栈开(直开旁路——wr2 单口失守)")
elif not main.ui.modals.flow.locks_input():
elif not main.ui.flow.locks_input():
_problems.append("[savesel] sl7 确认窗开时输入锁未生效(锁域失守——wr2)")
main.ui.modals.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源)
main.ui.modals.menu.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源)
await get_tree().process_frame
if main.ui.modals.confirm_dialog.is_open() or not FileAccess.file_exists(csel_tpl % 2):
if main.ui.modals.menu.confirm_dialog.is_open() or not FileAccess.file_exists(csel_tpl % 2):
_problems.append("[savesel] sl7 取消路径失守(应只关窗、文件仍在)")
elif main._confirm_consumer != "":
_problems.append("[savesel] sl8 取消后消费方状态量未清(分派面残留)")
if main.ui.modals.flow.stack._occluded.has(main.ui.modals.confirm_dialog):
if main.ui.flow.stack._occluded.has(main.ui.modals.menu.confirm_dialog):
_problems.append("[savesel] sl7 取消后确认窗遮挡账未清(关侧未走模态栈——wr2)")
del_btn.pressed.emit() # 再开窗走确认路径
await get_tree().process_frame
main.ui.modals.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源)
main.ui.modals.menu.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源)
await get_tree().process_frame
if FileAccess.file_exists(csel_tpl % 2) or main.ui.modals.confirm_dialog.is_open():
if FileAccess.file_exists(csel_tpl % 2) or main.ui.modals.menu.confirm_dialog.is_open():
_problems.append("[savesel] sl7 确认路径失守(应删档+关窗)")
elif main.run_scope.is_adventure or main.ui.screens.start_overlay.visible:
_problems.append("[savesel] sl8 删档确认误触发开局(双常驻接线形态——分派单口失守)")
elif bool(main.ui.modals.save_select_overlay._occupied_flags.get(2, true)):
elif bool(main.ui.modals.menu.save_select_overlay._occupied_flags.get(2, true)):
_problems.append("[savesel] sl7 删后选档面未就地刷新(槽2 应回空态)")
elif main._confirm_consumer != "":
_problems.append("[savesel] sl8 确认后消费方状态量未清(分派面残留)")
main._on_save_select_closed()
await get_tree().process_frame
if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
if main.ui.modals.menu.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
_problems.append("[savesel] 关闭后层未隐藏或开始层未恢复(模态栈单口失守)")
# ---- 设置面段(隔离 store 换注禁碰真实 user://settings.cfg;真实按钮信号路径)----
@@ -297,19 +297,19 @@ func _ready() -> void:
var iso_s = load("res://presentation/settings_store.gd").new("user://smoke_click_probe_settings.cfg")
iso_s.load_settings()
main.settings_store = iso_s
main.ui.modals.settings_overlay.retarget(iso_s)
main.ui.modals.menu.settings_overlay.retarget(iso_s)
var settings_btn: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "设置", "prefix")
if settings_btn == null:
_problems.append("设置入口按钮缺失(开始层 settingsbtn)")
else:
settings_btn.pressed.emit() # 真实按钮信号路径 → _open_settings
await get_tree().process_frame
if not main.ui.modals.settings_overlay.visible or main.ui.screens.start_overlay.visible:
if not main.ui.modals.menu.settings_overlay.visible or main.ui.screens.start_overlay.visible:
_problems.append("设置面未打开(点击路径)")
else:
# 减动效转正双态(卡④ AC③)——M4 适配:拨杆开关(_motion_btn)语义翻转 on=完整/off=快速,
# 状态字随行 _motion_st;查找由文字改成员引用(拨杆无文字)
var motion_btn: Button = main.ui.modals.settings_overlay._motion_btn
var motion_btn: Button = main.ui.modals.menu.settings_overlay._motion_btn
if motion_btn == null:
_problems.append("设置面减动效开关缺失")
else:
@@ -326,7 +326,7 @@ func _ready() -> void:
if UiKit.motion_scale != 1.0:
_problems.append("减动效关未恢复(motion_scale != 1.0)")
# 音量实时生效(卡④ AC①):滑杆 30% → master bus db 与 store 双读
var vol: HSlider = main.ui.modals.settings_overlay._volume
var vol: HSlider = main.ui.modals.menu.settings_overlay._volume
vol.value = 30
await get_tree().process_frame
var master_idx := AudioServer.get_bus_index("Master")
@@ -339,22 +339,22 @@ func _ready() -> void:
# 回归语义随调试浮层发布态退役,申报见迭代 38 实现报告 §8.2)
if main.ui.screens.debug_badge == null or main.ui.screens.debug_badge.visible:
_problems.append("设置面开中调试角标可见(发布态恒隐失守)")
var settings_close: Button = ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "返回", "prefix")
var settings_close: Button = ProbeUiUtil.find_button(main.ui.modals.menu.settings_overlay, "返回", "prefix")
if settings_close == null:
_problems.append("设置面返回按钮缺失")
else:
settings_close.pressed.emit()
await get_tree().process_frame
if main.ui.modals.settings_overlay.visible or not main.ui.screens.start_overlay.visible:
if main.ui.modals.menu.settings_overlay.visible or not main.ui.screens.start_overlay.visible:
_problems.append("设置面关闭后未恢复开始层")
# 旧入口零残留断言(卡④ AC③——测试调整板不含减动效开关;14.5 起载体=调试浮窗)
var legacy_motion = ProbeUiUtil.find_check_button(main.ui.modals.debug_overlay, "减动效")
var legacy_motion = ProbeUiUtil.find_check_button(main.ui.modals.menu.debug_overlay, "减动效")
if legacy_motion != null:
_problems.append("调整板减动效旧入口未退役(设置面唯一入口失守——卡④ AC③)")
# D1 断言(二轮):退出游戏只在一级菜单——开始菜单有、设置面无
if ProbeUiUtil.find_button(main.ui.screens.start_overlay, "退出游戏", "prefix") == null:
_problems.append("开始菜单缺退出游戏按钮(二轮 D1)")
if ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "退出游戏", "prefix") != null:
if ProbeUiUtil.find_button(main.ui.modals.menu.settings_overlay, "退出游戏", "prefix") != null:
_problems.append("设置面仍含退出游戏按钮(二轮 D1——只在一级菜单出现)")
# ---- 生涯页段(入口点击链+关闭回返,真实按钮信号路径;本页只读零写,
# 值断言归冒烟投影段与 ui_struct 结构族,此处只验开合路径与零跳转)----
@@ -362,29 +362,29 @@ func _ready() -> void:
if true:
main._open_career()
await get_tree().process_frame
if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
if not main.ui.modals.menu.career_overlay.visible or main.ui.screens.start_overlay.visible:
_problems.append("生涯页未打开(点击路径)")
else:
# 零跳转契约(提纲 P4 甲):页内除「返回」外零导航钮
for nav in ["营地", "商店", "设置", "退出", "冒险"]:
if ProbeUiUtil.find_button(main.ui.modals.career_overlay, nav, "contains") != null:
if ProbeUiUtil.find_button(main.ui.modals.menu.career_overlay, nav, "contains") != null:
_problems.append("生涯页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav)
# 局内入口断言(提纲 P4 甲):生涯入口只在开始层——战斗屏/地图屏零生涯角标
if main.ui.battle.board_zone.find_child("career_badge", true, false) != null \
or main.ui.modals.level_screen.find_child("career_badge", true, false) != null:
or main.ui.modals.run.level_screen.find_child("career_badge", true, false) != null:
_problems.append("局内屏出现生涯入口件(局内不放生涯入口违约)")
var career_close: Button = ProbeUiUtil.find_button(main.ui.modals.career_overlay, "返回", "prefix")
var career_close: Button = ProbeUiUtil.find_button(main.ui.modals.menu.career_overlay, "返回", "prefix")
if career_close == null:
_problems.append("生涯页返回按钮缺失")
else:
career_close.pressed.emit()
await get_tree().process_frame
if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible:
if main.ui.modals.menu.career_overlay.visible or not main.ui.screens.start_overlay.visible:
_problems.append("生涯页关闭后未恢复开始层")
# 换回全新零档(营地购买态不泄入后续冒险段——确定性;档案残留下次段首清理兜底)
ProbeUiUtil.isolate_save(main, "smoke_click_probe_reset", true, false, true) # 零档换注(继续上局钮已随三入口化撤出——卡② mn8)
main.ui.modals.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支)
main.ui.modals.menu.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
main.ui.modals.menu.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支)
var t1 := Time.get_ticks_msec()
main._start_adventure_from_menu()
await get_tree().process_frame
@@ -396,11 +396,11 @@ func _ready() -> void:
_problems.append("开场行回归失守:局外加成(pct>0)局的开场行应带「(含面板加成)」(wc3)")
# 关卡开始 = 入口选路停点——press_candidate 图上命中路径选入口候选进首战
#(首格可点 AC 口径随之含入口选路一步:菜单点击 → 停点呈现 → pick → 棋盘安装)
if main.ui.modals.level_screen.visible:
if main.ui.modals.run.level_screen.visible:
# 独立性断言:独立屏激活期战斗屏宿主整体隐藏
if main.ui.screens.battle_root.visible:
_problems.append("独立屏激活期战斗屏宿主未隐藏(零透出契约失守)")
main.ui.modals.level_screen.press_candidate(0)
main.ui.modals.run.level_screen.press_candidate(0)
await get_tree().process_frame
await get_tree().process_frame
var t_entry := Time.get_ticks_msec() - t1
@@ -421,21 +421,21 @@ func _ready() -> void:
while not _done and action_budget > 0:
action_budget -= 1
await get_tree().process_frame
if main.ui.modals.node_overlay.visible:
main.ui.modals.node_overlay.press_continue()
if main.ui.modals.run.node_overlay.visible:
main.ui.modals.run.node_overlay.press_continue()
_seals_placed.clear()
continue
if main.ui.modals.curio_overlay.visible:
if main.ui.modals.run.curio_overlay.visible:
# 三选一停点(CurioPickOverlay——奖励节点/关卡完成兑付共用面,须与硬锁态适配,
# 未适配则走到即卡死)。两态分派:result 态(兑付反馈——点继续推进)/
# 待选态(点首件+确认)。真实信号路径全线。〔无替换子态——装配不可变契约。〕
# 三选一停点输入锁定断言(停点态断言面:待选/兑付两态共用此层)。
if not _tested_curio_head and main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty() and String(main.ui.modals.curio_overlay._header).contains("关卡奖励点"):
if not _tested_curio_head and main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty() and String(main.ui.modals.run.curio_overlay._header).contains("关卡奖励点"):
# 停点态判据 = _node_curio_pending(奖励节点三选一停点住此——原条件查
# _curio_pending〔关卡完成三选一态〕与奖励头部互斥、恒不可命中=拦截能力未证病根,
# 卡⑤ d4 转正随修;头部校验串本体零触碰)
_tested_curio_head = true
var ch := String(main.ui.modals.curio_overlay._header)
var ch := String(main.ui.modals.run.curio_overlay._header)
if not ch.contains("冒险牌库") or not ch.contains("属性值提升与补给") or ch.contains("同权重") or ch.contains("同上限") or ch.contains("提升池"):
_problems.append("三选一头部旧话残留或新话缺失(wa4——%s)" % ch.replace("
", "|"))
@@ -444,45 +444,45 @@ func _ready() -> void:
var logtext: String = main.ui.battle.log_view.parsed_text()
if not logtext.contains("关卡奖励点(三选一——选毕推进)") or logtext.contains("内容待定"):
_problems.append("奖励节点战报文案未换代(卡⑤ wr2——旧话残留或新话缺失)")
if not main.ui.modals.flow.locks_input():
if not main.ui.flow.locks_input():
_problems.append("三选一停点打开时输入未锁定")
if main.ui.modals.curio_overlay._continue_btn.visible:
main.ui.modals.curio_overlay.press_continue()
if main.ui.modals.run.curio_overlay._continue_btn.visible:
main.ui.modals.run.curio_overlay.press_continue()
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty():
main.ui.modals.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形)
main.ui.modals.curio_overlay.press_confirm()
main.ui.modals.run.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形)
main.ui.modals.run.curio_overlay.press_confirm()
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty():
main.ui.modals.curio_overlay.press_card(0)
main.ui.modals.curio_overlay.press_confirm()
main.ui.modals.run.curio_overlay.press_card(0)
main.ui.modals.run.curio_overlay.press_confirm()
else:
main.ui.modals.curio_overlay.press_continue() # 停点已消费的残面——继续关屏
main.ui.modals.run.curio_overlay.press_continue() # 停点已消费的残面——继续关屏
_seals_placed.clear()
continue
if main.ui.modals.shop_overlay.visible:
if main.ui.modals.menu.shop_overlay.visible:
# 商店房覆盖层(摆放约束:每终点前一位恰一店,真实路径恒必经一店):零购离开(真实 leave 信号路径)放行选路。
# 分派链序:商店层=选路页上的模态覆盖(停点呈现为
# level_screen+shop 双 visible)——必须先于 level_screen 分派,否则下层候选点击
# 空转烧尽预算(实证:lv2 商店停点双 visible 循环 12000 帧 done=false)。
main.ui.modals.shop_overlay.leave_pressed.emit()
main.ui.modals.menu.shop_overlay.leave_pressed.emit()
_seals_placed.clear()
continue
if main.ui.modals.level_screen.visible:
if main.ui.modals.run.level_screen.visible:
# 节点图选路口(零自动推进停点)——图上命中路径交替选候选(岔路使用行为;布局决策 2 改造)
# 地图屏角标一次性 case:选路前经角标开/关状态面板(层在地图屏之上开合,
# 关闭后地图屏选路态零扰动——P2 甲层级语义)
if not _tested_map_status and not main.ui.modals.status_panel.visible:
if not _tested_map_status and not main.ui.modals.run.status_panel.visible:
_tested_map_status = true
var mpb: Button = main.ui.modals.level_screen.status_badge
var mpb: Button = main.ui.modals.run.level_screen.status_badge
if mpb == null or not mpb.visible or mpb.disabled:
_problems.append("地图屏状态角标缺失/未点亮/被置灰(P1 丁)")
else:
mpb.pressed.emit()
if not main.ui.modals.status_panel.visible or not main.ui.modals.level_screen.visible:
if not main.ui.modals.run.status_panel.visible or not main.ui.modals.run.level_screen.visible:
_problems.append("地图屏角标开状态面板失败或地图屏被误藏(P2 甲层级失守)")
var rbtn2: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix")
var rbtn2: Button = ProbeUiUtil.find_button(main.ui.modals.run.status_panel, "继续", "prefix")
if rbtn2 != null:
rbtn2.pressed.emit()
if main.ui.modals.status_panel.visible or not main.ui.modals.level_screen.visible:
if main.ui.modals.run.status_panel.visible or not main.ui.modals.run.level_screen.visible:
_problems.append("状态面板关闭后地图屏态失守(恢复语义破)")
# 地图屏加成帽换代断言(迭代36 卡A wa2——「特性」→「属性值提升」;帽 Label 常在树;
# 独立旗标——_tested_map_status 在同拍前置块已置位,禁复用其门控)
@@ -490,7 +490,7 @@ func _ready() -> void:
_tested_map_cap = true
var cap_found := false
var ls_labels: Array = []
_collect_labels_by_text(main.ui.modals.level_screen, ls_labels)
_collect_labels_by_text(main.ui.modals.run.level_screen, ls_labels)
for l in ls_labels:
if l.text == "属性值提升":
cap_found = true
@@ -502,16 +502,16 @@ func _ready() -> void:
if main.run_scope.runner != null:
for nd in main.run_scope.runner.nodes():
var nd_id: int = main.run_scope.runner.node_id_of(nd)
if String(nd.get("type", "")) == "reward" and main.ui.modals.level_screen._cand_ids.has(nd_id):
pick = main.ui.modals.level_screen._cand_ids.find(nd_id)
if String(nd.get("type", "")) == "reward" and main.ui.modals.run.level_screen._cand_ids.has(nd_id):
pick = main.ui.modals.run.level_screen._cand_ids.find(nd_id)
break
main.ui.modals.level_screen.press_candidate(pick)
main.ui.modals.run.level_screen.press_candidate(pick)
_route_clicks += 1
_seals_placed.clear()
continue
if main.ui.screens.inter_overlay.visible:
# 大奖励屏(冒险级停点——非末关的关卡完成恰一次):首见确认推进 L2(真实按钮信号路径);再见 = L2 已关卡完成,收口
if not main.ui.modals.flow.locks_input():
if not main.ui.flow.locks_input():
_problems.append("大奖励屏打开时输入未锁定")
_inter_confirms += 1
if _inter_confirms == 1:
@@ -530,23 +530,23 @@ func _ready() -> void:
break
_seals_placed.clear()
continue
if main.ui.modals.end_overlay.is_open() and main.run_scope.settlement_stop_timer.is_armed():
if main.ui.modals.run.end_overlay.is_open() and main.run_scope.settlement_stop_timer.is_armed():
# 战斗胜利总结结算框(真实信号路径——每场普通胜利到达):断言已拍板面后点「下一节点」
if not main.ui.modals.flow.locks_input():
if not main.ui.flow.locks_input():
_problems.append("胜利结算框打开时输入未锁定")
var gp_line: String = "%s %s" % [main.ui.modals.end_overlay._gp_name.text, main.ui.modals.end_overlay._gp_val.text] # 两列账行(审计差距④)——名/值列拼回整串断言
var gp_line: String = "%s %s" % [main.ui.modals.run.end_overlay._gp_name.text, main.ui.modals.run.end_overlay._gp_val.text] # 两列账行(审计差距④)——名/值列拼回整串断言
if not gp_line.contains("GP +%d" % main.run_scope.last_gain_amount) or not gp_line.contains("合计 %d" % int(main.run_scope.runner.gp)):
_problems.append("胜利结算框 GP 增量/总量与钱包实态不符(%s|实态 +%d/%d)" % [gp_line, main.run_scope.last_gain_amount, int(main.run_scope.runner.gp)])
if main.ui.modals.end_overlay.verdict_text() == "":
if main.ui.modals.run.end_overlay.verdict_text() == "":
_problems.append("胜利结算框判定行缺失")
_settlement_confirms += 1
main.ui.modals.end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径
main.ui.modals.run.end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径
await get_tree().process_frame
await get_tree().process_frame
if main.run_scope.settlement_stop_timer.is_armed() or not main.ui.modals.level_screen.visible:
if main.run_scope.settlement_stop_timer.is_armed() or not main.ui.modals.run.level_screen.visible:
_problems.append("胜利结算框「下一节点」未进选路页(卡⑥ AC②)")
continue
if main.ui.modals.end_overlay.is_open():
if main.ui.modals.run.end_overlay.is_open():
# 冒险终局(失败/放弃/打穿)——无单关直入口,见终局即收口(模态断言照旧)
# 三选一链适配:_adventure_mode 仍真且非选路挂起=中途结算面(Boss 胜利
# 总结「继续」后挂三选一→真终局未到)——真实信号路径点继续推进。
@@ -555,13 +555,13 @@ func _ready() -> void:
# (_adventure_mode)在真终局仍 true——会把真终局当中途面点掉返回键(实证)。
var _adv_running: bool = main.run_scope.adventure != null and String(main.run_scope.adventure.state) == "running"
if main.run_scope.is_adventure and _adv_running:
main.ui.modals.end_overlay._btn.pressed.emit()
main.ui.modals.run.end_overlay._btn.pressed.emit()
continue
if not main.ui.modals.flow.locks_input():
if not main.ui.flow.locks_input():
_problems.append("终局 overlay 打开时输入未锁定")
_done = true
print("[click_probe] 冒险终局:%s · 选路口点击 %d 次" % [
main.ui.modals.end_overlay.verdict_text().replace("\n", " "), _route_clicks])
main.ui.modals.run.end_overlay.verdict_text().replace("\n", " "), _route_clicks])
break
if main.run_scope.settlement == null or main.run_scope.runner == null:
continue
@@ -603,36 +603,36 @@ func _ready() -> void:
# ---- 预算耗尽收口(T17 硬锁批适配):选件变强后 probe 可能活过预算不再战败——断点段前提
# (冒险终局面在案)须主动满足。收口驱动:任何停点层逐个推进(复用主循环分支语义)至
# level_screen 选路停点→真实放弃二击→终局面。战斗中则继续 autoplay 打完。
if not _done and not main.ui.modals.end_overlay.is_open():
if not _done and not main.ui.modals.run.end_overlay.is_open():
var close_guard := 8000
var dbg_frame := 0 # T17 演播后排窗:每场胜利 +~400 帧演播等待(旧同帧 flush 零等待)——守卫按多场战斗放大
while close_guard > 0:
close_guard -= 1
await get_tree().process_frame
if main.ui.modals.end_overlay.is_open():
if main.ui.modals.run.end_overlay.is_open():
# 中途结算面(state=running)=点继续推进;真终局(settling)=断点段前提满足,收口。
if main.run_scope.is_adventure and main.run_scope.adventure != null and String(main.run_scope.adventure.state) == "running":
main.ui.modals.end_overlay._btn.pressed.emit()
main.ui.modals.run.end_overlay._btn.pressed.emit()
else:
break
elif main.ui.modals.curio_overlay.visible:
if main.ui.modals.curio_overlay._continue_btn.visible:
main.ui.modals.curio_overlay.press_continue()
elif main.ui.modals.run.curio_overlay.visible:
if main.ui.modals.run.curio_overlay._continue_btn.visible:
main.ui.modals.run.curio_overlay.press_continue()
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty():
main.ui.modals.curio_overlay.press_card(0)
main.ui.modals.curio_overlay.press_confirm()
main.ui.modals.run.curio_overlay.press_card(0)
main.ui.modals.run.curio_overlay.press_confirm()
else:
main.ui.modals.curio_overlay.press_continue()
elif main.ui.modals.node_overlay.visible:
main.ui.modals.node_overlay.press_continue()
elif main.ui.modals.shop_overlay.visible:
main.ui.modals.shop_overlay.leave_pressed.emit()
main.ui.modals.run.curio_overlay.press_continue()
elif main.ui.modals.run.node_overlay.visible:
main.ui.modals.run.node_overlay.press_continue()
elif main.ui.modals.menu.shop_overlay.visible:
main.ui.modals.menu.shop_overlay.leave_pressed.emit()
elif main.ui.screens.inter_overlay.visible:
var next_btn2: Button = ProbeUiUtil.find_button(main.ui.screens.inter_overlay, "进入下一层", "prefix")
if next_btn2 != null:
next_btn2.pressed.emit()
elif main.ui.modals.level_screen.visible:
var abandon_btn: Button = ProbeUiUtil.find_button(main.ui.modals.level_screen, "放弃", "prefix")
elif main.ui.modals.run.level_screen.visible:
var abandon_btn: Button = ProbeUiUtil.find_button(main.ui.modals.run.level_screen, "放弃", "prefix")
if abandon_btn != null:
_closed_via_abandon = true
abandon_btn.pressed.emit()
@@ -660,12 +660,12 @@ func _ready() -> void:
str(((bpf as Dictionary).get("bonus_deck", []) as Array).size()),
str((bpf as Dictionary).get("bonus_pending"))])
_problems.append("断点段:战败结算后断点未清(T3 三结局清断点失守)")
if not main.ui.modals.end_overlay.is_open():
if not main.ui.modals.run.end_overlay.is_open():
_problems.append("断点段:战败后未出冒险终局面")
if main.ui.modals.end_overlay._btn == null:
if main.ui.modals.run.end_overlay._btn == null:
_problems.append("断点段:终局面返回按钮缺失")
else:
main.ui.modals.end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄)
main.ui.modals.run.end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄)
await get_tree().process_frame
if not main.ui.screens.start_overlay.visible:
# 豁免:放弃路径收口时终局「返回」后仍挂三选一/关卡推进残留态(停点链),
@@ -687,19 +687,19 @@ func _ready() -> void:
var bal_gate: int = main._camp_data()["balance"]
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
await get_tree().process_frame
if not main.ui.modals.confirm_dialog.is_open():
if not main.ui.modals.menu.confirm_dialog.is_open():
_problems.append("大门无断点:确认弹窗未开(点大门应先出确认不直接进冒险)〔营地图批 V3〕")
if main.run_scope.is_adventure:
_problems.append("大门无断点:未确认即进冒险〔营地图批 V3〕")
if main.ui.modals.confirm_dialog._secondary_btn.visible:
if main.ui.modals.menu.confirm_dialog._secondary_btn.visible:
_problems.append("大门无断点:次钮应隐藏(两出口形态)〔营地图批 V3〕")
main.ui.modals.confirm_dialog.cancelled.emit()
main.ui.modals.menu.confirm_dialog.cancelled.emit()
await get_tree().process_frame
if not main.ui.modals.camp.scene.visible or main.run_scope.is_adventure or main._camp_data()["balance"] != bal_gate:
_problems.append("大门取消:未无变更退回营地(写口零调用/营地根 visible 失守)〔营地图批 V3〕")
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
await get_tree().process_frame
main.ui.modals.confirm_dialog.confirmed.emit() # 「是」→开始新冒险
main.ui.modals.menu.confirm_dialog.confirmed.emit() # 「是」→开始新冒险
await get_tree().process_frame
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
_problems.append("大门确认:冒险未发起或营地场景屏未隐藏〔营地图批 V3〕〔营地图批 B1〕")
@@ -714,9 +714,9 @@ func _ready() -> void:
await get_tree().process_frame
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
await get_tree().process_frame
if not main.ui.modals.confirm_dialog.is_open() or not main.ui.modals.confirm_dialog._secondary_btn.visible:
if not main.ui.modals.menu.confirm_dialog.is_open() or not main.ui.modals.menu.confirm_dialog._secondary_btn.visible:
_problems.append("大门有断点:三出口形态失守(次钮应现形)〔营地图批 V3〕")
main.ui.modals.confirm_dialog.secondary_confirmed.emit() # 「开始新冒险」出口
main.ui.modals.menu.confirm_dialog.secondary_confirmed.emit() # 「开始新冒险」出口
await get_tree().process_frame
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
_problems.append("大门有断点次确认:冒险未发起或营地未隐藏〔营地图批 V3〕")
@@ -736,8 +736,8 @@ func _ready() -> void:
if FileAccess.file_exists(p_ol):
DirAccess.remove_absolute(p_ol)
ProbeUiUtil.isolate_save(main, "smoke_click_probe_openline", true, false, true)
main.ui.modals.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑)
main.ui.modals.debug_overlay.seed_edit.text = "42"
main.ui.modals.menu.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑)
main.ui.modals.menu.debug_overlay.seed_edit.text = "42"
main._start_adventure_from_menu()
var ol_l1_wait := 0
while ol_l1_wait < 24 and String(main.ui.battle.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
@@ -822,14 +822,14 @@ func _run_modal_probes_once() -> void:
if not _tested_legend:
_tested_legend = true
main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit()
if not main.ui.modals.legend_overlay.is_open():
if not main.ui.modals.run.legend_overlay.is_open():
_problems.append("图例打开失败")
if main.ui.modals.end_overlay.is_open() or main.ui.screens.start_overlay.visible:
if main.ui.modals.run.end_overlay.is_open() or main.ui.screens.start_overlay.visible:
_problems.append("图例打开时上方遮挡层未隐藏(缺陷⑤复发)")
if not main.ui.modals.flow.locks_input():
if not main.ui.flow.locks_input():
_problems.append("图例打开时棋盘输入未锁定")
var legend_has_seal := false
for e in main.ui.modals.legend_overlay.ENTRIES:
for e in main.ui.modals.run.legend_overlay.ENTRIES:
if String(e[1]) == "封印":
legend_has_seal = true
# 图例封印行换代断言(迭代36 卡A wa5——真实 AP 条图例钮开层路径)
@@ -838,13 +838,13 @@ func _run_modal_probes_once() -> void:
", "|"))
if not legend_has_seal:
_problems.append("图例缺『封印』格态条目(格态图例基线)")
main.ui.modals.legend_overlay.close()
if main.ui.modals.legend_overlay.is_open() or main.ui.modals.flow.locks_input():
main.ui.modals.run.legend_overlay.close()
if main.ui.modals.run.legend_overlay.is_open() or main.ui.flow.locks_input():
_problems.append("图例关闭后未恢复输入/可见性")
# ②/②b 明细回看模态无探针(M9/P-C7——冒险中回看归「生涯」,06 登记)
# ⑤ 状态面板(P1 丁角标真实信号路径 + P2 甲模态语义 + P1 守卫 + R3 锁恢复):
# 角标打开 → 只读层可见+输入锁定+余额徽非空 → 「继续」关闭恢复;图例开着 → 角标置灰且点击被守卫兜住
if not _tested_status and not main.ui.modals.legend_overlay.is_open() and not main.ui.modals.status_panel.visible:
if not _tested_status and not main.ui.modals.run.legend_overlay.is_open() and not main.ui.modals.run.status_panel.visible:
_tested_status = true
var pbtn: Button = main.ui.battle.board_zone.status_badge
if pbtn == null or not pbtn.visible or pbtn.disabled:
@@ -852,17 +852,17 @@ func _run_modal_probes_once() -> void:
% [str(pbtn != null and pbtn.visible), str(pbtn != null and pbtn.disabled)])
else:
pbtn.pressed.emit() # 真实按钮信号路径 → 组合根 _on_status_badge_pressed
if not main.ui.modals.status_panel.visible:
if not main.ui.modals.run.status_panel.visible:
_problems.append("状态面板未随角标打开(组合根接线失守)")
if not main.ui.modals.flow.locks_input():
if not main.ui.flow.locks_input():
_problems.append("状态面板打开时输入未锁定(R3)")
var bal_txt: String = main.ui.modals.status_panel._balance_badge.value_label.text
var bal_txt: String = main.ui.modals.run.status_panel._balance_badge.value_label.text
if not bal_txt.begins_with("钥石 "):
_problems.append("状态面板余额徽投影缺失(%s)" % bal_txt)
# ---- 本局信息面板换代断言(迭代36 卡③——真实入口三断言)----
# ① 面板子树零旧段文案(跨关概览退役零残留)
var sv_labels: Array = []
_collect_labels_by_text(main.ui.modals.status_panel, sv_labels)
_collect_labels_by_text(main.ui.modals.run.status_panel, sv_labels)
for l in sv_labels:
if String(l.text).contains("道具工坊") or String(l.text).contains("天赋树"):
_problems.append("状态面板子树残留旧段文案「%s」(sv6①——零旧段失守)" % String(l.text))
@@ -874,8 +874,8 @@ func _run_modal_probes_once() -> void:
var attr_expect := "生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d" % [
int(rnr.hero_hp), rnr.effective_max_hp(), rnr.effective_atk(),
rnr.effective_ac(), int(rnr.gp)]
if String(main.ui.modals.status_panel._attrs_label.text) != attr_expect:
_problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.ui.modals.status_panel._attrs_label.text])
if String(main.ui.modals.run.status_panel._attrs_label.text) != attr_expect:
_problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.ui.modals.run.status_panel._attrs_label.text])
# ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立 oracle)
var sd: Dictionary = main._status_data()
var keys := ["balance", "hero_hp", "max_hp", "atk", "ac", "gp", "curios", "feats"]
@@ -899,22 +899,22 @@ func _run_modal_probes_once() -> void:
var feats_expect: Array = load("res://presentation/hud_presenter.gd").feat_holdings_lines(main.db, rnr.feats_held())
if str((sd as Dictionary).get("feats", [])) != str(feats_expect):
_problems.append("_status_data().feats 对拍 feat_holdings_lines 失守(sv7)")
var resume_btn: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix")
var resume_btn: Button = ProbeUiUtil.find_button(main.ui.modals.run.status_panel, "继续", "prefix")
if resume_btn == null:
_problems.append("状态面板关闭钮缺失(「继续」)")
else:
resume_btn.pressed.emit()
if main.ui.modals.status_panel.visible or main.ui.modals.flow.locks_input():
if main.ui.modals.run.status_panel.visible or main.ui.flow.locks_input():
_problems.append("状态面板关闭后未恢复(visible=%s locked=%s——R3 残留)"
% [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())])
% [str(main.ui.modals.run.status_panel.visible), str(main.ui.flow.locks_input())])
# P1 守卫:图例开着 → 两处入口角标置灰;置灰态点击被处理器兜住(探针 emit 绕过 disabled)
main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit()
if not main.ui.battle.board_zone.status_badge.disabled:
_problems.append("图例开着时战斗屏状态角标未置灰(P1 守卫失守)")
main.ui.battle.board_zone.status_badge.pressed.emit()
if main.ui.modals.status_panel.visible:
if main.ui.modals.run.status_panel.visible:
_problems.append("置灰态仍可开状态面板(守卫兜底失守)")
main.ui.modals.legend_overlay.close()
main.ui.modals.run.legend_overlay.close()
# ③ tooltip(状态效果槽悬停——来源+语义+剩余):有槽可见时触发
if not _tested_tooltip:
var row = main.ui.battle.stage.hero_standee.slot_row # 状态 tooltip 载体(B 区站桩)
@@ -1045,7 +1045,7 @@ func _locktest_tick() -> void:
if settlement == null or settlement.minesweeper.current_board() == null:
return
if _locktest_state == 0:
if main.ui.screens.row_pacer.pending_rows().is_empty() or main.ui.modals.flow.locks_input():
if main.ui.screens.row_pacer.pending_rows().is_empty() or main.ui.flow.locks_input():
return
if String(settlement.battle_result) != "" or String(settlement.action_economy.phase) != "Active":
return
@@ -1067,7 +1067,7 @@ func _locktest_tick() -> void:
return
_locktest_state = 1
elif _locktest_state == 1:
if main.ui.modals.curio_overlay.visible or main.ui.screens.inter_overlay.visible or main.ui.modals.end_overlay.is_open() \
if main.ui.modals.run.curio_overlay.visible or main.ui.screens.inter_overlay.visible or main.ui.modals.run.end_overlay.is_open() \
or String(settlement.battle_result) != "":
_locktest_state = 2 # 前提消失守卫:终结演播竞态——静默弃测
return