## 组合根 Esc 流程承接件(产品码首个键盘输入入口——main._unhandled_input 同名薄委托落地): ## Esc 四态路由(契约 9 判据单点=ModalStack 登记册现开集合,禁手枚名单)+三出口动线。 ## 静态承接件零成员态(仿 main_overlay_flow 形态——main 侧留同名薄委托,探针 main.X 路径零改动)。 class_name MainEscFlow extends RefCounted ## 营地语境判据(Q1 甲——场景根 visible;子界面开态抑制由契约 9① 现开集合承载,不并入本谓词) static func is_camp_context(root: Variant) -> bool: return root.ui.modals.camp.scene != null and root.ui.modals.camp.scene.visible ## Esc 四态优先序(契约 9——①先于②:设置开在浮窗之上态必须先命中「无反馈」,浮窗不隔层关闭): ## ①除浮窗外任一登记模态开→无反馈(子界面开态/设置开在浮窗之上)→②浮窗开→close(同键单义切换, ## Q2 甲)→③营地语境→open→④非营地→无反馈(选档/冒险/菜单语境 Esc 零响应——契约 2) static func on_esc_input(root: Variant) -> void: var esc: Control = root.ui.modals.camp.esc_overlay if _other_modal_open(root, esc): return # ① 除浮窗外任一登记模态开 → 无反馈 if esc.visible: root.ui.modals.flow.close(esc) # ② 再按 Esc 关窗 return if is_camp_context(root): root.ui.modals.flow.open(esc) # ③ 营地语境唤起 # ④ 非营地语境 → 无反馈 # 登记册现开集合查询(除浮窗与营地场景根本身——场景根=语境层非抑制对象; # 迭代登记册遍历=契约 9「判据单点」 sanctioned 读法,禁按层名手枚名单) static func _other_modal_open(root: Variant, esc: Control) -> bool: for m in root.ui.modals.flow.stack._list: if m != esc and m != root.ui.modals.camp.scene and is_instance_valid(m) and m.visible: return true return false ## 返回主菜单动线(契约 4):首步 close 浮窗(缺此步浮窗残留主菜单上方)→营地与选档层同收+开始层恢复 static func _on_return_to_menu(root: Variant) -> void: root.ui.modals.flow.close(root.ui.modals.camp.esc_overlay) root._camp_pres.on_camp_closed() # 关层收闭单口(卡④ T3——营地层与选档层同收+开始层恢复) ## 设置出口(浮窗登记在设置层之下——open 只隐上方层,浮窗由树序遮挡非属性隐藏;关后重现=栈语义) static func _on_settings(root: Variant) -> void: root.ui.modals.flow.open(root.ui.modals.settings_overlay) ## 退出出口(_quit_game 原样复用——settings_store.save_settings+quit,零语义副作用) static func _on_quit(root: Variant) -> void: root._quit_game()