iter44-卡1:卡①(组合根注册面批): ui_registry+三域容器建件·main.gd 49→15·全消费面改址main.ui.*·四闸主证行为零变化
This commit is contained in:
1 parent
14054d9dfb
commit
d2b36e59cb
41 files changed
+1073
-1070
No files matched your search
+76
-264
@@ -1,7 +1,8 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 组合根:唯一的模式流转与 core↔view 接线处(垂直切片验证:线性第一关承载元进度循环)。
|
||||
## db.hero 运行期只读,加成经 runner.set_hero_override / debug_session 生效值入战斗。
|
||||
## 禁链式穿透 settlement.*(相位读取只走 presentation_snapshot);模态开合只走 _open_modal/_close_modal。
|
||||
## 禁链式穿透 settlement.*(相位读取只走 presentation_snapshot);模态开合只走 ui.modals.flow(注册面编排口)。
|
||||
## 界面句柄与层清单在 ui 注册面(ui_registry.gd)——屏壳/战区/模态三域句柄与 LAYER_CONTRACT 单点宿主。
|
||||
## 局态全在 run_scope.gd 节点,禁在屏/视图侧持有局态引用;autoload 节点路径常量单点在 AUTOLOAD_PATH_*。
|
||||
extends Control
|
||||
|
||||
@@ -15,10 +16,8 @@ const AUTOLOAD_PATH_META := "/root/MetaProgression"
|
||||
|
||||
const UiAssembler := preload("res://presentation/ui_assembler.gd")
|
||||
const Narrator := preload("res://presentation/battle_narrator.gd")
|
||||
const ModalStack := preload("res://presentation/modal_stack.gd")
|
||||
const TuneSimController := preload("res://presentation/tune_sim_controller.gd")
|
||||
const DebugSessionScript := preload("res://presentation/debug_session.gd")
|
||||
const RowPacer := preload("res://presentation/row_pacer.gd") # 结算行排播泵
|
||||
const TreasureInteractController := preload("res://presentation/treasure_interact_controller.gd") # 秘宝两步交互
|
||||
const CurioInteractController := preload("res://presentation/curio_interact_controller.gd") # 三选一与替换子交互
|
||||
const CampShopPresenter := preload("res://presentation/camp_shop_presenter.gd") # 营地/商店投影
|
||||
@@ -31,33 +30,6 @@ const MenuScreenScene := preload("res://presentation/scenes/menu_screen.tscn")
|
||||
const CampScreenScene := preload("res://presentation/scenes/camp_screen.tscn")
|
||||
const AdventureScreenScene := preload("res://presentation/scenes/adventure_screen.tscn")
|
||||
|
||||
# ==================== 视觉层合同(唯一事实源——树序=登记序=z 序;改序只改此表) ====================
|
||||
# kind: shell=屏壳容器(成员随壳不占独立树位;level_screen 例外另占登记位)/modal=模态层(入登记册,
|
||||
# tags 随登记)/state=全屏态(占树位不入登记册)/node=非视觉节点(占树位不入登记册)。
|
||||
# host=宿住屏壳(树序随宿主、登记序按本合同);shell=模态成员的屏壳(成员与屏壳同槽:屏壳占该树位、
|
||||
# 成员占该登记位)。落位=_apply_layer_contract(全仓唯一层序落位点,add_child 之后统一排序+登记)。
|
||||
const LAYER_CONTRACT: Array[Dictionary] = [
|
||||
{"key": "adventure_screen", "kind": "shell"},
|
||||
{"key": "menu_screen", "kind": "shell"},
|
||||
{"key": "row_pacer", "kind": "node"},
|
||||
{"key": "legend_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "node_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "level_screen", "kind": "modal", "tags": [ModalStack.TAG_SCREEN_STATE], "host": "adventure_screen"},
|
||||
{"key": "curio_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "end_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "shop_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "camp_overlay", "kind": "modal", "tags": [], "shell": "camp_screen"},
|
||||
{"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]},
|
||||
{"key": "debug_badge", "kind": "float"}, # 调试入口全局浮层——末位=最顶绘制序;
|
||||
# float 类不注册 ModalStack(open() 遮挡记账只隐藏登记层)→ 任何 UI 打开都不遮它
|
||||
]
|
||||
|
||||
var db
|
||||
var run_scope = null # 会话状态承载(run_scope.gd;_ready 即建闲时态,永非 null)
|
||||
# SaveManager/MetaProgression 升格 autoload——成员名保留为「autoload 读视图」
|
||||
@@ -71,99 +43,24 @@ var meta: MetaProgressionCore:
|
||||
return get_node(AUTOLOAD_PATH_META)
|
||||
var logger = null # 试玩日志器(JSONL 持久化 + 卡死诊断转储——呈现层组件)
|
||||
var _debug_entry := false # 调试会话门控(缺省 false=发布态:角标恒隐、浮窗无 UI 开启口;沿革查 git log)
|
||||
var _seed_edit: LineEdit
|
||||
var confirm_dialog: ConfirmDialog # 通用确认弹窗(消费方编排——现行消费方=选档删档确认;新冒险互斥确认已随卡②主菜单撤出,归卡④大门)
|
||||
var ui: UiRegistry # 界面注册面(ui_registry.gd——界面句柄/层序合同/模态编排句柄的单点宿主)
|
||||
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
|
||||
var _inter_overlay: Control
|
||||
var _inter_label: Label
|
||||
var level_screen: LevelMapScreen # 关卡画面句柄(指向 adventure_screen.level_screen)
|
||||
var _battle_root: Control # 战斗屏宿主句柄(指向 adventure_screen.battle_root)
|
||||
|
||||
# 调试模式编排(单场/连续两层——状态机与战斗生命周期归 debug_session.gd,本类只接线与出 UI)
|
||||
var _debug = null
|
||||
|
||||
# 视图(views/)——battle.md 三区(节点链进度
|
||||
# 由关卡画面本体选路覆盖层承载)
|
||||
var stage: BattleStageView # B 区(含 log_view 明细带)——句柄指向 adventure_screen
|
||||
var log_view: BattleLogView
|
||||
var info_panel: HeroInfoPanelView # C 区(攻击/AC 数据框+两预留区)
|
||||
var board_zone: BoardZoneView # D 区(含 ap_bar/board_view/toast/fx)
|
||||
var board_view: BoardView # 探针兼容别名(= board_zone.board_view)
|
||||
var ap_bar: ApBarView # 探针兼容别名(= board_zone.ap_bar)
|
||||
var settlement_fx: SettlementFxView # 结算演出覆盖层(跨区弹道/光带——A–D 之上、模态之下)
|
||||
|
||||
# 三屏壳实例(屏成员经本类句柄暴露;探针挂载假设 main.X 一级路径)
|
||||
var _menu_screen = null # MenuScreen(开始菜单屏壳)
|
||||
var _camp_screen = null # CampScreen(营地屏壳)
|
||||
var _adventure_screen = null # AdventureScreen(冒险屏壳——内含关卡地图/战斗两容器)
|
||||
|
||||
# 拆件后的组合根持有面(职责见各类头注;装配见 _build_ui)
|
||||
var _row_pacer = null # 结算行排播泵(row_pacer.gd——本类只入队/排空/读队列)
|
||||
var _treasure_ui = null # 秘宝两步交互(treasure_interact_controller.gd——本类只接线与转发意图)
|
||||
var _bonus = null # 三选一与替换子交互(curio_interact_controller.gd——本类只接线与回调注入)
|
||||
var _camp_pres = null # 营地/商店投影(camp_shop_presenter.gd——本类只接线、探针缝保缝转发)
|
||||
|
||||
# 排播队列只读委托面(探针/测试读 main._row_queue——与 RowPacer 队列共享同一 Array 引用)
|
||||
var _row_queue: Array[Variant]:
|
||||
get:
|
||||
return _row_pacer.pending_rows()
|
||||
|
||||
# 模态 overlay(z 序 = 登记序;pattern 6 层级纪律)
|
||||
var legend_overlay: LegendOverlay
|
||||
var node_overlay: NodeOverlay
|
||||
var shop_overlay: ShopOverlay # 商店覆盖层(商店房节点兑付呈现)
|
||||
var camp_overlay: CampOverlay # 营地覆盖层句柄(指向 camp_screen.camp_overlay)
|
||||
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 不遮挡)
|
||||
|
||||
var _modal_flow = null # 模态编排纯逻辑类(modal_flow.gd——依赖仅 ModalStack+查询 Callable)
|
||||
var _modals = null # 模态层级状态机(=_modal_flow.stack——探针 _list 登记序断言缝保留)
|
||||
var _tune_sim = null # 调试浮窗批量模拟控制器(tune_sim_controller.gd)
|
||||
|
||||
# 结算框挂起态属性化(探针/冒烟直读写接缝语义零变化——读=armed,写=arm/disarm;计时器随会话住
|
||||
# run_scope.settlement_stop_timer)
|
||||
var _settlement_stop_pending: bool:
|
||||
get:
|
||||
return run_scope.settlement_stop_timer.is_armed() if run_scope != null else false
|
||||
set(v):
|
||||
if run_scope == null:
|
||||
return
|
||||
if v:
|
||||
run_scope.settlement_stop_timer.arm()
|
||||
else:
|
||||
run_scope.settlement_stop_timer.disarm()
|
||||
|
||||
# 锁期点击重放(v3.1):延迟一帧重入唯一动作通道——此刻队列空锁已解,按普通动作受理
|
||||
#(局面在演播期无变化:逻辑先行、演播只是回放——点击语义保真;_row_queue=RowPacer 委托面,
|
||||
# 排播泵本体在 row_pacer.gd)。
|
||||
var _deferred_modal: Control: # 演播后开窗挂起只读视图(t17_defer_probe 挂起态断言缝;本体归 _modal_flow)
|
||||
get:
|
||||
return _modal_flow._deferred if _modal_flow != null else null
|
||||
|
||||
|
||||
# 演播后排窗(伤害过程演完才切结算窗口):排播队列在播=挂起,演播完边沿
|
||||
# (_on_pump_ended 派发)再开——旧路径 _open_modal 前置 flush 会把击杀轮的伤害/死亡演出
|
||||
# 同帧倒掉(「直接跳战斗胜利」缺陷本体)。队列空=立即开(行为同旧路径)。
|
||||
func _open_modal_after_playback(m: Control) -> void:
|
||||
_modal_flow.open_after_playback(m) # 演播后排窗挂起编排归 ModalFlow
|
||||
|
||||
|
||||
# 演播完边沿派发(row_pacer.pump_ended 注入口——先开挂起 modal,后锁期点击重放):
|
||||
func _on_pump_ended() -> void:
|
||||
_modal_flow.pump_ended() # 派发编排归 ModalFlow(重放经注入 Callable 回本类)
|
||||
|
||||
|
||||
func _replay_locked_click() -> void:
|
||||
if run_scope.locked_click.is_empty() or not _row_queue.is_empty():
|
||||
if run_scope.locked_click.is_empty() or not ui.screens.row_pacer.pending_rows().is_empty():
|
||||
return
|
||||
var click: Dictionary[Variant, Variant] = run_scope.locked_click
|
||||
run_scope.locked_click = {}
|
||||
@@ -173,7 +70,7 @@ func _replay_locked_click() -> void:
|
||||
# 会话生命周期:_ready 建闲时态(永非 null——全组合根零空判责),
|
||||
# 开局/续玩/调试两层各建新会话,旧会话整节点 queue_free()(帧末回收;新节点先挂树无真空窗)。
|
||||
func _begin_session(adventure_session: bool) -> void:
|
||||
_row_pacer.flush() # 会话更替即清队(待播行不住局态——机制保证,不靠时序约定)
|
||||
ui.screens.row_pacer.flush() # 会话更替即清队(待播行不住局态——机制保证,不靠时序约定)
|
||||
if run_scope != null:
|
||||
run_scope.queue_free()
|
||||
run_scope = RunScope.new()
|
||||
@@ -194,10 +91,11 @@ func _ready() -> void:
|
||||
settings_store = SettingsStore.new()
|
||||
settings_store.load_settings()
|
||||
settings_store.apply()
|
||||
ui = UiRegistry.new() # 注册面先于装配建(_build_ui 八步向容器登记——装配时序零变化)
|
||||
_build_ui()
|
||||
_begin_session(false) # 闲时会话(gp_ledger 等探针注入面自 _ready 起恒在——ui_probe 合成段时序)
|
||||
_start_overlay.visible = true
|
||||
debug_badge.visible = _debug_entry # 浮层角标可见性须门控落定后赋值(构建/连接在 _build_ui);_debug_entry 缺省 false=发布态恒隐
|
||||
ui.screens.start_overlay.visible = true
|
||||
ui.screens.debug_badge.visible = _debug_entry # 浮层角标可见性须门控落定后赋值(构建/连接在 _build_ui);_debug_entry 缺省 false=发布态恒隐
|
||||
# boot 菜单优先——启动不自动开浮窗(开始屏不得被调试面板盖住);
|
||||
# 调试入口=UI 最顶层全局浮层角标(LAYER_CONTRACT 末位 float 类,任何 UI 不遮挡)
|
||||
|
||||
@@ -206,94 +104,13 @@ func _ready() -> void:
|
||||
|
||||
func _build_ui() -> void:
|
||||
MainUiBuild.build(self)
|
||||
debug_overlay.diagnose_requested.connect(_on_diagnose_pressed) # 诊断转储入口
|
||||
ui.modals.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed) # 诊断转储入口
|
||||
|
||||
|
||||
func _fatal_screen(msg: String) -> void:
|
||||
MainUiBuild.fatal_screen(self, msg)
|
||||
|
||||
|
||||
# ==================== 模态 overlay 层级管理(pattern 6 / 缺陷⑤——状态机归 modal_stack.gd) ====================
|
||||
|
||||
func _register_modal(m: Control, tags: Array = []) -> void:
|
||||
# 撑满宿主(Control 默认锚 = 0×0——全屏变暗层与居中布局整体失效;route 选路层预览截图实证后
|
||||
# 统一收口:六个模态同病,探针经信号直发绕过真实点击从未暴露。anchors_and_offsets 而非 anchors
|
||||
# ——后者会把控件重置回最小内容尺寸,ui_assembler 头注同款纪律)
|
||||
m.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_modal_flow.register(m, tags) # 排除组随层登记(non_locking=调试浮窗/screen_state=关卡地图屏)
|
||||
|
||||
|
||||
# 合同层寻址(key→节点;纯查表不含层序语义——层序唯一事实源=LAYER_CONTRACT)
|
||||
func _layer_node(key: String) -> Node:
|
||||
match key:
|
||||
"adventure_screen": return _adventure_screen
|
||||
"menu_screen": return _menu_screen
|
||||
"row_pacer": return _row_pacer
|
||||
"legend_overlay": return legend_overlay
|
||||
"node_overlay": return node_overlay
|
||||
"level_screen": return level_screen
|
||||
"curio_overlay": return curio_overlay
|
||||
"end_overlay": return _end_overlay
|
||||
"shop_overlay": return shop_overlay
|
||||
"camp_screen": return _camp_screen
|
||||
"camp_overlay": return camp_overlay
|
||||
"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
|
||||
"debug_badge": return debug_badge
|
||||
return null
|
||||
|
||||
|
||||
# 合同装配(_build_ui 尾调用):按 LAYER_CONTRACT 序统一落位——直接子层 move_child 到合同槽、
|
||||
# 模态层按合同序登记;宿住层(host)树序随宿主只登记;成员层(shell)与屏壳同槽(屏壳占树位、成员占登记位)。
|
||||
# 本函数是全仓唯一层序落位点(move_child 单口)——改层序只改 LAYER_CONTRACT。
|
||||
func _apply_layer_contract() -> void:
|
||||
var slot := 0
|
||||
for entry: Dictionary in LAYER_CONTRACT:
|
||||
var tags: Array[Variant] = entry.get("tags", [])
|
||||
if entry.has("shell"):
|
||||
var shell: Node = _layer_node(String(entry["shell"]))
|
||||
var member: Node = _layer_node(String(entry["key"]))
|
||||
if shell != null:
|
||||
move_child(shell, slot)
|
||||
slot += 1
|
||||
if member != null:
|
||||
_register_modal(member as Control, tags)
|
||||
else:
|
||||
push_error("[合同装配] 层缺失: %s" % String(entry["key"]))
|
||||
continue
|
||||
var node: Node = _layer_node(String(entry["key"]))
|
||||
if node == null:
|
||||
push_error("[合同装配] 层缺失: %s" % String(entry["key"]))
|
||||
continue
|
||||
if entry.has("host"):
|
||||
_register_modal(node as Control, tags)
|
||||
continue
|
||||
move_child(node, slot)
|
||||
slot += 1
|
||||
if String(entry["kind"]) == "modal":
|
||||
_register_modal(node as Control, tags)
|
||||
|
||||
|
||||
func _open_modal(m: Control) -> void:
|
||||
_modal_flow.open(m) # 挂起兜底/排播排空/遮挡记账/开侧同步编排归 ModalFlow
|
||||
|
||||
func _close_modal(m: Control) -> void:
|
||||
_modal_flow.close(m) # 关闭恢复+关侧同步归 ModalFlow(同上)
|
||||
|
||||
func _on_modal_closed(m: Control) -> void:
|
||||
_close_modal(m)
|
||||
|
||||
|
||||
func _input_locked() -> bool:
|
||||
# 模态/非战斗期棋盘输入锁(判据编排归 ModalFlow.locks_input:互斥域随层登记
|
||||
# [non_locking=调试浮窗]+ 三例外经查询 Callable 注入;本函数留探针缝)
|
||||
return _modal_flow.locks_input()
|
||||
|
||||
# ==================== 冒险模式入口(唯一试玩入口)====================
|
||||
|
||||
func _start_adventure_from_menu() -> void:
|
||||
@@ -301,7 +118,7 @@ func _start_adventure_from_menu() -> void:
|
||||
# 弹窗本体与两分支逻辑归卡④大门承接)。本函数为探针直调中间态保留(mn10——冒险段探针
|
||||
# 直调位点逐个登记实现报告「已知中间态」;卡④到位后由大门两分支换代)。无激活态写盘拒绝
|
||||
# 由卡① sv3 承接(结算不落盘)。
|
||||
_start_overlay.visible = false
|
||||
ui.screens.start_overlay.visible = false
|
||||
_start_adventure()
|
||||
|
||||
|
||||
@@ -309,16 +126,16 @@ func _start_adventure_from_menu() -> void:
|
||||
#(确认才删、取消只关窗——裁决 3)。"new_adventure" 消费方已随卡② 主菜单确认链摘除退役
|
||||
#(mn6/V5——主菜单零确认残留),分派机制保留(卡④ 到位接入第三消费方不返工)。
|
||||
func _on_confirm_accepted() -> void:
|
||||
_close_modal(confirm_dialog) # 模态栈单口
|
||||
ui.modals.flow.close(ui.modals.confirm_dialog) # 模态栈单口
|
||||
match _confirm_consumer:
|
||||
"delete_slot":
|
||||
save_manager.slots.delete_slot(_confirm_consumer_slot)
|
||||
save_select_overlay.open_save_select(HudPresenter.save_select_data(save_manager)) # 就地刷新(该槽回空态)
|
||||
ui.modals.save_select_overlay.open_save_select(HudPresenter.save_select_data(save_manager)) # 就地刷新(该槽回空态)
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
func _on_confirm_declined() -> void:
|
||||
_close_modal(confirm_dialog) # 只关窗(取消无状态变更——裁决 3:取消不删);关侧恢复归 ModalFlow
|
||||
ui.modals.flow.close(ui.modals.confirm_dialog) # 只关窗(取消无状态变更——裁决 3:取消不删);关侧恢复归 ModalFlow
|
||||
_confirm_consumer = ""
|
||||
|
||||
|
||||
@@ -334,16 +151,16 @@ func _resume_from_menu() -> void:
|
||||
if not save_manager.has_breakpoint():
|
||||
return
|
||||
var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot()
|
||||
_close_modal(debug_overlay) # 开冒险路径显式关浮窗(续玩同口径)
|
||||
_start_overlay.visible = false
|
||||
ui.modals.flow.close(ui.modals.debug_overlay) # 开冒险路径显式关浮窗(续玩同口径)
|
||||
ui.screens.start_overlay.visible = false
|
||||
_begin_session(true)
|
||||
_wire_adventure()
|
||||
run_scope.level_active = true
|
||||
_sync_status_entry() # 续玩恢复点亮状态面板入口角标
|
||||
level_screen.set_abandon_available(true)
|
||||
log_view.clear()
|
||||
ui.modals.level_screen.set_abandon_available(true)
|
||||
ui.battle.log_view.clear()
|
||||
logger.start_session(int(bp.get("master_seed", 0)))
|
||||
run_scope.adventure.set_hero_override(debug_overlay.atk_pct(), debug_overlay.hp_pct())
|
||||
run_scope.adventure.set_hero_override(ui.modals.debug_overlay.atk_pct(), ui.modals.debug_overlay.hp_pct())
|
||||
# 同调三键随续玩下传(build_adventure_params 统一构造,与开新局同源同口径)
|
||||
if not run_scope.adventure.resume_adventure(bp, build_adventure_params(save_manager, meta,
|
||||
{"treasure_unlocked": _treasure_ui.unlocked_set()})):
|
||||
@@ -379,16 +196,16 @@ static func build_adventure_params(save_manager_ref: SaveManagerCore, meta_ref:
|
||||
# 冒险模式(三关连探——AdventureRunner 编排;转发信号签名与 runner 一致,接线照原样对接;
|
||||
# 信号连接只在组合根)
|
||||
func _start_adventure() -> void:
|
||||
_close_modal(debug_overlay) # 开冒险路径显式关浮窗(调试浮窗默认开态下开局不带窗;对局中经角标再开)
|
||||
ui.modals.flow.close(ui.modals.debug_overlay) # 开冒险路径显式关浮窗(调试浮窗默认开态下开局不带窗;对局中经角标再开)
|
||||
_begin_session(true)
|
||||
_wire_adventure()
|
||||
run_scope.level_active = true
|
||||
_sync_status_entry() # 进对局点亮状态面板入口角标
|
||||
level_screen.set_abandon_available(true) # 放弃入口挂选路停点(天然非战斗期)
|
||||
log_view.clear()
|
||||
ui.modals.level_screen.set_abandon_available(true) # 放弃入口挂选路停点(天然非战斗期)
|
||||
ui.battle.log_view.clear()
|
||||
var seed_val := _seed_value()
|
||||
logger.start_session(seed_val)
|
||||
run_scope.adventure.set_hero_override(debug_overlay.atk_pct(), debug_overlay.hp_pct())
|
||||
run_scope.adventure.set_hero_override(ui.modals.debug_overlay.atk_pct(), ui.modals.debug_overlay.hp_pct())
|
||||
# 永久强化注入:冒险开始参数面——meta 现算 override 显式传参(全零 = 白板)
|
||||
# 开局参数面(同调三键同源构造——确定性承载=固定种子+参数档,首局教学图由种子域三分判定给 42)
|
||||
if not run_scope.adventure.start_adventure(seed_val, build_adventure_params(save_manager, meta,
|
||||
@@ -435,33 +252,33 @@ func _on_inter_next_pressed() -> void:
|
||||
func _show_board_fx_for_node(node_type: String = "") -> void:
|
||||
# Boss 遭遇变体:紫赤影子爬棋盘一次性占位演出(§2.6)
|
||||
if node_type == "boss":
|
||||
board_zone.fx.play_boss_shadow()
|
||||
ui.battle.board_zone.fx.play_boss_shadow()
|
||||
|
||||
|
||||
func _on_battle_finished(payload: Dictionary) -> void:
|
||||
# 仅调试模式接线(冒险的关卡结束归 AdventureRunner._on_level_finished——大奖励/冒险终局分派)
|
||||
var result: String = String(payload.get("result", ""))
|
||||
_sync_settlement_visuals() # → 全关(battle_over 反馈面)
|
||||
HudPresenter.battle_end_feedback(stage, ap_bar, true) # 灰化/idle 回正/RAP 清零/状态全清——HUD 单通道
|
||||
HudPresenter.battle_end_feedback(ui.battle.stage, ui.battle.board_zone.ap_bar, true) # 灰化/idle 回正/RAP 清零/状态全清——HUD 单通道
|
||||
var decision: Dictionary[Variant, Variant] = _debug.on_battle_finished(payload)
|
||||
match String(decision.outcome):
|
||||
"next_layer":
|
||||
_inter_label.text = "第 %d 层通过!\nHP %d/%d 带入下一层" % [
|
||||
ui.screens.inter_label.text = "第 %d 层通过!\nHP %d/%d 带入下一层" % [
|
||||
int(decision.layer), int(decision.carry_hp), int(db.combat.hero.max_hp)]
|
||||
_inter_overlay.visible = true
|
||||
ui.screens.inter_overlay.visible = true
|
||||
_refresh_presentations()
|
||||
return
|
||||
"level_end":
|
||||
_end_overlay.open_victory(Narrator.battle_end_text("victory", true),
|
||||
"返回", board_view.get_global_rect(), _end_victory_opts(payload))
|
||||
ui.modals.end_overlay.open_victory(Narrator.battle_end_text("victory", true),
|
||||
"返回", ui.battle.board_zone.board_view.get_global_rect(), _end_victory_opts(payload))
|
||||
"standalone_end":
|
||||
if result == "victory":
|
||||
_end_overlay.open_victory(Narrator.battle_end_text("victory", false),
|
||||
"回到遭遇选择", board_view.get_global_rect(), _end_victory_opts(payload))
|
||||
ui.modals.end_overlay.open_victory(Narrator.battle_end_text("victory", false),
|
||||
"回到遭遇选择", ui.battle.board_zone.board_view.get_global_rect(), _end_victory_opts(payload))
|
||||
else:
|
||||
_end_overlay.open_defeat(Narrator.battle_end_text("defeat", false),
|
||||
"返回", board_view.get_global_rect())
|
||||
_open_modal_after_playback(_end_overlay)
|
||||
ui.modals.end_overlay.open_defeat(Narrator.battle_end_text("defeat", false),
|
||||
"返回", ui.battle.board_zone.board_view.get_global_rect())
|
||||
ui.modals.flow.open_after_playback(ui.modals.end_overlay)
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
@@ -499,22 +316,17 @@ func _on_step_row(row: Dictionary) -> void:
|
||||
# 清图奖励可感知反馈(挖完全图须有奖励感——clear_blast 兑付对敌伤害
|
||||
# 零可见,toast 桥接归组合根呈现层;零兑付(未生效)不弹安静留明细带)
|
||||
if String(row.get("source_class", "")) == "clear_reward" and String(row.get("type", "")) != "reward_ineffective":
|
||||
board_zone.toast.show_toast("清图奖励:%s" % String(row.get("display", "")).replace("(清图奖励)", ""), "info")
|
||||
ui.battle.board_zone.toast.show_toast("清图奖励:%s" % String(row.get("display", "")).replace("(清图奖励)", ""), "info")
|
||||
# 排播入口:减动效=延迟归零同帧直落(行呈现不跳过);常态入队按段阶梯播放
|
||||
if UiKit.motion_scale < 1.0:
|
||||
_row_pacer.consume_now(row)
|
||||
ui.screens.row_pacer.consume_now(row)
|
||||
return
|
||||
_row_pacer.enqueue(row)
|
||||
ui.screens.row_pacer.enqueue(row)
|
||||
|
||||
|
||||
# 段间延迟取数面(减动效双态断言用——pacing_queue_test 直读;委托 row_pacer 同名面)
|
||||
func pace_delay_for(prev_seg: String, next_seg: String) -> float:
|
||||
return _row_pacer.pace_delay_for(prev_seg, next_seg)
|
||||
|
||||
|
||||
# 强制排空(模态开启前调用——明细回看/停点覆盖层/终局面所见明细带恒完整;pacing_queue_test 直读)
|
||||
func _flush_row_queue() -> void:
|
||||
_row_pacer.flush()
|
||||
return ui.screens.row_pacer.pace_delay_for(prev_seg, next_seg)
|
||||
|
||||
|
||||
func _on_round_started(ri: int) -> void:
|
||||
@@ -522,10 +334,10 @@ func _on_round_started(ri: int) -> void:
|
||||
#(view 内判减动效=静态化变体)。减动效快路=分隔行同帧直落(不跳过——consume_now 复用
|
||||
# _consume_row 轮首分隔行分支)。
|
||||
if UiKit.motion_scale < 1.0:
|
||||
_row_pacer.consume_now({"__round_line": ri})
|
||||
ui.screens.row_pacer.consume_now({"__round_line": ri})
|
||||
else:
|
||||
_row_pacer.enqueue({"__round_line": ri})
|
||||
settlement_fx.play_round_banner(ri)
|
||||
ui.screens.row_pacer.enqueue({"__round_line": ri})
|
||||
ui.battle.settlement_fx.play_round_banner(ri)
|
||||
# 秘宝呈现同步:每战首轮装配链
|
||||
# 显式重算〔🔴 契约复原语义:下一战装配链重算即含「未装配→写 0」复原〕。
|
||||
# 挂点时序依据:round_started 首轮发射位在 begin_battle 尾(attunement 注入之后)。
|
||||
@@ -534,7 +346,7 @@ func _on_round_started(ri: int) -> void:
|
||||
_sync_treasure_glow()
|
||||
# 巢穴预告呈现(装板报数量读数口消费:≥0=本战装配命中,toast 一次性提示)
|
||||
if run_scope.settlement != null and int(run_scope.settlement.nest_preview_count) >= 0:
|
||||
board_zone.toast.show_toast("巢穴预告——本板危险格共 %d 个" % int(run_scope.settlement.nest_preview_count), "info")
|
||||
ui.battle.board_zone.toast.show_toast("巢穴预告——本板危险格共 %d 个" % int(run_scope.settlement.nest_preview_count), "info")
|
||||
# 开战横幅——整图地形变异预告:首轮快照 board_variants
|
||||
# 非空才发射(Boss 图恒带一型=替换制;普通图空快照零发射);轮横幅下方叠行,view 内判减动效。
|
||||
if ri == 1 and run_scope.settlement != null:
|
||||
@@ -542,7 +354,7 @@ func _on_round_started(ri: int) -> void:
|
||||
if not bvs.is_empty():
|
||||
var vlines: Array[String] = HudPresenter.board_variant_lines(db, bvs)
|
||||
if not vlines.is_empty():
|
||||
settlement_fx.play_board_variant_banner("地形变异 · " + " · ".join(vlines))
|
||||
ui.battle.settlement_fx.play_board_variant_banner("地形变异 · " + " · ".join(vlines))
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
@@ -552,19 +364,19 @@ func _on_board_changed() -> void:
|
||||
run_scope.settlement = run_scope.runner.current_battle()
|
||||
if run_scope.settlement == null:
|
||||
return
|
||||
board_view.rebuild(run_scope.settlement.minesweeper.current_board(), run_scope.settlement.minesweeper)
|
||||
ui.battle.board_zone.board_view.rebuild(run_scope.settlement.minesweeper.current_board(), run_scope.settlement.minesweeper)
|
||||
_check_board_hints()
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
# 秘宝视野呈现桥:阔视野带 light_radius_bonus
|
||||
# 装配参数合计显式写给 board_view.set_fog_glow_bonus(装配=半径 8+4 / 未装配=复原 0),写入即 setter
|
||||
# 装配参数合计显式写给 ui.battle.board_zone.board_view.set_fog_glow_bonus(装配=半径 8+4 / 未装配=复原 0),写入即 setter
|
||||
# 内 refresh 全板重算雾态。调用点=每战首轮装配链(_on_round_started ri==1)——纯呈现边界 🔴 契约 2:
|
||||
# 只写呈现态参数,minesweeper 结算零触碰;off 态(未装配)写 0=缺省基线。
|
||||
func _sync_treasure_glow() -> void:
|
||||
if run_scope.settlement == null or board_view == null:
|
||||
if run_scope.settlement == null or ui.battle.board_zone.board_view == null:
|
||||
return
|
||||
board_view.set_fog_glow_bonus(run_scope.settlement.attune_param_sum("light_radius_bonus"))
|
||||
ui.battle.board_zone.board_view.set_fog_glow_bonus(run_scope.settlement.attune_param_sum("light_radius_bonus"))
|
||||
|
||||
|
||||
# ---- 一次性非模态提示(每 run 一次——pattern 10)与诚实提示 ----
|
||||
@@ -575,10 +387,10 @@ func _check_board_hints() -> void:
|
||||
return
|
||||
# 零合法板(棋盘生成 EC5 降级)诚实提示——非静默(toast,非模态)
|
||||
if board.illegal_product:
|
||||
board_zone.toast.show_toast("本板为零合法板降级产物——出图器已尽力,数据面诚实告知", "warn")
|
||||
ui.battle.board_zone.toast.show_toast("本板为零合法板降级产物——出图器已尽力,数据面诚实告知", "warn")
|
||||
# 接受最优降级(有合法板但无一达标)——降级标记独立于非法产物标记,同走诚实告知
|
||||
elif board.degraded_product:
|
||||
board_zone.toast.show_toast("本板为接受最优降级产物(未达可推理率及格线)——数据面诚实告知", "warn")
|
||||
ui.battle.board_zone.toast.show_toast("本板为接受最优降级产物(未达可推理率及格线)——数据面诚实告知", "warn")
|
||||
|
||||
|
||||
func _on_hover_forbidden(_cell: Vector2i) -> void:
|
||||
@@ -594,7 +406,7 @@ func _on_chord_attempted(_cell: Vector2i) -> void:
|
||||
func _show_hint(text: String, hint_id: String) -> void:
|
||||
run_scope.hints_shown[hint_id] = true
|
||||
logger.log_ux("ux_hint_shown", {"hint_id": hint_id})
|
||||
board_zone.toast.show_toast(text, "info")
|
||||
ui.battle.board_zone.toast.show_toast(text, "info")
|
||||
|
||||
|
||||
# ---- 节点覆盖层"继续"(奖励/恢复点兑付确认)与节点图选路覆盖层 ----
|
||||
@@ -706,15 +518,15 @@ func _on_wallet_changed(event: String, gp: int, extra: Dictionary)-> void:
|
||||
|
||||
func _on_board_action(cell: Vector2i, kind: String) -> void:
|
||||
# 玩家动作 → 核心写调用的唯一通道(模态/无战斗拒绝在此收口)
|
||||
if _input_locked():
|
||||
if ui.modals.flow.locks_input():
|
||||
return
|
||||
# 结算演播期棋盘冻结(v3 口径——不可点/不可移/不可挖):
|
||||
# 锁面=排播队列活跃(纯队列判据;排播泵尾迹窗口不可作演播中标志——见 row_pacer._process 注)。
|
||||
# 队列排空即解锁、无状态残留;HUD 按钮不在锁面(停手/明细回看各自通路,开启前
|
||||
# _flush_row_queue 自行排空)。
|
||||
# 开层前排空钩子(flow.flush_rows——模态开启前明细带恒完整)。
|
||||
# **点击不丢弃**(v3.1——「卡死」体感根因):锁期点击缓存最后一次,演播完边沿
|
||||
# (_process 泵 _was_pumping 分支)自动重放——玩家操作零丢失、体感=「演播一停动作就生效」。
|
||||
if not _row_queue.is_empty():
|
||||
if not ui.screens.row_pacer.pending_rows().is_empty():
|
||||
run_scope.locked_click = {"cell": cell, "kind": kind}
|
||||
return
|
||||
# 秘宝选中态劫持(两步交互第二步):点格=使用目标拾取,不落挖格;
|
||||
@@ -734,22 +546,22 @@ func _on_board_action(cell: Vector2i, kind: String) -> void:
|
||||
# settle_batch 内部同步走完 结算→战斗结束→关卡推进(事件同步发射)——关卡终局时 settlement 已被
|
||||
# runner 置空,清图演出判定须先判 settlement(重入隐患守卫)
|
||||
if accepted and kind == "dig" and run_scope.settlement != null and run_scope.settlement.minesweeper.current_board() != null and run_scope.settlement.minesweeper.clear_map_cleared():
|
||||
board_zone.fx.play_clear_burst()
|
||||
ui.battle.board_zone.fx.play_clear_burst()
|
||||
if accepted and kind == "dig" and run_scope.settlement != null and run_scope.settlement.minesweeper != null:
|
||||
# 挖掘演出:挥铲三帧 + 角色弹跳——触发=格子点开那一刻
|
||||
#(settle_batch 同步受理即状态已变,动画纯表现与 refresh 同拍零竞态;
|
||||
# 挖掘不移动英雄,出手方向 = 目标格−英雄格,圆心=英雄持握手位)
|
||||
var hero_cell: Vector2i = run_scope.settlement.minesweeper.hero_pos()
|
||||
board_zone.fx.play_shovel_swing(board_view.hero_hand_point(), cell - hero_cell, board_view.hero_sprite_scale())
|
||||
board_view.play_hero_dig_bounce()
|
||||
ui.battle.board_zone.fx.play_shovel_swing(ui.battle.board_zone.board_view.hero_hand_point(), cell - hero_cell, ui.battle.board_zone.board_view.hero_sprite_scale())
|
||||
ui.battle.board_zone.board_view.play_hero_dig_bounce()
|
||||
elif accepted and kind == "move":
|
||||
board_view.notify_hero_move() # 原地移动(目标=当前格幂等受理)帧闪回执——refresh diff 前置告知
|
||||
board_view.refresh()
|
||||
ui.battle.board_zone.board_view.notify_hero_move() # 原地移动(目标=当前格幂等受理)帧闪回执——refresh diff 前置告知
|
||||
ui.battle.board_zone.board_view.refresh()
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
func _on_stop_pressed() -> void:
|
||||
if _input_locked():
|
||||
if ui.modals.flow.locks_input():
|
||||
return
|
||||
# stop 经批次提交(排雷玩法 D1×6①——不直调 行动经济)
|
||||
if run_scope.settlement == null:
|
||||
@@ -758,15 +570,15 @@ func _on_stop_pressed() -> void:
|
||||
logger.log_action("stop", Vector2i(-1, -1), not outcomes.is_empty() and bool(outcomes[0].accepted))
|
||||
if not outcomes.is_empty() and String(outcomes[0].get("outcome", "")) == "voided":
|
||||
# voided ≠ 拒绝——独立文案(pattern 1:防静默改判读感欺骗)
|
||||
board_zone.toast.show_toast("本轮已清图,停手未执行 · 剩余 AP 已作废", "warn")
|
||||
ui.battle.board_zone.toast.show_toast("本轮已清图,停手未执行 · 剩余 AP 已作废", "warn")
|
||||
logger.log_ux("ux_reject", {"source": "voided", "sub_reason": "stop_voided"})
|
||||
board_view.refresh()
|
||||
ui.battle.board_zone.board_view.refresh()
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
func _on_refresh_confirmed() -> void:
|
||||
# 弃图刷新(挖掘期 Active 当场换图继续挖——D4;RAP 按门槛扣费 · 旧板封印作废,每轮上限读表)
|
||||
if _input_locked():
|
||||
if ui.modals.flow.locks_input():
|
||||
return
|
||||
var ok: bool = false
|
||||
if run_scope.level_active and run_scope.runner != null:
|
||||
@@ -776,7 +588,7 @@ func _on_refresh_confirmed() -> void:
|
||||
logger.log_action("refresh", Vector2i(-1, -1), ok)
|
||||
if ok:
|
||||
logger.log_ux("ux_refresh_attempt", {"result": "accepted", "round_refresh_index": _refresh_used_snapshot()})
|
||||
board_zone.toast.show_toast("RAP−%d · 旧板封印作废——新图已就位,AP 保留继续挖" % int(db.run.action_economy.rap_refresh_cost), "gold")
|
||||
ui.battle.board_zone.toast.show_toast("RAP−%d · 旧板封印作废——新图已就位,AP 保留继续挖" % int(db.run.action_economy.rap_refresh_cost), "gold")
|
||||
else:
|
||||
# 资源面/上限拒绝(EC13 合法拒绝可见;行动经济 无载荷——客户端按上下文推断)
|
||||
var rap: int = int(run_scope.settlement.presentation_snapshot().get("rap", 0)) if run_scope.settlement != null else 0
|
||||
@@ -784,11 +596,11 @@ func _on_refresh_confirmed() -> void:
|
||||
var result := "rejected_rap" if rap < cost else "rejected_cap"
|
||||
logger.log_ux("ux_refresh_attempt", {"result": result, "round_refresh_index": _refresh_used_snapshot() + 1})
|
||||
if result == "rejected_rap":
|
||||
board_zone.toast.show_toast("RAP 不足 · 还差 %d 点(弃图刷新需 8)" % (cost - rap), "warn")
|
||||
ap_bar.pulse_rap()
|
||||
ui.battle.board_zone.toast.show_toast("RAP 不足 · 还差 %d 点(弃图刷新需 8)" % (cost - rap), "warn")
|
||||
ui.battle.board_zone.ap_bar.pulse_rap()
|
||||
else:
|
||||
board_zone.toast.show_toast("本轮刷新次数已用完(每轮 2 次)", "warn")
|
||||
board_view.refresh()
|
||||
ui.battle.board_zone.toast.show_toast("本轮刷新次数已用完(每轮 2 次)", "warn")
|
||||
ui.battle.board_zone.board_view.refresh()
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
@@ -798,9 +610,9 @@ func _handle_outcome_feedback(outcome: Dictionary) -> void:
|
||||
return
|
||||
logger.log_ux("ux_reject", {"source": String(outcome.get("reject_reason", "")),
|
||||
"sub_reason": String(outcome.get("sub_reason", ""))})
|
||||
board_zone.toast.show_toast(HudPresenter.outcome_text(outcome), "warn")
|
||||
ui.battle.board_zone.toast.show_toast(HudPresenter.outcome_text(outcome), "warn")
|
||||
if String(outcome.get("reject_reason", "")) == "resource_face":
|
||||
ap_bar.pulse_ap()
|
||||
ui.battle.board_zone.ap_bar.pulse_ap()
|
||||
|
||||
|
||||
# ==================== 全屏 States 状态机(挖掘/结算/窗口/终局)====================
|
||||
@@ -816,7 +628,7 @@ func _refresh_used_snapshot() -> int:
|
||||
func _refresh_presentations() -> void:
|
||||
# 每个核心信号/写调用返回后的全量重查装配(更新触发注记——无推送信号,命令式重查)
|
||||
var refresh_remaining: int = maxi(0, int(db.run.action_economy.refresh_cap_per_round) - _refresh_used_snapshot())
|
||||
HudPresenter.refresh(stage, info_panel, ap_bar, {"settlement": run_scope.settlement, "runner": run_scope.runner, "level_active": run_scope.level_active, "db": db, "refresh_remaining": refresh_remaining})
|
||||
HudPresenter.refresh(ui.battle.stage, ui.battle.info_panel, ui.battle.board_zone.ap_bar, {"settlement": run_scope.settlement, "runner": run_scope.runner, "level_active": run_scope.level_active, "db": db, "refresh_remaining": refresh_remaining})
|
||||
_treasure_ui.refresh_slots() # C 区秘宝槽重查(试炼镐积点进度随挖格刷新悬停浮窗)
|
||||
_sync_settlement_visuals()
|
||||
|
||||
@@ -825,9 +637,9 @@ func _sync_settlement_visuals() -> void:
|
||||
# 结算阶段(round_end → 下一轮):D 区去饱和+金橙偏移锁输入(明度不变)+ 明细带展开 84px
|
||||
var settling := _is_settling()
|
||||
if settling and not run_scope.was_settling:
|
||||
settlement_fx.play_round_sweep(board_view.get_global_rect()) # 结算轮光带——时序敏感演出触发,例外留在组合根
|
||||
ui.battle.settlement_fx.play_round_sweep(ui.battle.board_zone.board_view.get_global_rect()) # 结算轮光带——时序敏感演出触发,例外留在组合根
|
||||
run_scope.was_settling = settling
|
||||
HudPresenter.sync_settlement_visuals(stage, board_view, log_view, settling,
|
||||
HudPresenter.sync_settlement_visuals(ui.battle.stage, ui.battle.board_zone.board_view, ui.battle.log_view, settling,
|
||||
HudPresenter.phase_text(run_scope.settlement, run_scope.runner, run_scope.boss_active, db))
|
||||
|
||||
|
||||
@@ -836,13 +648,13 @@ func _is_settling() -> bool:
|
||||
# 判据若只跟逻辑态,演播期棋盘会提前恢复可挖观感=「没意识到在结算演出」感知缺口根源;
|
||||
# 演播期维持去饱和+禁挖光(结算中语义),放完自然回挖掘认知。判据=纯队列(排播泵尾迹
|
||||
# 窗口不可作演播中标志——row_pacer._process 注)。battle 收尾演播例外:
|
||||
# overlay 开启即 _flush_row_queue 排空,视觉随队列空同步交接结算框。
|
||||
# overlay 开启经 flow.flush_rows 钩子排空,视觉随队列空同步交接结算框。
|
||||
# 相位取数经 presentation_snapshot 成文面——禁 settlement.action_economy 链式穿透)。
|
||||
if run_scope.settlement == null or not run_scope.settlement.is_battle_live():
|
||||
return not _row_queue.is_empty()
|
||||
return not ui.screens.row_pacer.pending_rows().is_empty()
|
||||
if String(run_scope.settlement.presentation_snapshot().get("phase", "")) == ActionEconomy.PHASE_WAITING:
|
||||
return true
|
||||
return not _row_queue.is_empty()
|
||||
return not ui.screens.row_pacer.pending_rows().is_empty()
|
||||
|
||||
|
||||
# 相位行 —— 承接体见 HudPresenter.phase_text(caller 直接改引)
|
||||
@@ -908,8 +720,8 @@ func _seed_value() -> int:
|
||||
# 重放模板 0 即在此种子下标定〕;置位 true=教学局已收束的非首局走 ③ 随机);
|
||||
# ③非首局空/非法框=引擎全局熵 randi()(Godot 4 启动自播种子,headless 同;不走 rng_service
|
||||
# 封闭流、禁 randomize()/自建 RNG——组合根建种子属引擎熵豁免面,不进玩法 RNG 消费)
|
||||
if _seed_edit.text.is_valid_int():
|
||||
return int(_seed_edit.text)
|
||||
if ui.modals.debug_overlay.seed_edit.text.is_valid_int():
|
||||
return int(ui.modals.debug_overlay.seed_edit.text)
|
||||
if save_manager.meta_state.career_state().first_adventure_done:
|
||||
return randi() # rng-seed-exempt(非首局空框引擎熵——组合根建种子不进玩法 RNG 消费,static_scan S1/S2 标记豁免)
|
||||
return 42
|
||||
@@ -30,10 +30,10 @@ static func _on_adventure_level_started(root: Variant, level_index: int, display
|
||||
# 开场行只显示 atk/max_hp 两值,注记指向不存在的显示轴反成失真),白板局不带
|
||||
var ov: Dictionary[Variant, Variant] = root.meta.hero_override()
|
||||
var boosted: bool = int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 \
|
||||
or root.debug_overlay.atk_pct() > 0 or root.debug_overlay.hp_pct() > 0 \
|
||||
or root.ui.modals.debug_overlay.atk_pct() > 0 or root.ui.modals.debug_overlay.hp_pct() > 0 \
|
||||
or root.run_scope.runner.journal().bonus_total("atk") > 0 \
|
||||
or root.run_scope.runner.journal().bonus_total("max_hp") > 0
|
||||
root.log_view.append_bb(root.Narrator.level_open_line(display_name, int(root.run_scope.adventure.master_seed),
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.level_open_line(display_name, int(root.run_scope.adventure.master_seed),
|
||||
root.run_scope.runner.effective_atk(), root.run_scope.runner.effective_max_hp(), boosted))
|
||||
|
||||
# runner 六信号重连(is_connected 守卫——未连即接;守卫失败 push_error=重连恰好一次契约承载)
|
||||
@@ -62,30 +62,30 @@ static func _on_runner_node(root: Variant, node: Dictionary, info: Dictionary) -
|
||||
root.logger.attach_battle(root.run_scope.settlement)
|
||||
root.run_scope.boss_active = t == "boss"
|
||||
root._show_board_fx_for_node(t)
|
||||
root.log_view.append_bb(root.Narrator.node_banner(root.run_scope.runner, node, t))
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.node_banner(root.run_scope.runner, node, t))
|
||||
elif t == "reward":
|
||||
# 奖励节点三选一(与关卡完成三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
|
||||
# 呈现编排归 curio_interact_controller.present_node_curio。
|
||||
root._bonus.present_node_curio()
|
||||
root.log_view.append_bb(root.Narrator.reward_line())
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.reward_line())
|
||||
elif t == "rest":
|
||||
var healed: int = int(info.get("healed", 0))
|
||||
root._open_modal(root.node_overlay)
|
||||
root.node_overlay.show_rest(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0)))
|
||||
root.log_view.append_bb(root.Narrator.rest_line(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0))))
|
||||
root.ui.modals.flow.open(root.ui.modals.node_overlay)
|
||||
root.ui.modals.node_overlay.show_rest(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0)))
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.rest_line(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0))))
|
||||
elif t == "shop":
|
||||
# 商店房兑付:到达即开店——商品/货架快照与余额经 runner 只读投影喂入;
|
||||
# 购买/离开归玩家意图(buy_requested/leave_pressed 接线),零自动推进。
|
||||
# pool 面 = 共享池货架三选一同构卡;legacy 面 = 固定三件(off 对照承载)
|
||||
root._open_modal(root.shop_overlay)
|
||||
root.ui.modals.flow.open(root.ui.modals.shop_overlay)
|
||||
if root.run_scope.runner.shop.is_shop_pool():
|
||||
root.shop_overlay.open_shelf(root.run_scope.runner.shop.shop_shelf(), int(root.run_scope.runner.gp))
|
||||
root.shop_overlay.set_visit_info(int(root.run_scope.runner.shop.shop_bp_visit_buys()), float(root.db.run.shop.price_a)) # 爬价规则行
|
||||
root.shop_overlay.set_refresh_display(root.run_scope.runner.shop.shop_refresh_free_left(), root.run_scope.runner.shop.shop_refresh_cost(), int(root.run_scope.runner.gp) >= root.run_scope.runner.shop.shop_refresh_cost()) # MC-1
|
||||
root.ui.modals.shop_overlay.open_shelf(root.run_scope.runner.shop.shop_shelf(), int(root.run_scope.runner.gp))
|
||||
root.ui.modals.shop_overlay.set_visit_info(int(root.run_scope.runner.shop.shop_bp_visit_buys()), float(root.db.run.shop.price_a)) # 爬价规则行
|
||||
root.ui.modals.shop_overlay.set_refresh_display(root.run_scope.runner.shop.shop_refresh_free_left(), root.run_scope.runner.shop.shop_refresh_cost(), int(root.run_scope.runner.gp) >= root.run_scope.runner.shop.shop_refresh_cost()) # MC-1
|
||||
else:
|
||||
root.shop_overlay.open_shop(root.run_scope.runner.shop.shop_products(), int(root.run_scope.runner.gp))
|
||||
root.shop_overlay.set_visit_info(-1, 1.0) # legacy 面无爬价行(off 对照承载)
|
||||
root.log_view.append_bb(root.Narrator.shop_line(int(root.run_scope.runner.gp)))
|
||||
root.ui.modals.shop_overlay.open_shop(root.run_scope.runner.shop.shop_products(), int(root.run_scope.runner.gp))
|
||||
root.ui.modals.shop_overlay.set_visit_info(-1, 1.0) # legacy 面无爬价行(off 对照承载)
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.shop_line(int(root.run_scope.runner.gp)))
|
||||
root.logger.log_node(node, info, root.run_scope.runner.node_index)
|
||||
root._refresh_presentations()
|
||||
|
||||
@@ -101,7 +101,7 @@ static func _on_adventure_finished(root: Variant, summary: Dictionary) -> void:
|
||||
_exit_level_screen(root) # 终局兜底:独立屏在开态(放弃入口)也先回战斗屏呈现摘要——屏级互斥单一出口
|
||||
root._sync_status_entry() # 离局熄灭状态面板入口角标(防菜单误现)
|
||||
root._sync_settlement_visuals() # → 全关
|
||||
HudPresenter.battle_end_feedback(root.stage, root.ap_bar, false) # 灰化/idle 回正/状态全清——HUD 单通道
|
||||
HudPresenter.battle_end_feedback(root.ui.battle.stage, root.ui.battle.board_zone.ap_bar, false) # 灰化/idle 回正/状态全清——HUD 单通道
|
||||
# 存档入账:settle = Boss 两档入账 + 工坊解锁 + 摘要入档 + 写盘(唯一写档点不变——
|
||||
# 调试两层不经过本处理器,天然不写档不污染生涯数据)。
|
||||
var detail: Dictionary[String, Variant] = {}
|
||||
@@ -110,30 +110,30 @@ static func _on_adventure_finished(root: Variant, summary: Dictionary) -> void:
|
||||
if not detail.is_empty():
|
||||
text += "\n" + root.Narrator.keystone_settle_text(detail, root._bonus.item_name_lookup())
|
||||
if result == "completed":
|
||||
root._end_overlay.open_victory(text, "返回冒险", root.board_view.get_global_rect(),
|
||||
root.ui.modals.end_overlay.open_victory(text, "返回冒险", root.ui.battle.board_zone.board_view.get_global_rect(),
|
||||
{"gp_summary": "本局 GP 入账 · 合计 %d" % int(summary.get("gp_final", 0))}) # GP 行(取数=summary gp_final 清账前快照);钥石明细已在 text(keystone_settle_text)——三行账
|
||||
else:
|
||||
root._end_overlay.open_defeat(text, "返回", root.board_view.get_global_rect())
|
||||
root.ui.modals.end_overlay.open_defeat(text, "返回", root.ui.battle.board_zone.board_view.get_global_rect())
|
||||
root.logger.log_level_end(summary) # 试玩时间线:冒险终局(摘要随载荷)
|
||||
if root.run_scope.runner != null:
|
||||
root.run_scope.runner.restore_hero_base()
|
||||
root._open_modal_after_playback(root._end_overlay)
|
||||
root.ui.modals.flow.open_after_playback(root.ui.modals.end_overlay)
|
||||
root._refresh_presentations()
|
||||
|
||||
static func _on_battle_end_continue(root: Variant) -> void:
|
||||
# 终局出口:返回开始界面(冒险摘要留档试玩日志;钥石在收束时入存档)
|
||||
root._close_modal(root._end_overlay)
|
||||
root._end_overlay.close()
|
||||
root.ui.modals.flow.close(root.ui.modals.end_overlay)
|
||||
root.ui.modals.end_overlay.close()
|
||||
# 三选一停点挂起分支:战斗总结结算(Boss 胜利钥石增量行)确认后进三选一面
|
||||
if root._bonus.resume_if_pending(): # 三选一停点在途恢复入口(curio_interact_controller)
|
||||
return
|
||||
# 挂起分派:战斗胜利总结结算框的「下一节点」≠ 终局——进选路页(暂存候选恢复呈现)
|
||||
if root._settlement_stop_pending:
|
||||
root._settlement_stop_pending = false
|
||||
if root.run_scope.settlement_stop_timer.is_armed():
|
||||
root.run_scope.settlement_stop_timer.disarm()
|
||||
_enter_level_screen(root)
|
||||
root.level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(root.db, root.run_scope.runner.feats_held())))
|
||||
root.level_screen.open_choices(root.run_scope.pending_candidates, root.run_scope.runner)
|
||||
root.level_screen.apply_status(level_map_status_payload(root.run_scope.runner))
|
||||
root.ui.modals.level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(root.db, root.run_scope.runner.feats_held())))
|
||||
root.ui.modals.level_screen.open_choices(root.run_scope.pending_candidates, root.run_scope.runner)
|
||||
root.ui.modals.level_screen.apply_status(level_map_status_payload(root.run_scope.runner))
|
||||
root.run_scope.pending_candidates = []
|
||||
root._refresh_presentations()
|
||||
return
|
||||
@@ -141,12 +141,12 @@ static func _on_battle_end_continue(root: Variant) -> void:
|
||||
# 局态封印/账本/模态挂起全复位(局态字段清单见 run_scope.gd)
|
||||
root._debug.reset()
|
||||
root._begin_session(false)
|
||||
root.debug_overlay.reset_sliders()
|
||||
root._start_overlay.visible = true
|
||||
root.ui.modals.debug_overlay.reset_sliders()
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
root._refresh_presentations()
|
||||
|
||||
static func _on_inter_next_pressed(root: Variant) -> void:
|
||||
root._inter_overlay.visible = false
|
||||
root.ui.screens.inter_overlay.visible = false
|
||||
if root.run_scope.is_adventure:
|
||||
# 冒险模式:大奖励屏确认 → 进下一关(AdventureRunner 序列推进——零自动推进的冒险级停点)
|
||||
if root.run_scope.adventure != null and not root.run_scope.adventure.continue_to_next_level():
|
||||
@@ -158,7 +158,7 @@ static func _on_inter_next_pressed(root: Variant) -> void:
|
||||
|
||||
|
||||
static func _on_node_continue(root: Variant) -> void:
|
||||
root._close_modal(root.node_overlay)
|
||||
root.ui.modals.flow.close(root.ui.modals.node_overlay)
|
||||
if root.run_scope.runner != null:
|
||||
# 确认 ≠ 推进——兑付确认后经 choices_presented 弹出选路口
|
||||
root.run_scope.runner.continue_after_node()
|
||||
@@ -167,33 +167,33 @@ static func _on_choices_presented(root: Variant, candidates: Array) -> void:
|
||||
# 零自动推进的停点呈现:整幅节点图 + 图上点击选路(战斗胜利/兑付确认统一到达此处)
|
||||
if root.run_scope.runner == null:
|
||||
return
|
||||
root.log_view.append_bb(root.Narrator.route_line(candidates, root.run_scope.runner))
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.route_line(candidates, root.run_scope.runner))
|
||||
# 战斗胜利总结结算(battle.md OQ14):战斗胜利停点先弹 BattleEndOverlay
|
||||
# 胜利形态(金蜡封+GP 增量+总量+判定行+「下一节点」+明细回看),确认后进选路页。判据 = 战斗节点
|
||||
# 的选路停点且战斗已收场(battle 收尾分支 _settlement 置 null 先于本信号;入口/兑付/商店停点
|
||||
# 的当前节点非 battle 型——三态天然分流)。Boss 胜/失败走 level_finished 另路不经此处。
|
||||
if root.run_scope.is_adventure and root.run_scope.runner.current_battle() == null \
|
||||
and String(root.run_scope.runner.current_node().get("type", "")) == "battle" and not candidates.is_empty():
|
||||
root._settlement_stop_pending = true
|
||||
root.run_scope.settlement_stop_timer.arm()
|
||||
root.run_scope.pending_candidates = candidates.duplicate()
|
||||
root._end_overlay.open_victory(root.Narrator.battle_end_text("victory", false), "下一节点",
|
||||
root.board_view.get_global_rect(),
|
||||
root.ui.modals.end_overlay.open_victory(root.Narrator.battle_end_text("victory", false), "下一节点",
|
||||
root.ui.battle.board_zone.board_view.get_global_rect(),
|
||||
{"gp_summary": "战利品 GP +%d · 合计 %d" % [root.run_scope.last_gain_amount, int(root.run_scope.runner.gp)]})
|
||||
root._open_modal_after_playback(root._end_overlay)
|
||||
root.ui.modals.flow.open_after_playback(root.ui.modals.end_overlay)
|
||||
root._refresh_presentations()
|
||||
return
|
||||
_enter_level_screen(root)
|
||||
root.level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(root.db, root.run_scope.runner.feats_held())))
|
||||
root.level_screen.open_choices(candidates, root.run_scope.runner)
|
||||
root.level_screen.apply_status(level_map_status_payload(root.run_scope.runner))
|
||||
root.ui.modals.level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(root.db, root.run_scope.runner.feats_held())))
|
||||
root.ui.modals.level_screen.open_choices(candidates, root.run_scope.runner)
|
||||
root.ui.modals.level_screen.apply_status(level_map_status_payload(root.run_scope.runner))
|
||||
root._refresh_presentations()
|
||||
|
||||
static func _on_route_chosen(root: Variant, node_id: int) -> void:
|
||||
# 视图已按节点类型定去留——战斗/Boss 出屏(visible=false,此处收栈
|
||||
# 记录一致性+回战斗屏);奖励/恢复/商店本屏保持激活(兑付覆盖层由节点事件域内弹出,模态栈
|
||||
# 打开低层时自动隐藏本屏、关闭自动恢复)
|
||||
if not root.level_screen.visible:
|
||||
root._close_modal(root.level_screen)
|
||||
if not root.ui.modals.level_screen.visible:
|
||||
root.ui.modals.flow.close(root.ui.modals.level_screen)
|
||||
_exit_level_screen(root)
|
||||
if root.run_scope.runner != null and not root.run_scope.runner.pick_next_node(node_id):
|
||||
push_error("[组合根] 选路被拒(编号 %d 不在候选)" % node_id)
|
||||
@@ -202,15 +202,15 @@ static func _on_route_chosen(root: Variant, node_id: int) -> void:
|
||||
|
||||
static func _enter_level_screen(root: Variant) -> void:
|
||||
root.run_scope.level_screen_active = true
|
||||
root._battle_root.visible = false
|
||||
root._open_modal(root.level_screen)
|
||||
root.ui.screens.battle_root.visible = false
|
||||
root.ui.modals.flow.open(root.ui.modals.level_screen)
|
||||
root._sync_status_entry() # 屏级互斥切换同步(_open_modal 单口不识屏级状态)
|
||||
|
||||
static func _exit_level_screen(root: Variant) -> void:
|
||||
root.run_scope.level_screen_active = false
|
||||
root._battle_root.visible = true
|
||||
if root.level_screen.visible:
|
||||
root._close_modal(root.level_screen)
|
||||
root.ui.screens.battle_root.visible = true
|
||||
if root.ui.modals.level_screen.visible:
|
||||
root.ui.modals.flow.close(root.ui.modals.level_screen)
|
||||
root._sync_status_entry() # 同上
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ static func _on_abandon_requested(root: Variant) -> void:
|
||||
return
|
||||
if not root.run_scope.adventure.abandon():
|
||||
root.logger.log_ux("ux_reject", {"source": "abandon", "sub_reason": "battle_in_flight"})
|
||||
root.board_zone.toast.show_toast("战斗进行中——无法放弃", "warn")
|
||||
root.ui.battle.board_zone.toast.show_toast("战斗进行中——无法放弃", "warn")
|
||||
|
||||
|
||||
# ---- 营地覆盖层与钥石账本(写调用唯一归组合根)----
|
||||
|
||||
@@ -13,7 +13,7 @@ static func _on_diagnose_pressed(root: Variant) -> void:
|
||||
if root.run_scope.settlement != null:
|
||||
ctx["settlement"] = root.run_scope.settlement.diagnostic_snapshot() # 诊断观测面(私有字段零直读)
|
||||
var path: String = root.logger.dump_diagnosis(ctx)
|
||||
root.board_zone.toast.show_toast("已转储诊断: " + path, "info")
|
||||
root.ui.battle.board_zone.toast.show_toast("已转储诊断: " + path, "info")
|
||||
|
||||
|
||||
|
||||
@@ -21,17 +21,17 @@ static func _show_debug_overlay(root: Variant) -> void:
|
||||
# 门禁:对局中可开;本函数仍是唯一开窗入口,门禁逻辑收函数内:
|
||||
# 棋盘输入锁/状态角标判据排除浮窗,
|
||||
# 遮挡语义=ModalStack 登记制(他模态开时暂隐、关后自动恢复),此处零场景限制
|
||||
if root.debug_overlay == null:
|
||||
if root.ui.modals.debug_overlay == null:
|
||||
return
|
||||
root._open_modal(root.debug_overlay)
|
||||
root.ui.modals.flow.open(root.ui.modals.debug_overlay)
|
||||
_sync_debug_overlay(root)
|
||||
|
||||
|
||||
|
||||
# 调试浮窗角标点击(两屏同款处理器——与状态角标同形制守卫;仍收口 _show_debug_overlay 单口)
|
||||
static func _on_debug_badge_pressed(root: Variant) -> void:
|
||||
var map_on: bool = root.level_screen != null and root.level_screen.visible
|
||||
if (map_on and root._modal_flow.badge_greyed_map()) or (not map_on and root._modal_flow.badge_greyed_battle()):
|
||||
var map_on: bool = root.ui.modals.level_screen != null and root.ui.modals.level_screen.visible
|
||||
if (map_on and root.ui.modals.flow.badge_greyed_map()) or (not map_on and root.ui.modals.flow.badge_greyed_battle()):
|
||||
return
|
||||
_show_debug_overlay(root)
|
||||
|
||||
@@ -40,10 +40,10 @@ static func _on_debug_badge_pressed(root: Variant) -> void:
|
||||
# 浮窗上下文同步(当局数据刷新+对局中按钮禁用单点):调用面=开窗口与模态开合
|
||||
# 单口(凡影响开态/对局态的路径必经其一——与 _sync_status_entry 同款纪律)
|
||||
static func _sync_debug_overlay(root: Variant) -> void:
|
||||
if root.debug_overlay == null:
|
||||
if root.ui.modals.debug_overlay == null:
|
||||
return
|
||||
root.debug_overlay.set_context_locked(root.run_scope.settlement != null or root.run_scope.is_adventure) # 对局中批量模拟/两层调试入口禁用(长程异步模拟与活跃结算并存态不开放)
|
||||
root.debug_overlay.update_run_data(_debug_run_data(root)) # 当局数据只读区块喂入(开窗+模态开合信号驱动)
|
||||
root.ui.modals.debug_overlay.set_context_locked(root.run_scope.settlement != null or root.run_scope.is_adventure) # 对局中批量模拟/两层调试入口禁用(长程异步模拟与活跃结算并存态不开放)
|
||||
root.ui.modals.debug_overlay.update_run_data(_debug_run_data(root)) # 当局数据只读区块喂入(开窗+模态开合信号驱动)
|
||||
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ static func _debug_run_data(root: Variant) -> Dictionary:
|
||||
|
||||
|
||||
static func _close_debug_overlay(root: Variant) -> void:
|
||||
root._close_modal(root.debug_overlay) # 模态栈单口——开始层全程未隐藏,无需恢复
|
||||
root.ui.modals.flow.close(root.ui.modals.debug_overlay) # 模态栈单口——开始层全程未隐藏,无需恢复
|
||||
|
||||
static func _on_tune_apply(root: Variant, atk_pct: int, hp_pct: int) -> void:
|
||||
# 加成无持久态可"落"——各战斗开局经 board_resolvers.debug_hero_override/runner 现算生效(应用=关闭浮窗)
|
||||
@@ -83,11 +83,11 @@ static func _start_debug_levels(root: Variant) -> void:
|
||||
# settlement/编排态=状态机破坏;模拟在跑时 reset_sliders 会作废测量读数(d5 置灰之外的信号绕过闸)
|
||||
if root.run_scope.is_adventure or root.run_scope.settlement != null or root._tune_sim.is_running():
|
||||
return
|
||||
root.debug_overlay.reset_sliders()
|
||||
root._close_modal(root.debug_overlay) # 浮窗内入口发起——随开随收(对局期不可再开:门禁在 _show_debug_overlay)
|
||||
root._start_overlay.visible = false
|
||||
root.log_view.clear()
|
||||
root.log_view.append_bb(root.Narrator.debug_open_line(root._seed_value(), 1, true))
|
||||
root.ui.modals.debug_overlay.reset_sliders()
|
||||
root.ui.modals.flow.close(root.ui.modals.debug_overlay) # 浮窗内入口发起——随开随收(对局期不可再开:门禁在 _show_debug_overlay)
|
||||
root.ui.screens.start_overlay.visible = false
|
||||
root.ui.battle.log_view.clear()
|
||||
root.ui.battle.log_view.append_bb(root.Narrator.debug_open_line(root._seed_value(), 1, true))
|
||||
root._begin_session(false)
|
||||
if not root._debug.start_debug_levels():
|
||||
root._fatal_screen("战斗启动失败——见输出日志")
|
||||
@@ -100,6 +100,6 @@ static func _on_debug_battle_created(root: Variant, settlement_ref: BattleOrches
|
||||
|
||||
static func _on_debug_battle_ready(root: Variant, settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void:
|
||||
root.run_scope.settlement = settlement_ref
|
||||
root.board_view.rebuild(root.run_scope.settlement.minesweeper.current_board(), root.run_scope.settlement.minesweeper)
|
||||
root.ui.battle.board_zone.board_view.rebuild(root.run_scope.settlement.minesweeper.current_board(), root.run_scope.settlement.minesweeper)
|
||||
root._check_board_hints()
|
||||
root._refresh_presentations()
|
||||
@@ -5,12 +5,12 @@
|
||||
extends RefCounted
|
||||
|
||||
static func _open_settings(root: Variant) -> void:
|
||||
root._start_overlay.visible = false
|
||||
root._open_modal(root.settings_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = false
|
||||
root.ui.modals.flow.open(root.ui.modals.settings_overlay) # 模态栈单口
|
||||
|
||||
static func _on_settings_closed(root: Variant) -> void:
|
||||
root._close_modal(root.settings_overlay) # 模态栈单口
|
||||
root._start_overlay.visible = true
|
||||
root.ui.modals.flow.close(root.ui.modals.settings_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)----
|
||||
@@ -25,22 +25,22 @@ static func _status_data(root: Variant) -> Dictionary:
|
||||
return HudPresenter.status_data(root.db, root.meta, root.run_scope.runner, root._treasure_ui)
|
||||
|
||||
static func _open_status_panel(root: Variant) -> void:
|
||||
root._open_modal(root.status_panel) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项)
|
||||
root.status_panel.open_status(_status_data(root))
|
||||
root.ui.modals.flow.open(root.ui.modals.status_panel) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项)
|
||||
root.ui.modals.status_panel.open_status(_status_data(root))
|
||||
|
||||
static func _on_status_closed(root: Variant) -> void:
|
||||
root._close_modal(root.status_panel) # 模态栈单口
|
||||
root.ui.modals.flow.close(root.ui.modals.status_panel) # 模态栈单口
|
||||
|
||||
|
||||
|
||||
static func _open_career(root: Variant) -> void:
|
||||
root._start_overlay.visible = false
|
||||
root._open_modal(root.career_overlay) # 模态栈单口
|
||||
root.career_overlay.open_career(HudPresenter.career_data(root.db, root.save_manager, root.meta))
|
||||
root.ui.screens.start_overlay.visible = false
|
||||
root.ui.modals.flow.open(root.ui.modals.career_overlay) # 模态栈单口
|
||||
root.ui.modals.career_overlay.open_career(HudPresenter.career_data(root.db, root.save_manager, root.meta))
|
||||
|
||||
static func _on_career_closed(root: Variant) -> void:
|
||||
root._close_modal(root.career_overlay) # 模态栈单口
|
||||
root._start_overlay.visible = true
|
||||
root.ui.modals.flow.close(root.ui.modals.career_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# 生涯页快照投影 —— 承接体见 HudPresenter.career_data(开层调用点直接改引)
|
||||
@@ -52,13 +52,13 @@ static func _on_career_closed(root: Variant) -> void:
|
||||
# 开层(模态栈单口;与开始层互斥=career 同款先例)。「进入存档」钮归卡② mn3——本函数为
|
||||
# 该钮的唯一落点(卡③ 批内探针函数直调=中间态,实现报告「已知中间态」登记)。
|
||||
static func _open_save_select(root: Variant) -> void:
|
||||
root._start_overlay.visible = false
|
||||
root._open_modal(root.save_select_overlay) # 模态栈单口
|
||||
root.save_select_overlay.open_save_select(HudPresenter.save_select_data(root.save_manager))
|
||||
root.ui.screens.start_overlay.visible = false
|
||||
root.ui.modals.flow.open(root.ui.modals.save_select_overlay) # 模态栈单口
|
||||
root.ui.modals.save_select_overlay.open_save_select(HudPresenter.save_select_data(root.save_manager))
|
||||
|
||||
static func _on_save_select_closed(root: Variant) -> void:
|
||||
root._close_modal(root.save_select_overlay) # 模态栈单口
|
||||
root._start_overlay.visible = true
|
||||
root.ui.modals.flow.close(root.ui.modals.save_select_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# 槽数据快照 —— 承接体见 HudPresenter.save_select_data(开层调用点直接改引)
|
||||
@@ -85,18 +85,18 @@ static func _on_save_select_load(root: Variant, slot: int) -> void:
|
||||
static func _on_save_select_delete(root: Variant, slot: int) -> void:
|
||||
root._confirm_consumer = "delete_slot"
|
||||
root._confirm_consumer_slot = slot
|
||||
root._open_modal(root.confirm_dialog) # 模态栈单口(显示编排/挂起兜底/遮挡记账——与其余覆盖层同口)
|
||||
root.confirm_dialog.open("删除存档位 %d?删除后该档不可恢复。" % slot, "删除", "取消")
|
||||
root.ui.modals.flow.open(root.ui.modals.confirm_dialog) # 模态栈单口(显示编排/挂起兜底/遮挡记账——与其余覆盖层同口)
|
||||
root.ui.modals.confirm_dialog.open("删除存档位 %d?删除后该档不可恢复。" % slot, "删除", "取消")
|
||||
|
||||
|
||||
|
||||
# 入口角标点击(两屏同款处理器):P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰,
|
||||
# 探针 pressed.emit 绕过 disabled 时由此兜住;地图屏自身的登记开态不计——has_open_excluding)
|
||||
static func _on_status_badge_pressed(root: Variant) -> void:
|
||||
var map_on: bool = root.level_screen != null and root.level_screen.visible
|
||||
var map_on: bool = root.ui.modals.level_screen != null and root.ui.modals.level_screen.visible
|
||||
# P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰,探针 pressed.emit 绕过 disabled 时由此兜住;
|
||||
# 地图屏自身的登记开态不计;判据排除调试浮窗:对局中浮窗开着时状态角标仍可点)
|
||||
if (map_on and root._modal_flow.badge_greyed_map()) or (not map_on and root._modal_flow.badge_greyed_battle()):
|
||||
if (map_on and root.ui.modals.flow.badge_greyed_map()) or (not map_on and root.ui.modals.flow.badge_greyed_battle()):
|
||||
return
|
||||
_open_status_panel(root)
|
||||
|
||||
@@ -106,17 +106,17 @@ static func _on_status_badge_pressed(root: Variant) -> void:
|
||||
# 地图屏角标=随屏;置灰=登记册上除地图屏自身外任一层开着(防「看得见点不着」态)。
|
||||
# 调用面=模态开合单口与屏级互斥口(凡影响开态/屏态的路径必经其一)。
|
||||
static func _sync_status_entry(root: Variant) -> void:
|
||||
if root.status_panel == null or root._modals == null or root.board_zone == null or root.level_screen == null:
|
||||
if root.ui.modals.status_panel == null or root.ui.modals.flow == null or root.ui.battle.board_zone == null or root.ui.modals.level_screen == null:
|
||||
return
|
||||
var map_on: bool = root.level_screen.visible
|
||||
root.board_zone.status_badge.visible = root.run_scope.level_active and not map_on
|
||||
root.level_screen.status_badge.visible = map_on
|
||||
var map_on: bool = root.ui.modals.level_screen.visible
|
||||
root.ui.battle.board_zone.status_badge.visible = root.run_scope.level_active and not map_on
|
||||
root.ui.modals.level_screen.status_badge.visible = map_on
|
||||
# 置灰判据为排除式(调试浮窗开态不计入:对局中浮窗开着时角标仍可点)
|
||||
root.board_zone.status_badge.disabled = root._modal_flow.badge_greyed_battle() # 互斥域谓词
|
||||
root.level_screen.status_badge.disabled = root._modal_flow.badge_greyed_map()
|
||||
root.ui.battle.board_zone.status_badge.disabled = root.ui.modals.flow.badge_greyed_battle() # 互斥域谓词
|
||||
root.ui.modals.level_screen.status_badge.disabled = root.ui.modals.flow.badge_greyed_map()
|
||||
# 调试浮层角标(全局浮层任何 UI 不遮挡——可见性不随场景变,只抬 offset
|
||||
# 避让:地图屏底栏 85px 常驻,角标抬至底栏上方 16px;其余场景右下角 16px 贴底)
|
||||
root.debug_badge.offset_bottom = -101.0 if map_on else -16.0
|
||||
root.ui.screens.debug_badge.offset_bottom = -101.0 if map_on else -16.0
|
||||
|
||||
|
||||
|
||||
@@ -131,19 +131,19 @@ static func _on_wallet_changed(root: Variant, event: String, gp: int, extra: Dic
|
||||
root.logger.log_wallet(event, gp, extra)
|
||||
if String(event) == "gain":
|
||||
root.run_scope.last_gain_amount = int(extra.get("amount", 0)) # GP 增量取数(每战恰一笔 gain)
|
||||
if root.shop_overlay.visible and root.run_scope.runner != null:
|
||||
if root.ui.modals.shop_overlay.visible and root.run_scope.runner != null:
|
||||
if root.run_scope.runner.shop.is_shop_pool():
|
||||
root.shop_overlay.refresh_shelf_state(int(root.run_scope.runner.gp)) # pool 面余额联动(货架内容仅买后重抽变)
|
||||
root.ui.modals.shop_overlay.refresh_shelf_state(int(root.run_scope.runner.gp)) # pool 面余额联动(货架内容仅买后重抽变)
|
||||
else:
|
||||
root.shop_overlay.refresh_state(root.run_scope.runner.shop.shop_products(), int(root.run_scope.runner.gp))
|
||||
root.ui.modals.shop_overlay.refresh_state(root.run_scope.runner.shop.shop_products(), int(root.run_scope.runner.gp))
|
||||
root._refresh_presentations()
|
||||
|
||||
|
||||
|
||||
static func _on_legend_toggled(root: Variant) -> void:
|
||||
if root.legend_overlay.is_open():
|
||||
root._close_modal(root.legend_overlay)
|
||||
if root.ui.modals.legend_overlay.is_open():
|
||||
root.ui.modals.flow.close(root.ui.modals.legend_overlay)
|
||||
else:
|
||||
root._open_modal(root.legend_overlay)
|
||||
root.ui.modals.flow.open(root.ui.modals.legend_overlay)
|
||||
|
||||
|
||||
+116
-121
@@ -1,7 +1,7 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 组合根 UI 装配分步承接件:三屏壳实例化、视图句柄接线、覆盖层装配与模态编排注入按步拆开。
|
||||
## 本件是 main._build_ui 的逐字分步搬运(成员访问改 root 前缀)——main 侧留同名薄委托,装配序与 z 序零改动。
|
||||
## 层序唯一事实源仍是 main.LAYER_CONTRACT + _apply_layer_contract(本件不持有层序逻辑)。
|
||||
## 层序唯一事实源=root.ui.LAYER_CONTRACT + root.ui.apply_layer_contract(ui_registry.gd——本件不持有层序逻辑)。
|
||||
extends RefCounted
|
||||
|
||||
const UiAssembler := preload("res://presentation/ui_assembler.gd")
|
||||
@@ -35,50 +35,48 @@ static func _build_screens(root: Variant) -> void:
|
||||
# 屏内 level_screen 与 battle_root 无同时可见态(屏级互斥+模态栈遮挡),层内先后零视觉差;
|
||||
# 视觉共存对逐对验算:legend/battle(图例战斗中开——legend 在冒险屏壳之后=其上 ✓)、
|
||||
# debug/level_screen(浮窗最后子=其上 ✓)、shop/level_screen(商店节点域弹出——模态均在屏壳上 ✓)。
|
||||
root._adventure_screen = AdventureScreenScene.instantiate()
|
||||
root.add_child(root._adventure_screen)
|
||||
root._menu_screen = MenuScreenScene.instantiate()
|
||||
root.add_child(root._menu_screen)
|
||||
root._start_overlay = root._menu_screen.start_overlay
|
||||
root._battle_root = root._adventure_screen.battle_root
|
||||
root.stage = root._adventure_screen.stage
|
||||
root.log_view = root._adventure_screen.log_view
|
||||
root.info_panel = root._adventure_screen.info_panel
|
||||
root.board_zone = root._adventure_screen.board_zone
|
||||
root.board_view = root._adventure_screen.board_view # 探针兼容别名(= board_zone.board_view)
|
||||
root.ap_bar = root._adventure_screen.ap_bar # 探针兼容别名(= board_zone.ap_bar)
|
||||
root.settlement_fx = root._adventure_screen.settlement_fx
|
||||
root.level_screen = root._adventure_screen.level_screen
|
||||
root.ui.screens.adventure_screen = AdventureScreenScene.instantiate()
|
||||
root.add_child(root.ui.screens.adventure_screen)
|
||||
root.ui.screens.menu_screen = MenuScreenScene.instantiate()
|
||||
root.add_child(root.ui.screens.menu_screen)
|
||||
root.ui.screens.start_overlay = root.ui.screens.menu_screen.start_overlay
|
||||
root.ui.screens.battle_root = root.ui.screens.adventure_screen.battle_root
|
||||
root.ui.battle.stage = root.ui.screens.adventure_screen.stage
|
||||
root.ui.battle.log_view = root.ui.screens.adventure_screen.log_view
|
||||
root.ui.battle.info_panel = root.ui.screens.adventure_screen.info_panel
|
||||
root.ui.battle.board_zone = root.ui.screens.adventure_screen.board_zone
|
||||
root.ui.battle.settlement_fx = root.ui.screens.adventure_screen.settlement_fx
|
||||
root.ui.modals.level_screen = root.ui.screens.adventure_screen.level_screen
|
||||
|
||||
static func _build_battle_wiring(root: Variant) -> void:
|
||||
# 结算行排播泵(本体在 row_pacer.gd;本类注入视图句柄与呈现刷新回调,零信号连接)
|
||||
root._row_pacer = RowPacer.new()
|
||||
root._row_pacer.log_view = root.log_view
|
||||
root._row_pacer.stage = root.stage
|
||||
root._row_pacer.board_zone = root.board_zone
|
||||
root._row_pacer.settlement_fx = root.settlement_fx
|
||||
root._row_pacer.board_view = root.board_view
|
||||
root._row_pacer.refresh_request = root._refresh_presentations
|
||||
root._row_pacer.pump_ended = root._on_pump_ended # 演播完边沿统一派发(挂起 modal 优先,其次锁期点击重放)
|
||||
root.add_child(root._row_pacer)
|
||||
root.ui.screens.row_pacer = RowPacer.new()
|
||||
root.ui.screens.row_pacer.log_view = root.ui.battle.log_view
|
||||
root.ui.screens.row_pacer.stage = root.ui.battle.stage
|
||||
root.ui.screens.row_pacer.board_zone = root.ui.battle.board_zone
|
||||
root.ui.screens.row_pacer.settlement_fx = root.ui.battle.settlement_fx
|
||||
root.ui.screens.row_pacer.board_view = root.ui.battle.board_zone.board_view
|
||||
root.ui.screens.row_pacer.refresh_request = root._refresh_presentations
|
||||
root.ui.screens.row_pacer.pump_ended = root.ui.modals.flow.pump_ended # 演播完边沿统一派发(挂起 modal 优先,其次锁期点击重放)
|
||||
root.add_child(root.ui.screens.row_pacer)
|
||||
# 秘宝两步交互(本体在 treasure_interact_controller.gd;settlement/runner/grant 随关卡/战斗更替,
|
||||
# 经现值回调注入——仿 identity_resolver 只读回调先例;grant 每战新建编排器,现值取件防悬挂)
|
||||
root._treasure_ui = TreasureInteractController.new({
|
||||
"db": root.db, "meta": root.meta, "info_panel": root.info_panel, "toast": root.board_zone.toast,
|
||||
"board_view": root.board_view,
|
||||
"db": root.db, "meta": root.meta, "info_panel": root.ui.battle.info_panel, "toast": root.ui.battle.board_zone.toast,
|
||||
"board_view": root.ui.battle.board_zone.board_view,
|
||||
"settlement_get": func() -> Variant: return root.run_scope.settlement,
|
||||
"runner_get": func() -> Variant: return root.run_scope.runner,
|
||||
"grant_get": func() -> Variant: return root.run_scope.settlement.reward_grant() if root.run_scope.settlement != null else null,
|
||||
})
|
||||
|
||||
root.board_zone.action_requested.connect(root._on_board_action)
|
||||
root.board_zone.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(战斗屏 HUD 角标)
|
||||
root.info_panel.treasure_slot_clicked.connect(root._treasure_ui.on_slot_clicked) # 两步交互第一步
|
||||
root.board_zone.stop_pressed.connect(root._on_stop_pressed)
|
||||
root.board_zone.refresh_confirmed.connect(root._on_refresh_confirmed)
|
||||
root.board_zone.legend_toggled.connect(root._on_legend_toggled)
|
||||
root.board_zone.hover_forbidden.connect(root._on_hover_forbidden)
|
||||
root.board_zone.chord_attempted.connect(root._on_chord_attempted)
|
||||
root.ui.battle.board_zone.action_requested.connect(root._on_board_action)
|
||||
root.ui.battle.board_zone.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(战斗屏 HUD 角标)
|
||||
root.ui.battle.info_panel.treasure_slot_clicked.connect(root._treasure_ui.on_slot_clicked) # 两步交互第一步
|
||||
root.ui.battle.board_zone.stop_pressed.connect(root._on_stop_pressed)
|
||||
root.ui.battle.board_zone.refresh_confirmed.connect(root._on_refresh_confirmed)
|
||||
root.ui.battle.board_zone.legend_toggled.connect(root._on_legend_toggled)
|
||||
root.ui.battle.board_zone.hover_forbidden.connect(root._on_hover_forbidden)
|
||||
root.ui.battle.board_zone.chord_attempted.connect(root._on_chord_attempted)
|
||||
|
||||
root.logger = PlaytestLogger.new()
|
||||
root.logger.ap_per_round = int(root.db.run.action_economy.ap_per_round)
|
||||
@@ -88,136 +86,135 @@ static func _build_overlays(root: Variant) -> void:
|
||||
# < 商店 < 营地 < 设置 < 状态面板(只读) < 生涯页(只读) < 确认弹窗 < 调试浮窗;
|
||||
# 关卡画面住冒险屏壳(树序在模态之下)——登记序仍夹在节点层与三选一层之间,遮挡语义零改动)
|
||||
var overlays: Dictionary[Variant, Variant] = UiAssembler.build_overlays(root, root.db)
|
||||
root.legend_overlay = overlays.legend_overlay
|
||||
root.node_overlay = overlays.node_overlay
|
||||
root._end_overlay = overlays.end_overlay
|
||||
root.shop_overlay = overlays.shop_overlay
|
||||
root._camp_screen = CampScreenScene.instantiate()
|
||||
root.add_child(root._camp_screen)
|
||||
root.camp_overlay = root._camp_screen.camp_overlay
|
||||
root.ui.modals.legend_overlay = overlays.legend_overlay
|
||||
root.ui.modals.node_overlay = overlays.node_overlay
|
||||
root.ui.modals.end_overlay = overlays.end_overlay
|
||||
root.ui.modals.shop_overlay = overlays.shop_overlay
|
||||
root.ui.screens.camp_screen = CampScreenScene.instantiate()
|
||||
root.add_child(root.ui.screens.camp_screen)
|
||||
root.ui.modals.camp_overlay = root.ui.screens.camp_screen.camp_overlay
|
||||
# 营地屏壳插位(夹商店层与设置层之间)由 LAYER_CONTRACT 营地槽统一落位——禁散落 move_child
|
||||
root.legend_overlay.closed.connect(root._on_modal_closed.bind(root.legend_overlay))
|
||||
root.node_overlay.continue_pressed.connect(root._on_node_continue)
|
||||
root.level_screen.route_chosen.connect(root._on_route_chosen)
|
||||
root.level_screen.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(地图屏顶栏角标)
|
||||
root.level_screen.abandon_requested.connect(root._on_abandon_requested) # 放弃入口
|
||||
root._end_overlay.continue_pressed.connect(root._on_battle_end_continue)
|
||||
root.ui.modals.legend_overlay.closed.connect(root.ui.modals.flow.close.bind(root.ui.modals.legend_overlay))
|
||||
root.ui.modals.node_overlay.continue_pressed.connect(root._on_node_continue)
|
||||
root.ui.modals.level_screen.route_chosen.connect(root._on_route_chosen)
|
||||
root.ui.modals.level_screen.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(地图屏顶栏角标)
|
||||
root.ui.modals.level_screen.abandon_requested.connect(root._on_abandon_requested) # 放弃入口
|
||||
root.ui.modals.end_overlay.continue_pressed.connect(root._on_battle_end_continue)
|
||||
|
||||
static func _build_aux_panels(root: Variant) -> void:
|
||||
root.settings_overlay = SettingsOverlay.new(root.settings_store) # 最小设置面(音量/全屏/减动效)
|
||||
root.add_child(root.settings_overlay)
|
||||
root.settings_overlay.closed.connect(root._on_settings_closed)
|
||||
root.ui.modals.settings_overlay = SettingsOverlay.new(root.settings_store) # 最小设置面(音量/全屏/减动效)
|
||||
root.add_child(root.ui.modals.settings_overlay)
|
||||
root.ui.modals.settings_overlay.closed.connect(root._on_settings_closed)
|
||||
# 局内状态面板·只读(登记设置层同档带 dim 遮罩,确认弹窗之前;
|
||||
# 数据=组合根开层实时读 MetaProgression 访问器喂入,本层零写通路)
|
||||
root.status_panel = StatusPanel.new()
|
||||
root.add_child(root.status_panel)
|
||||
root.status_panel.closed.connect(root._on_status_closed)
|
||||
root.ui.modals.status_panel = StatusPanel.new()
|
||||
root.add_child(root.ui.modals.status_panel)
|
||||
root.ui.modals.status_panel.closed.connect(root._on_status_closed)
|
||||
# 生涯页·只读全屏页(登记序=营地同档,设置层旁、确认弹窗之前;
|
||||
# 树序同步=addChild 同位(登记序=树序=z 序纪律);数据=组合根开层实时读 SaveManager 计数
|
||||
# 访问器+MetaProgression 访问器喂入,本层零写通路零跳转;入口=开始层「营地与系统」组生涯钮)
|
||||
root.career_overlay = CareerOverlay.new()
|
||||
root.add_child(root.career_overlay)
|
||||
root.career_overlay.closed.connect(root._on_career_closed)
|
||||
root.ui.modals.career_overlay = CareerOverlay.new()
|
||||
root.add_child(root.ui.modals.career_overlay)
|
||||
root.ui.modals.career_overlay.closed.connect(root._on_career_closed)
|
||||
# 选档界面(营地交互重做卡③):登记序=生涯同档(树序同步=addChild 同位);三动作只发意图信号
|
||||
#(激活/创建/删除+确认分派归组合根——视图零数据层依赖);入口=「进入存档」钮(卡② mn3 接线)。
|
||||
root.save_select_overlay = SaveSelectOverlay.new()
|
||||
root.add_child(root.save_select_overlay)
|
||||
root.save_select_overlay.closed.connect(root._on_save_select_closed)
|
||||
root.save_select_overlay.new_slot_requested.connect(root._on_save_select_new)
|
||||
root.save_select_overlay.load_slot_requested.connect(root._on_save_select_load)
|
||||
root.save_select_overlay.delete_slot_requested.connect(root._on_save_select_delete)
|
||||
root.ui.modals.save_select_overlay = SaveSelectOverlay.new()
|
||||
root.add_child(root.ui.modals.save_select_overlay)
|
||||
root.ui.modals.save_select_overlay.closed.connect(root._on_save_select_closed)
|
||||
root.ui.modals.save_select_overlay.new_slot_requested.connect(root._on_save_select_new)
|
||||
root.ui.modals.save_select_overlay.load_slot_requested.connect(root._on_save_select_load)
|
||||
root.ui.modals.save_select_overlay.delete_slot_requested.connect(root._on_save_select_delete)
|
||||
|
||||
static func _build_curio(root: Variant) -> void:
|
||||
# 提升三选一覆盖层(z 序 = 节点层之上、终局层之下;确认后同拍推进;
|
||||
# 插位由 LAYER_CONTRACT curio 槽统一落位——禁散落 move_child)
|
||||
root.curio_overlay = CurioPickOverlay.new()
|
||||
root.add_child(root.curio_overlay)
|
||||
root.ui.modals.curio_overlay = CurioPickOverlay.new()
|
||||
root.add_child(root.ui.modals.curio_overlay)
|
||||
# 提升三选一与替换子交互(本体在 curio_interact_controller.gd;本类只接线与回调注入,
|
||||
# 模态栈/演播后排窗/呈现刷新/fatal 经回调注入,adventure/runner 现值回调)
|
||||
root._bonus = CurioInteractController.new(CurioFlowContext.new(root.db,
|
||||
{"curio_overlay": root.curio_overlay, "end_overlay": root._end_overlay,
|
||||
"board_view": root.board_view, "toast": root.board_zone.toast},
|
||||
{"curio_overlay": root.ui.modals.curio_overlay, "end_overlay": root.ui.modals.end_overlay,
|
||||
"board_view": root.ui.battle.board_zone.board_view, "toast": root.ui.battle.board_zone.toast},
|
||||
{"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager,
|
||||
"adventure_get": func() -> Variant: return root.run_scope.adventure,
|
||||
"runner_get": func() -> Variant: return root.run_scope.runner,
|
||||
"adventure_mode_get": func() -> Variant: return root.run_scope.is_adventure},
|
||||
{"open_modal": root._open_modal, "open_modal_after_playback": root._open_modal_after_playback,
|
||||
"close_modal": root._close_modal, "refresh_presentations": root._refresh_presentations,
|
||||
{"open_modal": root.ui.modals.flow.open, "open_modal_after_playback": root.ui.modals.flow.open_after_playback,
|
||||
"close_modal": root.ui.modals.flow.close, "refresh_presentations": root._refresh_presentations,
|
||||
"fatal_screen": root._fatal_screen,
|
||||
"gp_summary": _gp_summary_cb(root)})) # 构造参数收拢 context(u20 三束)
|
||||
root.curio_overlay.pick_confirmed.connect(root._bonus.on_pick_confirmed)
|
||||
root.curio_overlay.continue_pressed.connect(root._bonus.on_continue)
|
||||
root.curio_overlay.refresh_requested.connect(root._bonus.on_refresh)
|
||||
root.ui.modals.curio_overlay.pick_confirmed.connect(root._bonus.on_pick_confirmed)
|
||||
root.ui.modals.curio_overlay.continue_pressed.connect(root._bonus.on_continue)
|
||||
root.ui.modals.curio_overlay.refresh_requested.connect(root._bonus.on_refresh)
|
||||
|
||||
static func _build_menu_wiring(root: Variant) -> void:
|
||||
# 菜单屏接线(句柄经屏壳暴露;三入口化——进入存档/设置/退出游戏,卡② mn3:
|
||||
# 进入存档钮落点=卡③选档层 _open_save_select,点击链在此闭环)
|
||||
root._menu_screen.entersavebtn.pressed.connect(root._open_save_select)
|
||||
root.ui.screens.menu_screen.entersavebtn.pressed.connect(root._open_save_select)
|
||||
# 调试入口=UI 最顶层全局浮层角标(任何 UI 不遮挡——菜单/战斗/地图三枚
|
||||
# 屏内角标由全局浮层承载;连接归组合根,树序/登记面见 LAYER_CONTRACT 末位)
|
||||
root.debug_badge = UiAssembler.build_debug_badge(root)
|
||||
root.add_child(root.debug_badge)
|
||||
root.debug_badge.pressed.connect(root._on_debug_badge_pressed)
|
||||
root.ui.screens.debug_badge = UiAssembler.build_debug_badge(root)
|
||||
root.add_child(root.ui.screens.debug_badge)
|
||||
root.ui.screens.debug_badge.pressed.connect(root._on_debug_badge_pressed)
|
||||
# 营地/商店投影(本体在 camp_shop_presenter.gd;本类只接线、探针缝保缝转发;
|
||||
# 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入)
|
||||
root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db,
|
||||
{"camp_overlay": root.camp_overlay, "shop_overlay": root.shop_overlay,
|
||||
"toast": root.board_zone.toast, "start_overlay": root._start_overlay, "logger": root.logger},
|
||||
{"camp_overlay": root.ui.modals.camp_overlay, "shop_overlay": root.ui.modals.shop_overlay,
|
||||
"toast": root.ui.battle.board_zone.toast, "start_overlay": root.ui.screens.start_overlay, "logger": root.logger},
|
||||
{"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager,
|
||||
"runner_get": func() -> Variant: return root.run_scope.runner},
|
||||
{"open_modal": root._open_modal, "close_modal": root._close_modal,
|
||||
{"open_modal": root.ui.modals.flow.open, "close_modal": root.ui.modals.flow.close,
|
||||
"refresh_presentations": root._refresh_presentations})) # 构造参数收拢 context(u21 三束)
|
||||
root.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy)
|
||||
root.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1
|
||||
root.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave)
|
||||
root.camp_overlay.buy_requested.connect(root._camp_pres.on_camp_buy)
|
||||
root.camp_overlay.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle)
|
||||
root.camp_overlay.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy)
|
||||
root.camp_overlay.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle)
|
||||
root.camp_overlay.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression)
|
||||
root.camp_overlay.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock)
|
||||
root.camp_overlay.forge_select_requested.connect(root._camp_pres.on_camp_forge_select)
|
||||
root.camp_overlay.closed.connect(root._camp_pres.on_camp_closed)
|
||||
root._menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口
|
||||
root._menu_screen.quitbtn.pressed.connect(root._quit_game) # 退出游戏提级一级菜单
|
||||
root.ui.modals.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy)
|
||||
root.ui.modals.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1
|
||||
root.ui.modals.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave)
|
||||
root.ui.modals.camp_overlay.buy_requested.connect(root._camp_pres.on_camp_buy)
|
||||
root.ui.modals.camp_overlay.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle)
|
||||
root.ui.modals.camp_overlay.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy)
|
||||
root.ui.modals.camp_overlay.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle)
|
||||
root.ui.modals.camp_overlay.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression)
|
||||
root.ui.modals.camp_overlay.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock)
|
||||
root.ui.modals.camp_overlay.forge_select_requested.connect(root._camp_pres.on_camp_forge_select)
|
||||
root.ui.modals.camp_overlay.closed.connect(root._camp_pres.on_camp_closed)
|
||||
root.ui.screens.menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口
|
||||
root.ui.screens.menu_screen.quitbtn.pressed.connect(root._quit_game) # 退出游戏提级一级菜单
|
||||
|
||||
static func _build_debug_and_resolvers(root: Variant) -> void:
|
||||
var inter: Dictionary[Variant, Variant] = UiAssembler.build_inter_overlay(root)
|
||||
root._inter_overlay = inter.overlay
|
||||
root._inter_label = inter.label
|
||||
root.ui.screens.inter_overlay = inter.overlay
|
||||
root.ui.screens.inter_label = inter.label
|
||||
inter.next_btn.pressed.connect(root._on_inter_next_pressed)
|
||||
|
||||
# 确认弹窗(新冒险互斥确认弹窗化):树序落在开始层/过场层之后 = 子级最末
|
||||
#(纪律「登记序 = 树序 = z 序」——confirm_dialog.open 禁运行时 move_child 自提抬层;
|
||||
# 弹窗只在开始菜单域弹出,绘制序恒最上;
|
||||
# 诊断转储入口在调试浮窗)
|
||||
root.confirm_dialog = ConfirmDialog.new()
|
||||
root.add_child(root.confirm_dialog)
|
||||
root.ui.modals.confirm_dialog = ConfirmDialog.new()
|
||||
root.add_child(root.ui.modals.confirm_dialog)
|
||||
# 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer,
|
||||
# confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工)
|
||||
root.confirm_dialog.confirmed.connect(root._on_confirm_accepted)
|
||||
root.confirm_dialog.cancelled.connect(root._on_confirm_declined)
|
||||
root.ui.modals.confirm_dialog.confirmed.connect(root._on_confirm_accepted)
|
||||
root.ui.modals.confirm_dialog.cancelled.connect(root._on_confirm_declined)
|
||||
|
||||
# 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口;
|
||||
# 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块;
|
||||
# 诊断转储入口在本浮窗,玩家画面零常驻调试件)。
|
||||
# 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态);
|
||||
# 开窗唯一入口 = _show_debug_overlay(C6 单口——角标/探针/启动路径全收口)
|
||||
root.debug_overlay = DebugOverlay.new(root.db, int(root.db.combat.hero.atk), int(root.db.combat.hero.max_hp))
|
||||
root.add_child(root.debug_overlay)
|
||||
root._seed_edit = root.debug_overlay.seed_edit # 种子读取面在浮窗(_seed_value 唯一消费点不变)
|
||||
root.debug_overlay.closed.connect(root._on_modal_closed.bind(root.debug_overlay))
|
||||
root.debug_overlay.apply_requested.connect(root._on_tune_apply)
|
||||
root.debug_overlay.simulate_requested.connect(root._on_tune_simulate)
|
||||
root.debug_overlay.debug_levels_requested.connect(root._start_debug_levels)
|
||||
root.ui.modals.debug_overlay = DebugOverlay.new(root.db, int(root.db.combat.hero.atk), int(root.db.combat.hero.max_hp))
|
||||
root.add_child(root.ui.modals.debug_overlay)
|
||||
root.ui.modals.debug_overlay.closed.connect(root.ui.modals.flow.close.bind(root.ui.modals.debug_overlay))
|
||||
root.ui.modals.debug_overlay.apply_requested.connect(root._on_tune_apply)
|
||||
root.ui.modals.debug_overlay.simulate_requested.connect(root._on_tune_simulate)
|
||||
root.ui.modals.debug_overlay.debug_levels_requested.connect(root._start_debug_levels)
|
||||
|
||||
# 棋盘解析回调族(有态绑定件——board_resolvers.gd 承接;组合根只接线;局部持有不占类成员位)
|
||||
var resolvers = BoardResolversScript.new(root.db, root.debug_overlay, func() -> Variant: return root.run_scope.settlement)
|
||||
var resolvers = BoardResolversScript.new(root.db, root.ui.modals.debug_overlay, func() -> Variant: return root.run_scope.settlement)
|
||||
# 奖励徽章数据源:地图战斗资源 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效)
|
||||
root.board_view.set_identity_resolver(resolvers.identity_lookup)
|
||||
root.ui.battle.board_zone.board_view.set_identity_resolver(resolvers.identity_lookup)
|
||||
# 数字遮蔽数据源:地形变异 hide_number 解析只读回调(信息型呈现——board_view 渲染面读取)
|
||||
root.board_view.set_variant_mask_resolver(resolvers.variant_mask_lookup)
|
||||
root.board_view.set_variant_mark_resolver(resolvers.variant_mark_lookup) # flat_damage 未开格角标
|
||||
root.ui.battle.board_zone.board_view.set_variant_mask_resolver(resolvers.variant_mask_lookup)
|
||||
root.ui.battle.board_zone.board_view.set_variant_mark_resolver(resolvers.variant_mark_lookup) # flat_damage 未开格角标
|
||||
# 调试会话(英雄生效值预览回调绑同一解析件——创建晚于浮窗、随 _build_ui 一次接线)
|
||||
root._debug = DebugSessionScript.new(root.db, root.get_node(root.AUTOLOAD_PATH_RNG),
|
||||
func() -> Dictionary: return resolvers.debug_hero_override(), root._seed_value)
|
||||
@@ -228,24 +225,22 @@ static func _build_modal_flow(root: Variant) -> void:
|
||||
# 模态登记(缺陷⑤纪律——登记序 = 树序 = z 序):层序唯一事实源=LAYER_CONTRACT,
|
||||
# _apply_layer_contract 统一落位(关卡画面宿住冒险屏壳、营地屏壳夹商店与设置层之间、
|
||||
# 三选一对位终局层之下等特例均在合同数据内表达,禁散落 move_child)
|
||||
# 模态编排注入(ModalFlow 纯逻辑类:依赖仅 ModalStack+查询 Callable)
|
||||
root._modal_flow = ModalFlow.new()
|
||||
root._modal_flow.start_visible_get = func() -> bool: return root._start_overlay.visible
|
||||
root._modal_flow.inter_visible_get = func() -> bool: return root._inter_overlay.visible
|
||||
root._modal_flow.settlement_get = func() -> Variant: return root.run_scope.settlement
|
||||
root._modal_flow.rows_empty_get = func() -> bool: return root._row_pacer.pending_rows().is_empty()
|
||||
root._modal_flow.flush_rows = root._flush_row_queue
|
||||
root._modal_flow.on_open_sync = func() -> void:
|
||||
# 模态编排注入(ModalFlow 纯逻辑类:依赖仅 ModalStack+查询 Callable——实例随 ui.modals 容器构造建立)
|
||||
root.ui.modals.flow.start_visible_get = func() -> bool: return root.ui.screens.start_overlay.visible
|
||||
root.ui.modals.flow.inter_visible_get = func() -> bool: return root.ui.screens.inter_overlay.visible
|
||||
root.ui.modals.flow.settlement_get = func() -> Variant: return root.run_scope.settlement
|
||||
root.ui.modals.flow.rows_empty_get = func() -> bool: return root.ui.screens.row_pacer.pending_rows().is_empty()
|
||||
root.ui.modals.flow.flush_rows = root.ui.screens.row_pacer.flush
|
||||
root.ui.modals.flow.on_open_sync = func() -> void:
|
||||
root._sync_status_entry() # 状态面板入口角标可见性/置灰随开态同步
|
||||
root._sync_debug_overlay() # 浮窗当局数据/按钮态随开态同步
|
||||
root._modal_flow.on_close_sync = func() -> void:
|
||||
root.ui.modals.flow.on_close_sync = func() -> void:
|
||||
root._sync_status_entry() # 关闭恢复角标可点态(无残留——同源口径)
|
||||
root._sync_debug_overlay() # 他层关闭复活浮窗时同步刷新(关后自动恢复语义配套)
|
||||
root._modal_flow.replay_click = root._replay_locked_click
|
||||
root._modals = root._modal_flow.stack # 探针 _list 登记序断言缝(ui_struct 登记序=z 序纪律断言)
|
||||
root.ui.modals.flow.replay_click = root._replay_locked_click
|
||||
# 视觉层合同装配(唯一事实源=LAYER_CONTRACT:树序+登记序同源派生——改序只改合同表)
|
||||
root._apply_layer_contract()
|
||||
root._tune_sim = TuneSimController.new(root.db, root.debug_overlay, int(root.db.combat.hero.max_hp))
|
||||
root.ui.apply_layer_contract(root)
|
||||
root._tune_sim = TuneSimController.new(root.db, root.ui.modals.debug_overlay, int(root.db.combat.hero.max_hp))
|
||||
|
||||
static func fatal_screen(root: Variant, msg: String) -> void:
|
||||
var l := UiKit.label(msg,UiKit.FS_SUB, true, UiKit.RED_ALERT)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
## 战区视图域句柄册:战斗屏 A–D 区视图与结算演出覆盖层的注册面域容器。
|
||||
## 纯字段无方法;RefCounted 不挂树——节点本体树位与 add_child 归属由组合根与装配承接件负责。
|
||||
class_name UiBattle
|
||||
extends RefCounted
|
||||
|
||||
var stage: BattleStageView # B 区(含 log_view 明细带)
|
||||
var log_view: BattleLogView
|
||||
var info_panel: HeroInfoPanelView # C 区(攻击/AC 数据框+两预留区)
|
||||
var board_zone: BoardZoneView # D 区(含 ap_bar/board_view/toast/fx)
|
||||
var settlement_fx: SettlementFxView # 结算演出覆盖层(跨区弹道/光带——A–D 之上、模态之下)
|
||||
@@ -0,0 +1 @@
|
||||
uid://d35j5hry1f40v
|
||||
@@ -0,0 +1,32 @@
|
||||
## 模态层域句柄册:十三个模态覆盖层句柄+模态栈编排(ModalFlow——登记/开合/输入锁谓词)的注册面域容器。
|
||||
## RefCounted 不挂树;模态登记唯一口=register_layer(anchors 撑满宿主+排入 ModalStack)。
|
||||
## flow 随容器构造即建(装配早期注入点经 root.ui.modals.flow.* 取 Callable——晚于 flow 建立时点)。
|
||||
class_name UiModals
|
||||
extends RefCounted
|
||||
|
||||
var flow: ModalFlow
|
||||
var legend_overlay: LegendOverlay
|
||||
var node_overlay: NodeOverlay
|
||||
var level_screen: LevelMapScreen
|
||||
var curio_overlay: CurioPickOverlay
|
||||
var end_overlay: BattleEndOverlay
|
||||
var shop_overlay: ShopOverlay
|
||||
var camp_overlay: CampOverlay
|
||||
var settings_overlay: SettingsOverlay
|
||||
var status_panel: StatusPanel
|
||||
var career_overlay: CareerOverlay
|
||||
var save_select_overlay: SaveSelectOverlay
|
||||
var confirm_dialog: ConfirmDialog
|
||||
var debug_overlay: DebugOverlay
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
flow = ModalFlow.new() # 查询/钩子 Callable 由 _build_modal_flow 注入(注入时点不变——注入值面向已在座实例)
|
||||
|
||||
|
||||
func register_layer(m: Control, tags: Array = []) -> void:
|
||||
# 撑满宿主(Control 默认锚 = 0×0——全屏变暗层与居中布局整体失效;route 选路层预览截图实证后
|
||||
# 统一收口:六个模态同病,探针经信号直发绕过真实点击从未暴露。anchors_and_offsets 而非 anchors
|
||||
# ——后者会把控件重置回最小内容尺寸,ui_assembler 头注同款纪律)
|
||||
m.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
flow.register(m, tags) # 排除组随层登记(non_locking=调试浮窗/screen_state=关卡地图屏)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dbs4qveqvr5hg
|
||||
@@ -0,0 +1,101 @@
|
||||
## 界面注册面:呈现层界面元素清单的唯一事实源——哪些层、什么序、地址在哪。
|
||||
## 持有三域容器(screens/battle/modals)、LAYER_CONTRACT 合同表、node(key) 合同寻址、
|
||||
## apply_layer_contract(root) 唯一落位口(全仓唯一 move_child 点)。
|
||||
## RefCounted 不挂树:零信号连接、零会话期引用、零核心写调用——组合根单点装配不变。
|
||||
class_name UiRegistry
|
||||
extends RefCounted
|
||||
|
||||
const ModalStack := preload("res://presentation/modal_stack.gd")
|
||||
|
||||
# ==================== 视觉层合同(唯一事实源——树序=登记序=z 序;改序只改此表) ====================
|
||||
# kind: shell=屏壳容器(成员随壳不占独立树位;level_screen 例外另占登记位)/modal=模态层(入登记册,
|
||||
# tags 随登记)/state=全屏态(占树位不入登记册)/node=非视觉节点(占树位不入登记册)。
|
||||
# host=宿住屏壳(树序随宿主、登记序按本合同);shell=模态成员的屏壳(成员与屏壳同槽:屏壳占该树位、
|
||||
# 成员占该登记位)。落位=apply_layer_contract(全仓唯一层序落位点,add_child 之后统一排序+登记)。
|
||||
const LAYER_CONTRACT: Array[Dictionary] = [
|
||||
{"key": "adventure_screen", "kind": "shell"},
|
||||
{"key": "menu_screen", "kind": "shell"},
|
||||
{"key": "row_pacer", "kind": "node"},
|
||||
{"key": "legend_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "node_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "level_screen", "kind": "modal", "tags": [ModalStack.TAG_SCREEN_STATE], "host": "adventure_screen"},
|
||||
{"key": "curio_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "end_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "shop_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "camp_overlay", "kind": "modal", "tags": [], "shell": "camp_screen"},
|
||||
{"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]},
|
||||
{"key": "debug_badge", "kind": "float"}, # 调试入口全局浮层——末位=最顶绘制序;
|
||||
# float 类不注册 ModalStack(open() 遮挡记账只隐藏登记层)→ 任何 UI 打开都不遮它
|
||||
]
|
||||
|
||||
var screens: UiScreens
|
||||
var battle: UiBattle
|
||||
var modals: UiModals
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
screens = UiScreens.new()
|
||||
battle = UiBattle.new()
|
||||
modals = UiModals.new()
|
||||
|
||||
|
||||
# 合同层寻址(key→节点;纯查表不含层序语义——层序唯一事实源=LAYER_CONTRACT)
|
||||
func node(key: String) -> Node:
|
||||
match key:
|
||||
"adventure_screen": return screens.adventure_screen
|
||||
"menu_screen": return screens.menu_screen
|
||||
"row_pacer": return screens.row_pacer
|
||||
"legend_overlay": return modals.legend_overlay
|
||||
"node_overlay": return modals.node_overlay
|
||||
"level_screen": return modals.level_screen
|
||||
"curio_overlay": return modals.curio_overlay
|
||||
"end_overlay": return modals.end_overlay
|
||||
"shop_overlay": return modals.shop_overlay
|
||||
"camp_screen": return screens.camp_screen
|
||||
"camp_overlay": return modals.camp_overlay
|
||||
"settings_overlay": return modals.settings_overlay
|
||||
"status_panel": return modals.status_panel
|
||||
"career_overlay": return modals.career_overlay
|
||||
"save_select_overlay": return modals.save_select_overlay
|
||||
"inter_overlay": return screens.inter_overlay
|
||||
"confirm_dialog": return modals.confirm_dialog
|
||||
"debug_overlay": return modals.debug_overlay
|
||||
"debug_badge": return screens.debug_badge
|
||||
return null
|
||||
|
||||
|
||||
# 合同装配(_build_ui 尾调用):按 LAYER_CONTRACT 序统一落位——直接子层 move_child 到合同槽、
|
||||
# 模态层按合同序登记;宿住层(host)树序随宿主只登记;成员层(shell)与屏壳同槽(屏壳占树位、成员占登记位)。
|
||||
# 本函数是全仓唯一层序落位点(move_child 单口)——改层序只改 LAYER_CONTRACT。
|
||||
func apply_layer_contract(root: Control) -> void:
|
||||
var slot := 0
|
||||
for entry: Dictionary in LAYER_CONTRACT:
|
||||
var tags: Array[Variant] = entry.get("tags", [])
|
||||
if entry.has("shell"):
|
||||
var shell: Node = node(String(entry["shell"]))
|
||||
var member: Node = node(String(entry["key"]))
|
||||
if shell != null:
|
||||
root.move_child(shell, slot)
|
||||
slot += 1
|
||||
if member != null:
|
||||
modals.register_layer(member as Control, tags)
|
||||
else:
|
||||
push_error("[合同装配] 层缺失: %s" % String(entry["key"]))
|
||||
continue
|
||||
var layer: Node = node(String(entry["key"]))
|
||||
if layer == null:
|
||||
push_error("[合同装配] 层缺失: %s" % String(entry["key"]))
|
||||
continue
|
||||
if entry.has("host"):
|
||||
modals.register_layer(layer as Control, tags)
|
||||
continue
|
||||
root.move_child(layer, slot)
|
||||
slot += 1
|
||||
if String(entry["kind"]) == "modal":
|
||||
modals.register_layer(layer as Control, tags)
|
||||
@@ -0,0 +1 @@
|
||||
uid://d1hahbtan1te8
|
||||
@@ -0,0 +1,16 @@
|
||||
## 屏壳与全屏容器域句柄册:三屏壳、屏内一级子层、全屏态、浮层角标、排播泵节点的注册面域容器。
|
||||
## 纯字段无方法;RefCounted 不挂树——节点本体树位与 add_child 归属由组合根与装配承接件负责。
|
||||
class_name UiScreens
|
||||
extends RefCounted
|
||||
|
||||
const RowPacer := preload("res://presentation/row_pacer.gd") # 排播泵取型(row_pacer.gd 非 class_name 全局)
|
||||
|
||||
var menu_screen: Control
|
||||
var camp_screen: Control
|
||||
var adventure_screen: Control
|
||||
var start_overlay: Control
|
||||
var battle_root: Control
|
||||
var inter_overlay: Control
|
||||
var inter_label: Label
|
||||
var debug_badge: Button
|
||||
var row_pacer: RowPacer
|
||||
@@ -0,0 +1 @@
|
||||
uid://dd8ep7nhmyrdq
|
||||
@@ -37,14 +37,14 @@ func _ready() -> void:
|
||||
# 等关卡画面出图(choices_presented 异步到达——最多 3s 轮询)
|
||||
for i in 60:
|
||||
await get_tree().process_frame
|
||||
if main.level_screen != null and main.level_screen.visible:
|
||||
if main.ui.modals.level_screen != null and main.ui.modals.level_screen.visible:
|
||||
break
|
||||
if main.level_screen == null or not main.level_screen.visible:
|
||||
if main.ui.modals.level_screen == null or not main.ui.modals.level_screen.visible:
|
||||
_fail("关卡画面未出现(函数直调中间态路径)")
|
||||
return
|
||||
for i in 5:
|
||||
await get_tree().process_frame
|
||||
main.level_screen.press_candidate(0)
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
for i in 20:
|
||||
await get_tree().process_frame
|
||||
var st = main.run_scope.settlement
|
||||
|
||||
@@ -23,12 +23,12 @@ func _ready() -> void:
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.isolate_save(main, "smoke_layout_probe", true, false, true)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(确定性禁依赖默认随机分支)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(确定性禁依赖默认随机分支)
|
||||
main._start_adventure_from_menu()
|
||||
for i in range(8):
|
||||
await get_tree().process_frame
|
||||
# 进战斗屏——三区(B 战斗画面/C 角色信息/D 扫雷图)同屏常驻,菜单屏不构成断言面
|
||||
main.level_screen.press_candidate(0)
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
for i in range(20):
|
||||
await get_tree().process_frame
|
||||
for vp in VIEWPORTS:
|
||||
@@ -57,8 +57,9 @@ func _check_one(label: String, main: Node) -> void:
|
||||
if vr.size != Vector2(VIEWPORTS[0]["size"]) and vr.size.y < 1.0:
|
||||
_problems.append("[%s] 逻辑视口退化 %s" % [label, vr.size])
|
||||
var rects := {}
|
||||
var battle_ui: Object = main.get("ui").battle # 注册面战区域容器(字段名与三区 key 同名——合同键即字段名)
|
||||
for key in ["stage", "info_panel", "board_zone"]:
|
||||
var ctl: Control = main.get(key)
|
||||
var ctl: Control = battle_ui.get(key)
|
||||
if ctl == null:
|
||||
_problems.append("[%s] %s = null(三区未装配)" % [label, key])
|
||||
return
|
||||
@@ -73,7 +74,7 @@ func _check_one(label: String, main: Node) -> void:
|
||||
# battle_root 内层根下的全幅 ColorRect)这一缺陷类不在本探针拦截面内,探针对此仍全绿。
|
||||
# 要拦它得改为递归遍历全子树找重叠,那是另一套判据(且极易误报容器父子关系),
|
||||
# 本探针不设此判据。
|
||||
var bv: Control = main.board_view
|
||||
var bv: Control = main.ui.battle.board_zone.board_view
|
||||
if bv == null:
|
||||
_problems.append("[%s] board_view = null" % label)
|
||||
return
|
||||
@@ -89,6 +90,6 @@ func _check_one(label: String, main: Node) -> void:
|
||||
if rects[keys[i]].intersects(rects[keys[j]]):
|
||||
_problems.append("[%s] 顶层区重叠 %s×%s(%s ∩ %s)"
|
||||
% [label, keys[i], keys[j], rects[keys[i]], rects[keys[j]]])
|
||||
var cell: float = main.board_view.get("cell_size")
|
||||
var cell: float = main.ui.battle.board_zone.board_view.get("cell_size")
|
||||
if cell < MIN_CELL:
|
||||
_problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL])
|
||||
@@ -130,7 +130,7 @@ static func open_camp_via_savesel(main, tag: String) -> bool:
|
||||
if FileAccess.file_exists(sp):
|
||||
DirAccess.remove_absolute(sp)
|
||||
main._open_save_select()
|
||||
var new_btn: Button = main.save_select_overlay._new_btns.get(1)
|
||||
var new_btn: Button = main.ui.modals.save_select_overlay._new_btns.get(1)
|
||||
if new_btn == null:
|
||||
return false
|
||||
new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp
|
||||
|
||||
@@ -50,10 +50,10 @@ func _ready() -> void:
|
||||
# 其余任意两屏同时为真=互斥失守。
|
||||
func _screen_seals() -> Dictionary:
|
||||
return {
|
||||
"菜单": main._start_overlay != null and main._start_overlay.visible,
|
||||
"营地": main.camp_overlay != null and main.camp_overlay.visible,
|
||||
"菜单": main.ui.screens.start_overlay != null and main.ui.screens.start_overlay.visible,
|
||||
"营地": main.ui.modals.camp_overlay != null and main.ui.modals.camp_overlay.visible,
|
||||
"冒险": main.run_scope != null and main.run_scope.is_adventure,
|
||||
"终局": main._end_overlay != null and main._end_overlay.visible,
|
||||
"终局": main.ui.modals.end_overlay != null and main.ui.modals.end_overlay.visible,
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ func _frames(n: int = 2) -> void:
|
||||
func _step_boot_menu() -> void:
|
||||
_expect_exclusive("①boot", "菜单")
|
||||
_expect_adventure_seal("①boot", false)
|
||||
if main.debug_overlay != null and main.debug_overlay.visible:
|
||||
if main.ui.modals.debug_overlay != null and main.ui.modals.debug_overlay.visible:
|
||||
_problems.append("①boot:调试浮窗 boot 自动开(菜单优先失守)")
|
||||
|
||||
|
||||
@@ -117,10 +117,10 @@ func _step_menu_to_camp() -> void:
|
||||
|
||||
# ---- ③ 营地 → 菜单(回退路径:营地关闭发自覆盖层自身信号)----
|
||||
func _step_camp_back_to_menu() -> void:
|
||||
if main.camp_overlay == null:
|
||||
if main.ui.modals.camp_overlay == null:
|
||||
_problems.append("③营地→菜单:营地覆盖层句柄缺失")
|
||||
return
|
||||
main.camp_overlay.closed.emit()
|
||||
main.ui.modals.camp_overlay.closed.emit()
|
||||
await _frames()
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
await _frames()
|
||||
@@ -133,22 +133,22 @@ func _step_camp_back_to_menu() -> void:
|
||||
func _step_save_select_open_back() -> void:
|
||||
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_savesel", false, false, true)
|
||||
main.save_manager.slots.attach_slot_template("user://smoke_screen_flow_savesel_slot_%d.json")
|
||||
var esb: Button = ProbeUiUtil.find_button(main._start_overlay, "进入存档", "prefix")
|
||||
var esb: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "进入存档", "prefix")
|
||||
if esb == null:
|
||||
_problems.append("[savesel] mn3 进入存档钮缺失(卡② 交付物)")
|
||||
return
|
||||
esb.pressed.emit() # 真实按钮信号路径 → _open_save_select(卡② mn3 点击链——卡③ 直调中间态就此闭环)
|
||||
await get_tree().process_frame
|
||||
var seals := _screen_seals()
|
||||
if not main.save_select_overlay.visible or not seals_check_single(seals):
|
||||
_problems.append("[savesel] sl10 开层后屏级互斥失守(选档=%s)" % str(main.save_select_overlay.visible))
|
||||
var back_btn: Button = ProbeUiUtil.find_button(main.save_select_overlay, "返回", "prefix")
|
||||
if not main.ui.modals.save_select_overlay.visible or not seals_check_single(seals):
|
||||
_problems.append("[savesel] sl10 开层后屏级互斥失守(选档=%s)" % str(main.ui.modals.save_select_overlay.visible))
|
||||
var back_btn: Button = ProbeUiUtil.find_button(main.ui.modals.save_select_overlay, "返回", "prefix")
|
||||
if back_btn == null:
|
||||
_problems.append("[savesel] sl10 返回钮缺失(页壳顶条)")
|
||||
else:
|
||||
back_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.save_select_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[savesel] sl10 返回后选档未隐藏或开始层未恢复(关闭链失守)")
|
||||
# ESC 语境:再开选档→合成 ESC 键→层不关、流程零扰动(ESC 不属选档语境——零实现断言)
|
||||
main._open_save_select()
|
||||
@@ -159,7 +159,7 @@ func _step_save_select_open_back() -> void:
|
||||
ev.pressed = true
|
||||
main.get_viewport().push_input(ev, true)
|
||||
await get_tree().process_frame
|
||||
if not main.save_select_overlay.visible:
|
||||
if not main.ui.modals.save_select_overlay.visible:
|
||||
_problems.append("[savesel] sl10 ESC 关闭了选档层(选档语境 ESC 应不响应——卡⑤ 范围外)")
|
||||
main._on_save_select_closed()
|
||||
await get_tree().process_frame
|
||||
@@ -171,7 +171,7 @@ func seals_check_single(f: Dictionary) -> bool:
|
||||
for k in f:
|
||||
if bool(f[k]):
|
||||
on += 1
|
||||
if main.save_select_overlay.visible:
|
||||
if main.ui.modals.save_select_overlay.visible:
|
||||
on += 1 # 选档自身计入(调用点已开层)
|
||||
return on == 1
|
||||
|
||||
@@ -184,25 +184,25 @@ func _step_menu_to_adventure() -> void:
|
||||
_expect_adventure_seal("④菜单→冒险", true)
|
||||
if main.run_scope != null and main.run_scope.adventure == null:
|
||||
_problems.append("④菜单→冒险:冒险实例未建立(入口停点未达)")
|
||||
if main.level_screen != null and not main.level_screen.visible:
|
||||
if main.ui.modals.level_screen != null and not main.ui.modals.level_screen.visible:
|
||||
_problems.append("④菜单→冒险:入口停点未落在关卡地图屏(level_screen 不可见)")
|
||||
|
||||
|
||||
# ---- ⑤ 冒险内屏切换:关卡地图屏 ⇄ 战斗屏(屏内两态,屏级仍独占)----
|
||||
func _step_level_to_battle_and_back() -> void:
|
||||
if not main.level_screen.visible:
|
||||
if not main.ui.modals.level_screen.visible:
|
||||
_problems.append("⑤冒险内切换:前置不在关卡地图屏")
|
||||
return
|
||||
main._exit_level_screen()
|
||||
await _frames()
|
||||
if main.level_screen.visible:
|
||||
if main.ui.modals.level_screen.visible:
|
||||
_problems.append("⑤冒险内切换:退出关卡屏后 level_screen 仍可见")
|
||||
if main._battle_root != null and not main._battle_root.visible:
|
||||
if main.ui.screens.battle_root != null and not main.ui.screens.battle_root.visible:
|
||||
_problems.append("⑤冒险内切换:战斗屏未接手(battle_root 不可见)")
|
||||
_expect_exclusive("⑤冒险内切换(关卡→战斗)", "冒险")
|
||||
main._enter_level_screen()
|
||||
await _frames()
|
||||
if not main.level_screen.visible:
|
||||
if not main.ui.modals.level_screen.visible:
|
||||
_problems.append("⑤冒险内切换:回到关卡地图屏失败")
|
||||
_expect_exclusive("⑤冒险内切换(战斗→关卡)", "冒险")
|
||||
|
||||
@@ -215,16 +215,16 @@ func _step_abandon_to_end() -> void:
|
||||
main._on_abandon_requested()
|
||||
await _frames(4)
|
||||
_expect_exclusive("⑥放弃→终局", "终局")
|
||||
if main._end_overlay != null and not main._end_overlay.visible:
|
||||
if main.ui.modals.end_overlay != null and not main.ui.modals.end_overlay.visible:
|
||||
_problems.append("⑥放弃→终局:终局覆盖层未开")
|
||||
|
||||
|
||||
# ---- ⑦ 终局 → 菜单(终局出口返回开始界面)----
|
||||
func _step_end_back_to_menu() -> void:
|
||||
if main._end_overlay == null or not main._end_overlay.visible:
|
||||
if main.ui.modals.end_overlay == null or not main.ui.modals.end_overlay.visible:
|
||||
_problems.append("⑦终局→菜单:前置不在终局")
|
||||
return
|
||||
main._end_overlay.continue_pressed.emit()
|
||||
main.ui.modals.end_overlay.continue_pressed.emit()
|
||||
await _frames(3)
|
||||
_expect_exclusive("⑦终局→菜单", "菜单")
|
||||
if main.run_scope != null and main.run_scope.is_adventure:
|
||||
|
||||
+29
-29
@@ -51,10 +51,10 @@ func _ready() -> void:
|
||||
var top: Node = null
|
||||
var top_rx := -1
|
||||
var top_ry := -1
|
||||
for c: Node in [main._start_overlay, main._battle_root, main._inter_overlay,
|
||||
main.legend_overlay, main.node_overlay, main.level_screen, main.curio_overlay,
|
||||
main._end_overlay, main.shop_overlay, main.camp_overlay, main.settings_overlay,
|
||||
main.status_panel, main.career_overlay, main.confirm_dialog, main.debug_overlay]:
|
||||
for c: Node in [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_overlay, 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]:
|
||||
if c == null or not (c is CanvasItem) or not (c as CanvasItem).visible:
|
||||
continue
|
||||
var rx: int = c.get_parent().get_index()
|
||||
@@ -63,14 +63,14 @@ func _ready() -> void:
|
||||
top = c
|
||||
top_rx = rx
|
||||
top_ry = ry
|
||||
if top != main._start_overlay:
|
||||
if top != main.ui.screens.start_overlay:
|
||||
_problems.append("boot 态顶层可见层≠start_overlay(实测 %s——菜单被埋)〔装配生命批 V4〕"
|
||||
% (top.name if top != null else "null"))
|
||||
# 自检二:菜单标题 label 必须 is_visible_in_tree 且 rect 落屏内
|
||||
#(对比度只判反差不判「字在」——本条+checker 墨色占比共同补内容判据)
|
||||
var vp := get_viewport().get_visible_rect()
|
||||
for pair in [["menu_title", "Dungeon Sweeper"]]:
|
||||
var lbl: Label = ProbeUiUtil.find_label(main._start_overlay, String(pair[1]))
|
||||
var lbl: Label = ProbeUiUtil.find_label(main.ui.screens.start_overlay, String(pair[1]))
|
||||
if lbl == null:
|
||||
_problems.append("菜单文字缺失: %s(find_label 零命中)〔装配生命批 V4〕" % String(pair[1]))
|
||||
continue
|
||||
@@ -79,22 +79,22 @@ func _ready() -> void:
|
||||
var gr: Rect2 = lbl.get_global_rect()
|
||||
if not vp.encloses(gr):
|
||||
_problems.append("菜单文字 rect 越出视口: %s(%s ⊄ %s)〔装配生命批 V4〕" % [String(pair[1]), str(gr), str(vp)])
|
||||
_meta[String(pair[0])] = ProbeUiUtil.label_rect(main._start_overlay, String(pair[1]))
|
||||
_meta[String(pair[0])] = ProbeUiUtil.label_rect(main.ui.screens.start_overlay, String(pair[1]))
|
||||
await _snap("menu")
|
||||
# ①b 设置面(settingsbtn 真实信号路径,开合同款真实钮)
|
||||
var setbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "设置", "prefix")
|
||||
var setbtn: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "设置", "prefix")
|
||||
if setbtn == null:
|
||||
_problems.append("设置入口缺失(开始层 settingsbtn)")
|
||||
else:
|
||||
setbtn.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if not main.settings_overlay.visible:
|
||||
if not main.ui.modals.settings_overlay.visible:
|
||||
_problems.append("设置面未打开(settingsbtn 路径)")
|
||||
else:
|
||||
_meta["settings_title"] = ProbeUiUtil.label_rect(main.settings_overlay, "设置")
|
||||
_meta["settings_title"] = ProbeUiUtil.label_rect(main.ui.modals.settings_overlay, "设置")
|
||||
await _snap("settings")
|
||||
var set_close: Button = ProbeUiUtil.find_button(main.settings_overlay, "返回", "prefix")
|
||||
var set_close: Button = ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "返回", "prefix")
|
||||
if set_close == null:
|
||||
_problems.append("设置面返回钮缺失")
|
||||
else:
|
||||
@@ -105,19 +105,19 @@ func _ready() -> void:
|
||||
main._open_career()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if not main.career_overlay.visible or main._start_overlay.visible:
|
||||
if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
_problems.append("生涯页未打开或开始层未隐藏(函数直调中间态路径)")
|
||||
else:
|
||||
_meta["career_title"] = ProbeUiUtil.label_rect(main.career_overlay, "生 涯")
|
||||
_meta["career_title"] = ProbeUiUtil.label_rect(main.ui.modals.career_overlay, "生 涯")
|
||||
await _snap("career")
|
||||
var career_close: Button = ProbeUiUtil.find_button(main.career_overlay, "返回", "prefix")
|
||||
var career_close: Button = ProbeUiUtil.find_button(main.ui.modals.career_overlay, "返回", "prefix")
|
||||
if career_close == null:
|
||||
_problems.append("生涯页返回钮缺失")
|
||||
else:
|
||||
career_close.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if main.career_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("生涯页关闭后页未隐藏或开始层未恢复")
|
||||
# ② 营地(进档开层新路径——卡② mn9;槽模板注入隔离命名空间)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"):
|
||||
@@ -125,7 +125,7 @@ func _ready() -> void:
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
await _snap("camp")
|
||||
var close_btn: Button = ProbeUiUtil.find_button(main.camp_overlay, "关闭营地", "contains")
|
||||
var close_btn: Button = ProbeUiUtil.find_button(main.ui.modals.camp_overlay, "关闭营地", "contains")
|
||||
if close_btn != null:
|
||||
close_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
@@ -137,24 +137,24 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
await _snap("level")
|
||||
# GP 常驻徽章断言(读数=runner.gp 现值,刷新=写路径回执后重查〔open_choices〕)
|
||||
var gp_label: Label = ProbeUiUtil.find_label(main.level_screen, "◈ ")
|
||||
var gp_label: Label = ProbeUiUtil.find_label(main.ui.modals.level_screen, "◈ ")
|
||||
if gp_label == null or not gp_label.is_visible_in_tree():
|
||||
_problems.append("GP 徽章不可见(关卡画面顶栏)")
|
||||
elif main.level_screen.get("_runner") != null:
|
||||
var expect := "◈ %d" % int(main.level_screen.get("_runner").gp)
|
||||
elif main.ui.modals.level_screen.get("_runner") != null:
|
||||
var expect := "◈ %d" % int(main.ui.modals.level_screen.get("_runner").gp)
|
||||
if gp_label.text != expect:
|
||||
_problems.append("GP 徽章读数不符: %s != %s" % [gp_label.text, expect])
|
||||
# ④ 战斗(节点一——真实选路提交路径 press_candidate,ui_click_probe :199 同款)
|
||||
if main.level_screen != null and main.level_screen.visible:
|
||||
main.level_screen.press_candidate(0)
|
||||
if main.ui.modals.level_screen != null and main.ui.modals.level_screen.visible:
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
for i in 10:
|
||||
await get_tree().process_frame
|
||||
await _snap("battle")
|
||||
# 战斗屏文字框/明度带锚点(全局 rect——checker 取样坐标源;明细带锚 S1 裁后改认现行文案)
|
||||
_meta["battle_log_hint"] = ProbeUiUtil.label_rect(main.stage, "上轮摘要")
|
||||
_meta["battle_log_hint"] = ProbeUiUtil.label_rect(main.ui.battle.stage, "上轮摘要")
|
||||
_meta["hero_panel"] = _node_rect(ProbeUiUtil.find_by_script(main, "hero_info_panel_view"))
|
||||
_meta["stage_view"] = _node_rect(main.stage)
|
||||
_meta["board_zone"] = _node_rect(main.board_zone)
|
||||
_meta["stage_view"] = _node_rect(main.ui.battle.stage)
|
||||
_meta["board_zone"] = _node_rect(main.ui.battle.board_zone)
|
||||
# draw calls 真机测量(基线 270→目标 <200;窗口化渲染帧读数,
|
||||
# 取值时机=战斗截图同帧后一帧——呈现终态满负荷)
|
||||
await get_tree().process_frame
|
||||
@@ -162,25 +162,25 @@ func _ready() -> void:
|
||||
_meta["battle_draw_calls"] = dc
|
||||
print("[shot_probe] battle draw_calls=%d(基线 270/目标 <200)" % dc)
|
||||
# ⑤ 状态面板(战斗屏状态角标真实信号路径;层在战斗屏之上开合)
|
||||
var pb: Button = main.board_zone.status_badge
|
||||
var pb: Button = main.ui.battle.board_zone.status_badge
|
||||
if pb == null or not pb.visible or pb.disabled:
|
||||
_problems.append("状态面板入口角标缺失/置灰(战斗屏 board_zone)")
|
||||
else:
|
||||
pb.pressed.emit()
|
||||
for i in 3:
|
||||
await get_tree().process_frame
|
||||
if not main.status_panel.visible:
|
||||
if not main.ui.modals.status_panel.visible:
|
||||
_problems.append("状态面板未打开(角标路径)")
|
||||
else:
|
||||
_meta["status_title"] = ProbeUiUtil.label_rect(main.status_panel, "状态")
|
||||
_meta["status_title"] = ProbeUiUtil.label_rect(main.ui.modals.status_panel, "状态")
|
||||
await _snap("status")
|
||||
var status_close: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix")
|
||||
var status_close: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix")
|
||||
if status_close == null:
|
||||
_problems.append("状态面板继续钮缺失")
|
||||
else:
|
||||
status_close.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.status_panel.visible:
|
||||
if main.ui.modals.status_panel.visible:
|
||||
_problems.append("状态面板关闭后未隐藏(继续钮路径)")
|
||||
else:
|
||||
_problems.append("关卡画面未出现(函数直调中间态路径)")
|
||||
|
||||
@@ -29,14 +29,14 @@ func _ready() -> void:
|
||||
main._start_adventure_from_menu() # 函数直调中间态(卡② mn10——开始冒险钮已撤)
|
||||
for i in 60:
|
||||
await get_tree().process_frame
|
||||
if main.level_screen != null and main.level_screen.visible:
|
||||
if main.ui.modals.level_screen != null and main.ui.modals.level_screen.visible:
|
||||
break
|
||||
if main.level_screen == null or not main.level_screen.visible:
|
||||
if main.ui.modals.level_screen == null or not main.ui.modals.level_screen.visible:
|
||||
_fail("关卡画面未出现")
|
||||
return
|
||||
for i in 5:
|
||||
await get_tree().process_frame
|
||||
main.level_screen.press_candidate(0)
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
for i in 20:
|
||||
await get_tree().process_frame
|
||||
if main.run_scope.settlement == null:
|
||||
@@ -67,18 +67,18 @@ func _ready() -> void:
|
||||
_fail("战斗未胜利(result='%s')" % str(main.run_scope.settlement.battle_result))
|
||||
return
|
||||
var t_settle := Time.get_ticks_msec()
|
||||
var _rows_at_victory: int = main._row_pacer.pending_rows().size() # 时间戳①:结算入队完成帧(AP 耗尽/停手→五行段同步跑完→行全在队)
|
||||
var _rows_at_victory: int = main.ui.screens.row_pacer.pending_rows().size() # 时间戳①:结算入队完成帧(AP 耗尽/停手→五行段同步跑完→行全在队)
|
||||
# 断言①胜利同帧:终局面未开 + 挂起在案 + 队列非空(判别性——旧行为同帧开窗即红)
|
||||
if main._end_overlay.is_open():
|
||||
if main.ui.modals.end_overlay.is_open():
|
||||
_problems.append("胜利同帧终局面已开(演播后排窗失效——同帧 flush 旧路径回潮)")
|
||||
if main._deferred_modal == null:
|
||||
if main.ui.modals.flow._deferred == null:
|
||||
_problems.append("胜利同帧挂起态缺失(_deferred_modal == null)")
|
||||
if main._row_pacer.pending_rows().is_empty():
|
||||
if main.ui.screens.row_pacer.pending_rows().is_empty():
|
||||
_problems.append("胜利同帧排播队列已空(结算行未走排播链)")
|
||||
var _played_rows: int = _rows_at_victory
|
||||
# 断言②排播放完:终局面开启(20s 守卫——0.22s×行数 ×10 余量)
|
||||
var waited := 0.0
|
||||
while not main._end_overlay.is_open() and waited < 20.0:
|
||||
while not main.ui.modals.end_overlay.is_open() and waited < 20.0:
|
||||
await get_tree().process_frame
|
||||
waited += get_process_delta_time()
|
||||
var t_popup := Time.get_ticks_msec()
|
||||
@@ -86,7 +86,7 @@ func _ready() -> void:
|
||||
print("[t17_defer] AP点用完: %s" % ap_clock)
|
||||
print("[t17_defer] 触发结算: %s(与 AP 用完同帧——核心同步,相差 0ms)" % settle_clock)
|
||||
print("[t17_defer] 弹窗出现: %s(距触发结算 %d ms = 逐行演播时长,行数 %d)" % [popup_clock, t_popup - t_ap_out, _played_rows])
|
||||
if not main._end_overlay.is_open():
|
||||
if not main.ui.modals.end_overlay.is_open():
|
||||
_problems.append("排播放完后终局面未开(%.1fs——演播完边沿派发失守)" % waited)
|
||||
if _problems.is_empty():
|
||||
print("[t17_defer] PASS — 胜利同帧挂起+排播完开窗 两断言全过(waited=%.1fs)" % waited)
|
||||
|
||||
+171
-171
@@ -51,7 +51,7 @@ func _ready() -> void:
|
||||
var t_ui_ready := Time.get_ticks_msec() - t0
|
||||
# 调试浮窗 boot 不开(菜单优先——入口=UI 最顶层全局浮层角标,
|
||||
# 任何 UI 不遮挡;浮层角标遮挡判据见设置面段)
|
||||
if main.debug_overlay.visible:
|
||||
if main.ui.modals.debug_overlay.visible:
|
||||
_problems.append("调试浮窗 boot 自动开(菜单优先失守)")
|
||||
# ---- 营地购买合成点击路径(选档进档开层→购买→关闭全走真实按钮信号路径;
|
||||
# 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json);陈旧档段首清理)----
|
||||
@@ -65,17 +65,17 @@ func _ready() -> void:
|
||||
_problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
if not main.camp_overlay.visible:
|
||||
if not main.ui.modals.camp_overlay.visible:
|
||||
_problems.append("营地层未打开(进档开层新路径)")
|
||||
elif main.camp_overlay._tree_buy_btns.is_empty():
|
||||
elif main.ui.modals.camp_overlay._tree_buy_btns.is_empty():
|
||||
_problems.append("树淬炼柜台购买钮缺失(点击路径——四柜台重排后树=主界面)")
|
||||
else:
|
||||
# 树钮句柄用后即取(refresh_camp 随刷重建树行板——旧钮 queue_free,跨刷新句柄必失效)
|
||||
iso_c.meta_state._add_keystone(9999)
|
||||
main.camp_overlay.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var buy_btn: Button = null
|
||||
for card in main.camp_overlay._tree_buy_btns:
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
if String(card.node_id) == "sp_hp":
|
||||
buy_btn = card.btn
|
||||
if buy_btn == null:
|
||||
@@ -93,16 +93,16 @@ func _ready() -> void:
|
||||
_problems.append("营地树购买未持久(reload 后级 != 1)")
|
||||
# Fork 双态切换点击路径(V20——购=解锁默认 A,钮切 B;前置=同调位 1,先点亮)
|
||||
var socket_btn: Button = null
|
||||
for card in main.camp_overlay._tree_buy_btns:
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
if String(card.node_id) == "att_socket1":
|
||||
socket_btn = card.btn
|
||||
if socket_btn != null:
|
||||
socket_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
main.camp_overlay.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var fork_btn: Button = null
|
||||
for card in main.camp_overlay._tree_buy_btns:
|
||||
for card in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
if String(card.node_id) == "fork_posture":
|
||||
fork_btn = card.btn
|
||||
if fork_btn == null:
|
||||
@@ -110,10 +110,10 @@ func _ready() -> void:
|
||||
else:
|
||||
fork_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
main.camp_overlay.refresh_camp(main._camp_data())
|
||||
main.ui.modals.camp_overlay.refresh_camp(main._camp_data())
|
||||
await get_tree().process_frame
|
||||
var side_b: Button = null
|
||||
for fk in main.camp_overlay._tree_fork_btns:
|
||||
for fk in main.ui.modals.camp_overlay._tree_fork_btns:
|
||||
if String(fk.group) == "fg_posture" and String(fk.side) == "b":
|
||||
side_b = fk.btn
|
||||
if side_b == null:
|
||||
@@ -128,7 +128,7 @@ func _ready() -> void:
|
||||
# ---- 营地六处灰字取色断言(迭代36 wc4——真实入口开营地后按文本锚取 label,
|
||||
# font_color 逐处 == STONE_DEEP;六源位 :167 图鉴锁文/:417+:476 行状态字/:509 同调形态/:513 档位标签/:525 刷新行)----
|
||||
var camp_labels: Array = []
|
||||
_collect_labels_by_text(main.camp_overlay, camp_labels)
|
||||
_collect_labels_by_text(main.ui.modals.camp_overlay, camp_labels)
|
||||
var atlas_locks := _labels_with_text(camp_labels, "柜台结构位预留")
|
||||
if atlas_locks.is_empty():
|
||||
_problems.append("营地灰字:图鉴柜台锁文 label 未找到(wc4 定位锚失守)")
|
||||
@@ -137,9 +137,9 @@ func _ready() -> void:
|
||||
_problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)")
|
||||
# 「未解锁」锚收窄到秘宝匣行容器子树(树行/锻造行也有「未解锁」态字样——非 wc4 目标位)
|
||||
var locked_status: Array = []
|
||||
if main.camp_overlay.get("_workshop_holder") != null:
|
||||
if main.ui.modals.camp_overlay.get("_workshop_holder") != null:
|
||||
var ws_labels: Array = []
|
||||
_collect_labels_by_text(main.camp_overlay._workshop_holder, ws_labels)
|
||||
_collect_labels_by_text(main.ui.modals.camp_overlay._workshop_holder, ws_labels)
|
||||
locked_status = _labels_with_text(ws_labels, "未解锁")
|
||||
if locked_status.is_empty():
|
||||
_problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)")
|
||||
@@ -177,7 +177,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
await get_tree().process_frame
|
||||
if main.camp_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.camp_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("营地关闭后未恢复开始层")
|
||||
# ---- 选档界面行为走查(迭代 33 卡③):sl5 空槽新档链/sl6 有档槽进档链/sl7 删档二次确认双路径
|
||||
# /sl8 消费方分派单口/sl12 动线序不回闪。开层=函数直调(「进入存档」钮归卡② mn3——中间态在案);
|
||||
@@ -196,11 +196,11 @@ func _ready() -> void:
|
||||
f_c2.close() # 槽2 有档异值(进档链夹具);槽1/3 空
|
||||
main._open_save_select()
|
||||
await get_tree().process_frame
|
||||
if not main.save_select_overlay.visible:
|
||||
if not main.ui.modals.save_select_overlay.visible:
|
||||
_problems.append("[savesel] 选档层未打开(click 段前置)")
|
||||
# sl12 动线序(新档链同拍采样):逐帧可见性次序——首帧选档隐藏时营地层必须已可见、开始层保持隐藏
|
||||
#(禁先关后开——_close_modal 会恢复开始层=回闪一帧)
|
||||
var new_btn: Button = main.save_select_overlay._new_btns.get(1)
|
||||
var new_btn: Button = main.ui.modals.save_select_overlay._new_btns.get(1)
|
||||
if new_btn == null:
|
||||
_problems.append("[savesel] 槽1 新档钮缺失(sl5/sl12 驱动中断)")
|
||||
else:
|
||||
@@ -208,7 +208,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.save_select_overlay.visible, main.camp_overlay.visible, main._start_overlay.visible])
|
||||
samples.append([main.ui.modals.save_select_overlay.visible, main.ui.modals.camp_overlay.visible, main.ui.screens.start_overlay.visible])
|
||||
if samples[0][0]:
|
||||
_problems.append("[savesel] sl12 新档后选档层仍可见(落点开层未发生)")
|
||||
elif not samples[0][1]:
|
||||
@@ -217,8 +217,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._modals._occluded.get(main.camp_overlay, [])
|
||||
if not occluded.has(main.save_select_overlay):
|
||||
var occluded: Array = main.ui.modals.flow.stack._occluded.get(main.ui.modals.camp_overlay, [])
|
||||
if not occluded.has(main.ui.modals.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 落盘应全零)")
|
||||
@@ -227,7 +227,7 @@ func _ready() -> void:
|
||||
# sl6 进档链:槽2 进档 → 激活=2(camp 数据随激活槽)
|
||||
main._open_save_select()
|
||||
await get_tree().process_frame
|
||||
var load_btn: Button = main.save_select_overlay._load_btns.get(2)
|
||||
var load_btn: Button = main.ui.modals.save_select_overlay._load_btns.get(2)
|
||||
if load_btn == null:
|
||||
_problems.append("[savesel] 槽2 进档钮缺失(sl6 驱动中断——夹具应有档)")
|
||||
else:
|
||||
@@ -235,51 +235,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.camp_overlay.visible or main.save_select_overlay.visible:
|
||||
if not main.ui.modals.camp_overlay.visible or main.ui.modals.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.save_select_overlay._delete_btns.get(2)
|
||||
var del_btn: Button = main.ui.modals.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.confirm_dialog.is_open():
|
||||
if not main.ui.modals.confirm_dialog.is_open():
|
||||
_problems.append("[savesel] sl7 一击未只开确认窗(直删形态)")
|
||||
elif not FileAccess.file_exists(csel_tpl % 2):
|
||||
_problems.append("[savesel] sl7 确认前文件已被删(一次点击即删违契)")
|
||||
else:
|
||||
if not main._modal_flow.stack._occluded.has(main.confirm_dialog):
|
||||
if not main.ui.modals.flow.stack._occluded.has(main.ui.modals.confirm_dialog):
|
||||
_problems.append("[savesel] sl7 确认窗未经模态栈开(直开旁路——wr2 单口失守)")
|
||||
elif not main._input_locked():
|
||||
elif not main.ui.modals.flow.locks_input():
|
||||
_problems.append("[savesel] sl7 确认窗开时输入锁未生效(锁域失守——wr2)")
|
||||
main.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源)
|
||||
main.ui.modals.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源)
|
||||
await get_tree().process_frame
|
||||
if main.confirm_dialog.is_open() or not FileAccess.file_exists(csel_tpl % 2):
|
||||
if main.ui.modals.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._modal_flow.stack._occluded.has(main.confirm_dialog):
|
||||
if main.ui.modals.flow.stack._occluded.has(main.ui.modals.confirm_dialog):
|
||||
_problems.append("[savesel] sl7 取消后确认窗遮挡账未清(关侧未走模态栈——wr2)")
|
||||
del_btn.pressed.emit() # 再开窗走确认路径
|
||||
await get_tree().process_frame
|
||||
main.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源)
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源)
|
||||
await get_tree().process_frame
|
||||
if FileAccess.file_exists(csel_tpl % 2) or main.confirm_dialog.is_open():
|
||||
if FileAccess.file_exists(csel_tpl % 2) or main.ui.modals.confirm_dialog.is_open():
|
||||
_problems.append("[savesel] sl7 确认路径失守(应删档+关窗)")
|
||||
elif main.run_scope.is_adventure or main._start_overlay.visible:
|
||||
elif main.run_scope.is_adventure or main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[savesel] sl8 删档确认误触发开局(双常驻接线形态——分派单口失守)")
|
||||
elif bool(main.save_select_overlay._occupied_flags.get(2, true)):
|
||||
elif bool(main.ui.modals.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.save_select_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[savesel] 关闭后层未隐藏或开始层未恢复(模态栈单口失守)")
|
||||
|
||||
# ---- 设置面段(隔离 store 换注禁碰真实 user://settings.cfg;真实按钮信号路径)----
|
||||
@@ -289,19 +289,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.settings_overlay.retarget(iso_s)
|
||||
var settings_btn: Button = ProbeUiUtil.find_button(main._start_overlay, "设置", "prefix")
|
||||
main.ui.modals.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.settings_overlay.visible or main._start_overlay.visible:
|
||||
if not main.ui.modals.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.settings_overlay._motion_btn
|
||||
var motion_btn: Button = main.ui.modals.settings_overlay._motion_btn
|
||||
if motion_btn == null:
|
||||
_problems.append("设置面减动效开关缺失")
|
||||
else:
|
||||
@@ -318,7 +318,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.settings_overlay._volume
|
||||
var vol: HSlider = main.ui.modals.settings_overlay._volume
|
||||
vol.value = 30
|
||||
await get_tree().process_frame
|
||||
var master_idx := AudioServer.get_bus_index("Master")
|
||||
@@ -329,24 +329,24 @@ func _ready() -> void:
|
||||
_problems.append("音量未入 store(%f ≠ 0.3)" % iso_s.volume_linear)
|
||||
# 调试入口断言(卡⑦ c4 发布态换代——角标恒隐;原「最顶层浮层不被遮挡」
|
||||
# 回归语义随调试浮层发布态退役,申报见迭代 38 实现报告 §8.2)
|
||||
if main.debug_badge == null or main.debug_badge.visible:
|
||||
if main.ui.screens.debug_badge == null or main.ui.screens.debug_badge.visible:
|
||||
_problems.append("设置面开中调试角标可见(发布态恒隐失守)")
|
||||
var settings_close: Button = ProbeUiUtil.find_button(main.settings_overlay, "返回", "prefix")
|
||||
var settings_close: Button = ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "返回", "prefix")
|
||||
if settings_close == null:
|
||||
_problems.append("设置面返回按钮缺失")
|
||||
else:
|
||||
settings_close.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.settings_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.settings_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("设置面关闭后未恢复开始层")
|
||||
# 旧入口零残留断言(卡④ AC③——测试调整板不含减动效开关;14.5 起载体=调试浮窗)
|
||||
var legacy_motion = ProbeUiUtil.find_check_button(main.debug_overlay, "减动效")
|
||||
var legacy_motion = ProbeUiUtil.find_check_button(main.ui.modals.debug_overlay, "减动效")
|
||||
if legacy_motion != null:
|
||||
_problems.append("调整板减动效旧入口未退役(设置面唯一入口失守——卡④ AC③)")
|
||||
# D1 断言(二轮):退出游戏只在一级菜单——开始菜单有、设置面无
|
||||
if ProbeUiUtil.find_button(main._start_overlay, "退出游戏", "prefix") == null:
|
||||
if ProbeUiUtil.find_button(main.ui.screens.start_overlay, "退出游戏", "prefix") == null:
|
||||
_problems.append("开始菜单缺退出游戏按钮(二轮 D1)")
|
||||
if ProbeUiUtil.find_button(main.settings_overlay, "退出游戏", "prefix") != null:
|
||||
if ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "退出游戏", "prefix") != null:
|
||||
_problems.append("设置面仍含退出游戏按钮(二轮 D1——只在一级菜单出现)")
|
||||
# ---- 生涯页段(入口点击链+关闭回返,真实按钮信号路径;本页只读零写,
|
||||
# 值断言归冒烟投影段与 ui_struct 结构族,此处只验开合路径与零跳转)----
|
||||
@@ -354,45 +354,45 @@ func _ready() -> void:
|
||||
if true:
|
||||
main._open_career()
|
||||
await get_tree().process_frame
|
||||
if not main.career_overlay.visible or main._start_overlay.visible:
|
||||
if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
_problems.append("生涯页未打开(点击路径)")
|
||||
else:
|
||||
# 零跳转契约(提纲 P4 甲):页内除「返回」外零导航钮
|
||||
for nav in ["营地", "商店", "设置", "退出", "冒险"]:
|
||||
if ProbeUiUtil.find_button(main.career_overlay, nav, "contains") != null:
|
||||
if ProbeUiUtil.find_button(main.ui.modals.career_overlay, nav, "contains") != null:
|
||||
_problems.append("生涯页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav)
|
||||
# 局内入口断言(提纲 P4 甲):生涯入口只在开始层——战斗屏/地图屏零生涯角标
|
||||
if main.board_zone.find_child("career_badge", true, false) != null \
|
||||
or main.level_screen.find_child("career_badge", true, false) != null:
|
||||
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:
|
||||
_problems.append("局内屏出现生涯入口件(局内不放生涯入口违约)")
|
||||
var career_close: Button = ProbeUiUtil.find_button(main.career_overlay, "返回", "prefix")
|
||||
var career_close: Button = ProbeUiUtil.find_button(main.ui.modals.career_overlay, "返回", "prefix")
|
||||
if career_close == null:
|
||||
_problems.append("生涯页返回按钮缺失")
|
||||
else:
|
||||
career_close.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.career_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.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.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支)
|
||||
main.ui.modals.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支)
|
||||
var t1 := Time.get_ticks_msec()
|
||||
main._start_adventure_from_menu()
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
# 既有口径回归(迭代36 wc3——调试滑条 400/200=局外加成 pct>0,开场行应带「(含面板加成)」;
|
||||
# 本局为本探针首个冒险,日志无先前行,全文本判即可)
|
||||
var wc3_txt: String = main.log_view._log.get_parsed_text()
|
||||
var wc3_txt: String = main.ui.battle.log_view._log.get_parsed_text()
|
||||
if not wc3_txt.contains("种子 42") or not wc3_txt.contains("(含面板加成)"):
|
||||
_problems.append("开场行回归失守:局外加成(pct>0)局的开场行应带「(含面板加成)」(wc3)")
|
||||
# 关卡开始 = 入口选路停点——press_candidate 图上命中路径选入口候选进首战
|
||||
#(首格可点 AC 口径随之含入口选路一步:菜单点击 → 停点呈现 → pick → 棋盘安装)
|
||||
if main.level_screen.visible:
|
||||
if main.ui.modals.level_screen.visible:
|
||||
# 独立性断言:独立屏激活期战斗屏宿主整体隐藏
|
||||
if main._battle_root.visible:
|
||||
if main.ui.screens.battle_root.visible:
|
||||
_problems.append("独立屏激活期战斗屏宿主未隐藏(零透出契约失守)")
|
||||
main.level_screen.press_candidate(0)
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var t_entry := Time.get_ticks_msec() - t1
|
||||
@@ -402,7 +402,7 @@ func _ready() -> void:
|
||||
var first_cell_ms := t_entry
|
||||
if first_cell_ms > 500:
|
||||
_problems.append("进入战斗屏首格可点超时 %dms > 500ms" % first_cell_ms)
|
||||
if main.board_view.cells_probe().is_empty():
|
||||
if main.ui.battle.board_zone.board_view.cells_probe().is_empty():
|
||||
_problems.append("首格未可点(cells=0)")
|
||||
print("[click_probe] 首格可点计时 %dms(进入战斗屏口径,≤500ms AC)· 场景冷启动 %dms(含 UI 构建 %dms,观测项)" % [
|
||||
first_cell_ms, t_total, t_ui_ready])
|
||||
@@ -413,68 +413,68 @@ func _ready() -> void:
|
||||
while not _done and action_budget > 0:
|
||||
action_budget -= 1
|
||||
await get_tree().process_frame
|
||||
if main.node_overlay.visible:
|
||||
main.node_overlay.press_continue()
|
||||
if main.ui.modals.node_overlay.visible:
|
||||
main.ui.modals.node_overlay.press_continue()
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main.curio_overlay.visible:
|
||||
if main.ui.modals.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.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.curio_overlay._header).contains("关卡奖励点"):
|
||||
# 停点态判据 = _node_curio_pending(奖励节点三选一停点住此——原条件查
|
||||
# _curio_pending〔关卡完成三选一态〕与奖励头部互斥、恒不可命中=拦截能力未证病根,
|
||||
# 卡⑤ d4 转正随修;头部校验串本体零触碰)
|
||||
_tested_curio_head = true
|
||||
var ch := String(main.curio_overlay._header)
|
||||
var ch := String(main.ui.modals.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("
|
||||
", "|"))
|
||||
# 奖励节点战报文案换代断言(卡⑤ wr2——reward_line 同屏打印于停点拉起前,
|
||||
# 同一驱动路径承载;旧「内容待定」残留或新话缺失即红)
|
||||
var logtext: String = main.log_view.parsed_text()
|
||||
var logtext: String = main.ui.battle.log_view.parsed_text()
|
||||
if not logtext.contains("关卡奖励点(三选一——选毕推进)") or logtext.contains("内容待定"):
|
||||
_problems.append("奖励节点战报文案未换代(卡⑤ wr2——旧话残留或新话缺失)")
|
||||
if not main._input_locked():
|
||||
if not main.ui.modals.flow.locks_input():
|
||||
_problems.append("三选一停点打开时输入未锁定")
|
||||
if main.curio_overlay._continue_btn.visible:
|
||||
main.curio_overlay.press_continue()
|
||||
if main.ui.modals.curio_overlay._continue_btn.visible:
|
||||
main.ui.modals.curio_overlay.press_continue()
|
||||
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty():
|
||||
main.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形)
|
||||
main.curio_overlay.press_confirm()
|
||||
main.ui.modals.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形)
|
||||
main.ui.modals.curio_overlay.press_confirm()
|
||||
elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty():
|
||||
main.curio_overlay.press_card(0)
|
||||
main.curio_overlay.press_confirm()
|
||||
main.ui.modals.curio_overlay.press_card(0)
|
||||
main.ui.modals.curio_overlay.press_confirm()
|
||||
else:
|
||||
main.curio_overlay.press_continue() # 停点已消费的残面——继续关屏
|
||||
main.ui.modals.curio_overlay.press_continue() # 停点已消费的残面——继续关屏
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main.shop_overlay.visible:
|
||||
if main.ui.modals.shop_overlay.visible:
|
||||
# 商店房覆盖层(摆放约束:每终点前一位恰一店,真实路径恒必经一店):零购离开(真实 leave 信号路径)放行选路。
|
||||
# 分派链序:商店层=选路页上的模态覆盖(停点呈现为
|
||||
# level_screen+shop 双 visible)——必须先于 level_screen 分派,否则下层候选点击
|
||||
# 空转烧尽预算(实证:lv2 商店停点双 visible 循环 12000 帧 done=false)。
|
||||
main.shop_overlay.leave_pressed.emit()
|
||||
main.ui.modals.shop_overlay.leave_pressed.emit()
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main.level_screen.visible:
|
||||
if main.ui.modals.level_screen.visible:
|
||||
# 节点图选路口(零自动推进停点)——图上命中路径交替选候选(岔路使用行为;布局决策 2 改造)
|
||||
# 地图屏角标一次性 case:选路前经角标开/关状态面板(层在地图屏之上开合,
|
||||
# 关闭后地图屏选路态零扰动——P2 甲层级语义)
|
||||
if not _tested_map_status and not main.status_panel.visible:
|
||||
if not _tested_map_status and not main.ui.modals.status_panel.visible:
|
||||
_tested_map_status = true
|
||||
var mpb: Button = main.level_screen.status_badge
|
||||
var mpb: Button = main.ui.modals.level_screen.status_badge
|
||||
if mpb == null or not mpb.visible or mpb.disabled:
|
||||
_problems.append("地图屏状态角标缺失/未点亮/被置灰(P1 丁)")
|
||||
else:
|
||||
mpb.pressed.emit()
|
||||
if not main.status_panel.visible or not main.level_screen.visible:
|
||||
if not main.ui.modals.status_panel.visible or not main.ui.modals.level_screen.visible:
|
||||
_problems.append("地图屏角标开状态面板失败或地图屏被误藏(P2 甲层级失守)")
|
||||
var rbtn2: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix")
|
||||
var rbtn2: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix")
|
||||
if rbtn2 != null:
|
||||
rbtn2.pressed.emit()
|
||||
if main.status_panel.visible or not main.level_screen.visible:
|
||||
if main.ui.modals.status_panel.visible or not main.ui.modals.level_screen.visible:
|
||||
_problems.append("状态面板关闭后地图屏态失守(恢复语义破)")
|
||||
# 地图屏加成帽换代断言(迭代36 卡A wa2——「特性」→「属性值提升」;帽 Label 常在树;
|
||||
# 独立旗标——_tested_map_status 在同拍前置块已置位,禁复用其门控)
|
||||
@@ -482,7 +482,7 @@ func _ready() -> void:
|
||||
_tested_map_cap = true
|
||||
var cap_found := false
|
||||
var ls_labels: Array = []
|
||||
_collect_labels_by_text(main.level_screen, ls_labels)
|
||||
_collect_labels_by_text(main.ui.modals.level_screen, ls_labels)
|
||||
for l in ls_labels:
|
||||
if l.text == "属性值提升":
|
||||
cap_found = true
|
||||
@@ -494,20 +494,20 @@ 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.level_screen._cand_ids.has(nd_id):
|
||||
pick = main.level_screen._cand_ids.find(nd_id)
|
||||
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)
|
||||
break
|
||||
main.level_screen.press_candidate(pick)
|
||||
main.ui.modals.level_screen.press_candidate(pick)
|
||||
_route_clicks += 1
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main._inter_overlay.visible:
|
||||
if main.ui.screens.inter_overlay.visible:
|
||||
# 大奖励屏(冒险级停点——非末关的关卡完成恰一次):首见确认推进 L2(真实按钮信号路径);再见 = L2 已关卡完成,收口
|
||||
if not main._input_locked():
|
||||
if not main.ui.modals.flow.locks_input():
|
||||
_problems.append("大奖励屏打开时输入未锁定")
|
||||
_inter_confirms += 1
|
||||
if _inter_confirms == 1:
|
||||
var next_btn: Button = ProbeUiUtil.find_button(main._inter_overlay, "进入下一层", "prefix")
|
||||
var next_btn: Button = ProbeUiUtil.find_button(main.ui.screens.inter_overlay, "进入下一层", "prefix")
|
||||
if next_btn == null:
|
||||
_problems.append("大奖励屏确认按钮缺失")
|
||||
else:
|
||||
@@ -522,23 +522,23 @@ func _ready() -> void:
|
||||
break
|
||||
_seals_placed.clear()
|
||||
continue
|
||||
if main._end_overlay.is_open() and main._settlement_stop_pending:
|
||||
if main.ui.modals.end_overlay.is_open() and main.run_scope.settlement_stop_timer.is_armed():
|
||||
# 战斗胜利总结结算框(真实信号路径——每场普通胜利到达):断言已拍板面后点「下一节点」
|
||||
if not main._input_locked():
|
||||
if not main.ui.modals.flow.locks_input():
|
||||
_problems.append("胜利结算框打开时输入未锁定")
|
||||
var gp_line: String = "%s %s" % [main._end_overlay._gp_name.text, main._end_overlay._gp_val.text] # 两列账行(审计差距④)——名/值列拼回整串断言
|
||||
var gp_line: String = "%s %s" % [main.ui.modals.end_overlay._gp_name.text, main.ui.modals.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._end_overlay.verdict_text() == "":
|
||||
if main.ui.modals.end_overlay.verdict_text() == "":
|
||||
_problems.append("胜利结算框判定行缺失")
|
||||
_settlement_confirms += 1
|
||||
main._end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径
|
||||
main.ui.modals.end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
if main._settlement_stop_pending or not main.level_screen.visible:
|
||||
if main.run_scope.settlement_stop_timer.is_armed() or not main.ui.modals.level_screen.visible:
|
||||
_problems.append("胜利结算框「下一节点」未进选路页(卡⑥ AC②)")
|
||||
continue
|
||||
if main._end_overlay.is_open():
|
||||
if main.ui.modals.end_overlay.is_open():
|
||||
# 冒险终局(失败/放弃/打穿)——无单关直入口,见终局即收口(模态断言照旧)
|
||||
# 三选一链适配:_adventure_mode 仍真且非选路挂起=中途结算面(Boss 胜利
|
||||
# 总结「继续」后挂三选一→真终局未到)——真实信号路径点继续推进。
|
||||
@@ -547,13 +547,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._end_overlay._btn.pressed.emit()
|
||||
main.ui.modals.end_overlay._btn.pressed.emit()
|
||||
continue
|
||||
if not main._input_locked():
|
||||
if not main.ui.modals.flow.locks_input():
|
||||
_problems.append("终局 overlay 打开时输入未锁定")
|
||||
_done = true
|
||||
print("[click_probe] 冒险终局:%s · 选路口点击 %d 次" % [
|
||||
main._end_overlay.verdict_text().replace("\n", " "), _route_clicks])
|
||||
main.ui.modals.end_overlay.verdict_text().replace("\n", " "), _route_clicks])
|
||||
break
|
||||
if main.run_scope.settlement == null or main.run_scope.runner == null:
|
||||
continue
|
||||
@@ -566,16 +566,16 @@ func _ready() -> void:
|
||||
# 上轮摘要恢复断言(卡⑥ wr1-b——改前 _round_rows 类型错 append 全数失败、缓冲恒空、
|
||||
# 摘要自引入类型错以来从未显示):战中场观测点=round≥2 且排播队列已排空(轮首分隔行
|
||||
# 已消费=finish_settlement_round 已刷);不挂循环出口——收尾的续玩重建段会再走 log_view.clear()
|
||||
if not _tested_round_summary and int(st["round"]) >= 2 and main._row_queue.is_empty():
|
||||
if not _tested_round_summary and int(st["round"]) >= 2 and main.ui.screens.row_pacer.pending_rows().is_empty():
|
||||
_tested_round_summary = true
|
||||
if main.log_view._summary.text == "":
|
||||
if main.ui.battle.log_view._summary.text == "":
|
||||
_problems.append("上轮摘要恒空(卡⑥ wr1-b——结算行缓冲仍失真)")
|
||||
if JSON.stringify(st) == JSON.stringify(_last_state): # 排播演播期豁免(硬锁 v3——合法停顿态):演播期棋盘锁生效、probe 点击被吞、
|
||||
# 逻辑态恒定属预期。判据=纯队列(与 main 锁同源)。Waiting 收尾等待期**不豁免**——
|
||||
# 锁若把收尾链卡死,此处 stall 照报(探测面)。
|
||||
# T1 D1 处置=保留豁免+注记:冻结/缓存/重放语义不再靠豁免绕行,由 _locktest_tick
|
||||
# 脚本化断言正面承载(每 run 一次;逻辑态恒定本豁免继续作 stall 判据)。
|
||||
if main._row_queue.is_empty():
|
||||
if main.ui.screens.row_pacer.pending_rows().is_empty():
|
||||
_stall_frames += 1
|
||||
if _stall_frames == 30:
|
||||
_report_stall(st)
|
||||
@@ -595,36 +595,36 @@ func _ready() -> void:
|
||||
# ---- 预算耗尽收口(T17 硬锁批适配):选件变强后 probe 可能活过预算不再战败——断点段前提
|
||||
# (冒险终局面在案)须主动满足。收口驱动:任何停点层逐个推进(复用主循环分支语义)至
|
||||
# level_screen 选路停点→真实放弃二击→终局面。战斗中则继续 autoplay 打完。
|
||||
if not _done and not main._end_overlay.is_open():
|
||||
if not _done and not main.ui.modals.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._end_overlay.is_open():
|
||||
if main.ui.modals.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._end_overlay._btn.pressed.emit()
|
||||
main.ui.modals.end_overlay._btn.pressed.emit()
|
||||
else:
|
||||
break
|
||||
elif main.curio_overlay.visible:
|
||||
if main.curio_overlay._continue_btn.visible:
|
||||
main.curio_overlay.press_continue()
|
||||
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.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty():
|
||||
main.curio_overlay.press_card(0)
|
||||
main.curio_overlay.press_confirm()
|
||||
main.ui.modals.curio_overlay.press_card(0)
|
||||
main.ui.modals.curio_overlay.press_confirm()
|
||||
else:
|
||||
main.curio_overlay.press_continue()
|
||||
elif main.node_overlay.visible:
|
||||
main.node_overlay.press_continue()
|
||||
elif main.shop_overlay.visible:
|
||||
main.shop_overlay.leave_pressed.emit()
|
||||
elif main._inter_overlay.visible:
|
||||
var next_btn2: Button = ProbeUiUtil.find_button(main._inter_overlay, "进入下一层", "prefix")
|
||||
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()
|
||||
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.level_screen.visible:
|
||||
var abandon_btn: Button = ProbeUiUtil.find_button(main.level_screen, "放弃", "prefix")
|
||||
elif main.ui.modals.level_screen.visible:
|
||||
var abandon_btn: Button = ProbeUiUtil.find_button(main.ui.modals.level_screen, "放弃", "prefix")
|
||||
if abandon_btn != null:
|
||||
_closed_via_abandon = true
|
||||
abandon_btn.pressed.emit()
|
||||
@@ -652,14 +652,14 @@ 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._end_overlay.is_open():
|
||||
if not main.ui.modals.end_overlay.is_open():
|
||||
_problems.append("断点段:战败后未出冒险终局面")
|
||||
if main._end_overlay._btn == null:
|
||||
if main.ui.modals.end_overlay._btn == null:
|
||||
_problems.append("断点段:终局面返回按钮缺失")
|
||||
else:
|
||||
main._end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄)
|
||||
main.ui.modals.end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄)
|
||||
await get_tree().process_frame
|
||||
if not main._start_overlay.visible:
|
||||
if not main.ui.screens.start_overlay.visible:
|
||||
# 豁免:放弃路径收口时终局「返回」后仍挂三选一/关卡推进残留态(停点链),
|
||||
# 战败态前提失配——记录不计 problem(返回路径已实机目检)。
|
||||
if not _closed_via_abandon:
|
||||
@@ -683,16 +683,16 @@ 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.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑)
|
||||
main.debug_overlay.seed_edit.text = "42"
|
||||
main.ui.modals.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42"
|
||||
main._start_adventure_from_menu()
|
||||
var ol_l1_wait := 0
|
||||
while ol_l1_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
|
||||
while ol_l1_wait < 24 and String(main.ui.battle.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
|
||||
ol_l1_wait += 1
|
||||
await get_tree().process_frame
|
||||
if ol_l1_wait >= 24:
|
||||
_problems.append("开场行路径①:L1 开场行未达日志(开局清日志后 24 帧内未现种子行)")
|
||||
elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") > 0:
|
||||
elif String(main.ui.battle.log_view._log.get_parsed_text()).count("(含面板加成)") > 0:
|
||||
_problems.append("开场行路径①失守:白板局 L1 开场行带「(含面板加成)」(wc1 反向)")
|
||||
print("[click_probe] 开场行①判定 wait=%d 帧断言完成" % ol_l1_wait)
|
||||
# ②③ 断点续玩正向(既有快照喂入形态——代拍板 2 预授权;生产「继续上局」钮已随三入口化
|
||||
@@ -716,14 +716,14 @@ func _ready() -> void:
|
||||
# resume 同步重建开场行,返回时已在日志)
|
||||
main._resume_from_menu()
|
||||
var ol_r_wait := 0
|
||||
while ol_r_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
|
||||
while ol_r_wait < 24 and String(main.ui.battle.log_view._log.get_parsed_text()).count("· 种子 ") < 1:
|
||||
ol_r_wait += 1
|
||||
await get_tree().process_frame
|
||||
if main.run_scope.runner == null or main.run_scope.runner.journal().bonus_total("atk") != 5:
|
||||
_problems.append("开场行路径②③前提失守:续玩重建后加算区 atk!=5(断点回填链断)")
|
||||
if ol_r_wait >= 24:
|
||||
_problems.append("开场行路径②③失守:续玩重建未发开场行(选路停点形应重发——形态/驱动复核)")
|
||||
elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") < 1:
|
||||
elif String(main.ui.battle.log_view._log.get_parsed_text()).count("(含面板加成)") < 1:
|
||||
_problems.append("开场行路径②③失守:bonus 已入账(pct=0/ov=0)的续玩重建开场行未带「(含面板加成)」(wc1 正向/wc2)")
|
||||
else:
|
||||
print("[click_probe] 开场行②③判定:续玩重建开场行带后缀(bonus.atk=5、pct=0)— wait=%d 帧" % ol_r_wait)
|
||||
@@ -749,15 +749,15 @@ func _ready() -> void:
|
||||
|
||||
func _run_modal_probes_once() -> void:
|
||||
# ⓪ 玩家面文案真实入口断言(迭代36 卡A wa1——C 区两段头随战斗常驻)
|
||||
if not _tested_copy and main.info_panel != null and main.info_panel.visible:
|
||||
if not _tested_copy and main.ui.battle.info_panel != null and main.ui.battle.info_panel.visible:
|
||||
_tested_copy = true
|
||||
var feat_head: Label = null
|
||||
var curio_head: Label = null
|
||||
for child in main.info_panel._feat_section.get_children():
|
||||
for child in main.ui.battle.info_panel._feat_section.get_children():
|
||||
if child is Label:
|
||||
feat_head = child
|
||||
break
|
||||
for child in main.info_panel._curio_section.get_children():
|
||||
for child in main.ui.battle.info_panel._curio_section.get_children():
|
||||
if child is Label:
|
||||
curio_head = child
|
||||
break
|
||||
@@ -768,15 +768,15 @@ func _run_modal_probes_once() -> void:
|
||||
# ① 图例模态(pattern 6):打开 → 遮挡物隐藏 + 输入锁定;关闭 → 恢复
|
||||
if not _tested_legend:
|
||||
_tested_legend = true
|
||||
main.ap_bar._btn_legend.pressed.emit()
|
||||
if not main.legend_overlay.is_open():
|
||||
main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit()
|
||||
if not main.ui.modals.legend_overlay.is_open():
|
||||
_problems.append("图例打开失败")
|
||||
if main._end_overlay.is_open() or main._start_overlay.visible:
|
||||
if main.ui.modals.end_overlay.is_open() or main.ui.screens.start_overlay.visible:
|
||||
_problems.append("图例打开时上方遮挡层未隐藏(缺陷⑤复发)")
|
||||
if not main._input_locked():
|
||||
if not main.ui.modals.flow.locks_input():
|
||||
_problems.append("图例打开时棋盘输入未锁定")
|
||||
var legend_has_seal := false
|
||||
for e in main.legend_overlay.ENTRIES:
|
||||
for e in main.ui.modals.legend_overlay.ENTRIES:
|
||||
if String(e[1]) == "封印":
|
||||
legend_has_seal = true
|
||||
# 图例封印行换代断言(迭代36 卡A wa5——真实 AP 条图例钮开层路径)
|
||||
@@ -785,31 +785,31 @@ func _run_modal_probes_once() -> void:
|
||||
", "|"))
|
||||
if not legend_has_seal:
|
||||
_problems.append("图例缺『封印』格态条目(格态图例基线)")
|
||||
main.legend_overlay.close()
|
||||
if main.legend_overlay.is_open() or main._input_locked():
|
||||
main.ui.modals.legend_overlay.close()
|
||||
if main.ui.modals.legend_overlay.is_open() or main.ui.modals.flow.locks_input():
|
||||
_problems.append("图例关闭后未恢复输入/可见性")
|
||||
# ②/②b 明细回看模态无探针(M9/P-C7——冒险中回看归「生涯」,06 登记)
|
||||
# ⑤ 状态面板(P1 丁角标真实信号路径 + P2 甲模态语义 + P1 守卫 + R3 锁恢复):
|
||||
# 角标打开 → 只读层可见+输入锁定+余额徽非空 → 「继续」关闭恢复;图例开着 → 角标置灰且点击被守卫兜住
|
||||
if not _tested_status and not main.legend_overlay.is_open() and not main.status_panel.visible:
|
||||
if not _tested_status and not main.ui.modals.legend_overlay.is_open() and not main.ui.modals.status_panel.visible:
|
||||
_tested_status = true
|
||||
var pbtn: Button = main.board_zone.status_badge
|
||||
var pbtn: Button = main.ui.battle.board_zone.status_badge
|
||||
if pbtn == null or not pbtn.visible or pbtn.disabled:
|
||||
_problems.append("战斗屏状态角标缺失/未点亮/被置灰(P1 丁入口失守 visible=%s disabled=%s)"
|
||||
% [str(pbtn != null and pbtn.visible), str(pbtn != null and pbtn.disabled)])
|
||||
else:
|
||||
pbtn.pressed.emit() # 真实按钮信号路径 → 组合根 _on_status_badge_pressed
|
||||
if not main.status_panel.visible:
|
||||
if not main.ui.modals.status_panel.visible:
|
||||
_problems.append("状态面板未随角标打开(组合根接线失守)")
|
||||
if not main._input_locked():
|
||||
if not main.ui.modals.flow.locks_input():
|
||||
_problems.append("状态面板打开时输入未锁定(R3)")
|
||||
var bal_txt: String = main.status_panel._balance_badge.value_label.text
|
||||
var bal_txt: String = main.ui.modals.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.status_panel, sv_labels)
|
||||
_collect_labels_by_text(main.ui.modals.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))
|
||||
@@ -821,8 +821,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.status_panel._attrs_label.text) != attr_expect:
|
||||
_problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.status_panel._attrs_label.text])
|
||||
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])
|
||||
# ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立 oracle)
|
||||
var sd: Dictionary = main._status_data()
|
||||
var keys := ["balance", "hero_hp", "max_hp", "atk", "ac", "gp", "curios", "feats"]
|
||||
@@ -846,25 +846,25 @@ 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.status_panel, "继续", "prefix")
|
||||
var resume_btn: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix")
|
||||
if resume_btn == null:
|
||||
_problems.append("状态面板关闭钮缺失(「继续」)")
|
||||
else:
|
||||
resume_btn.pressed.emit()
|
||||
if main.status_panel.visible or main._input_locked():
|
||||
if main.ui.modals.status_panel.visible or main.ui.modals.flow.locks_input():
|
||||
_problems.append("状态面板关闭后未恢复(visible=%s locked=%s——R3 残留)"
|
||||
% [str(main.status_panel.visible), str(main._input_locked())])
|
||||
% [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())])
|
||||
# P1 守卫:图例开着 → 两处入口角标置灰;置灰态点击被处理器兜住(探针 emit 绕过 disabled)
|
||||
main.ap_bar._btn_legend.pressed.emit()
|
||||
if not main.board_zone.status_badge.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.board_zone.status_badge.pressed.emit()
|
||||
if main.status_panel.visible:
|
||||
main.ui.battle.board_zone.status_badge.pressed.emit()
|
||||
if main.ui.modals.status_panel.visible:
|
||||
_problems.append("置灰态仍可开状态面板(守卫兜底失守)")
|
||||
main.legend_overlay.close()
|
||||
main.ui.modals.legend_overlay.close()
|
||||
# ③ tooltip(状态效果槽悬停——来源+语义+剩余):有槽可见时触发
|
||||
if not _tested_tooltip:
|
||||
var row = main.stage.hero_standee.slot_row # 状态 tooltip 载体(B 区站桩)
|
||||
var row = main.ui.battle.stage.hero_standee.slot_row # 状态 tooltip 载体(B 区站桩)
|
||||
for child in row.get_children():
|
||||
if child is PanelContainer and child.visible and child.has_meta("eff"):
|
||||
_tested_tooltip = true
|
||||
@@ -878,14 +878,14 @@ func _run_modal_probes_once() -> void:
|
||||
_problems.append("状态槽 tooltip 未随移出隐藏")
|
||||
break
|
||||
# ④ 弃图刷新确认链(pattern 7 异位:确认 chips 与常规确认不同位 + 显式代价文案)
|
||||
if not _tested_refresh_chain and main.ap_bar.is_refresh_ready():
|
||||
if not _tested_refresh_chain and main.ui.battle.board_zone.ap_bar.is_refresh_ready():
|
||||
_tested_refresh_chain = true
|
||||
var rap_before: int = int(main.run_scope.settlement.action_economy.rap)
|
||||
var board_before = main.run_scope.settlement.minesweeper.current_board()
|
||||
main.ap_bar.press_refresh_for_probe()
|
||||
if not main.ap_bar.confirm_visible():
|
||||
main.ui.battle.board_zone.ap_bar.press_refresh_for_probe()
|
||||
if not main.ui.battle.board_zone.ap_bar.confirm_visible():
|
||||
_problems.append("弃图刷新确认 chips 未出现(异位确认缺失)")
|
||||
main.ap_bar.press_confirm_for_probe()
|
||||
main.ui.battle.board_zone.ap_bar.press_confirm_for_probe()
|
||||
if int(main.run_scope.settlement.action_economy.rap) != rap_before - 8:
|
||||
_problems.append("弃图刷新未扣 RAP8(%d→%d)" % [rap_before, int(main.run_scope.settlement.action_economy.rap)])
|
||||
if main.run_scope.settlement.action_economy.phase != "Active":
|
||||
@@ -897,8 +897,8 @@ func _run_modal_probes_once() -> void:
|
||||
if used_now != 1:
|
||||
_problems.append("刷新轮内计数错误(%d)" % used_now)
|
||||
var remain_expect: int = maxi(0, int(main.db.run.action_economy.refresh_cap_per_round) - used_now)
|
||||
if not main.ap_bar.refresh_remaining_text().contains(str(remain_expect)):
|
||||
_problems.append("HUD 刷新余量与快照推导失符(HUD='%s' 应含 %d)" % [main.ap_bar.refresh_remaining_text(), remain_expect])
|
||||
if not main.ui.battle.board_zone.ap_bar.refresh_remaining_text().contains(str(remain_expect)):
|
||||
_problems.append("HUD 刷新余量与快照推导失符(HUD='%s' 应含 %d)" % [main.ui.battle.board_zone.ap_bar.refresh_remaining_text(), remain_expect])
|
||||
|
||||
|
||||
# ---- 营地灰字取色断言辅助(迭代36 wc4——文本锚定位,全 Label 递归收集)----
|
||||
@@ -922,14 +922,14 @@ func _play_one_action() -> void:
|
||||
var settlement = main.run_scope.settlement
|
||||
if settlement == null:
|
||||
return # 结算竞态防抖:battle_finished 与本轮动作调度同帧到达时 settlement 可能已被置空(基线既有噪声——加固)
|
||||
if not main._row_queue.is_empty():
|
||||
if not main.ui.screens.row_pacer.pending_rows().is_empty():
|
||||
return # T1 D1——演播期不出手:此时点击只会进锁期缓存被「最后一次」覆盖(原逐帧连点语义无行为覆盖价值,且毁锁期断言注入的独占前提)
|
||||
var minesweeper = settlement.minesweeper
|
||||
# 行为①:随机封印(右键——未开相邻格),约 20% 轮次
|
||||
if _ap_spent_this_round == 0 and _rand.randf() < 0.2:
|
||||
var target := _random_unknown_adjacent(minesweeper)
|
||||
if target.x >= 0:
|
||||
main.board_view.click_cell(target, MOUSE_BUTTON_RIGHT)
|
||||
main.ui.battle.board_zone.board_view.click_cell(target, MOUSE_BUTTON_RIGHT)
|
||||
_seals_placed.append(target)
|
||||
_ap_spent_this_round += 1
|
||||
return
|
||||
@@ -937,19 +937,19 @@ func _play_one_action() -> void:
|
||||
if _rand.randf() < 0.1 and not _seals_placed.is_empty():
|
||||
var fc: Vector2i = _seals_placed.pop_back()
|
||||
if minesweeper.has_seal(fc) and not minesweeper.is_revealed(fc):
|
||||
main.board_view.click_cell(fc, MOUSE_BUTTON_RIGHT)
|
||||
main.ui.battle.board_zone.board_view.click_cell(fc, MOUSE_BUTTON_RIGHT)
|
||||
_ap_spent_this_round += 1
|
||||
return
|
||||
# 行为③:随机无效点击(非相邻格/已开格),约 8% 轮次
|
||||
if _rand.randf() < 0.08:
|
||||
main.board_view.click_cell(Vector2i(_rand.randi_range(0, minesweeper.current_board().width - 1), _rand.randi_range(0, minesweeper.current_board().height - 1)), MOUSE_BUTTON_LEFT)
|
||||
main.ui.battle.board_zone.board_view.click_cell(Vector2i(_rand.randi_range(0, minesweeper.current_board().width - 1), _rand.randi_range(0, minesweeper.current_board().height - 1)), MOUSE_BUTTON_LEFT)
|
||||
return
|
||||
# 挖掘主策略(最近可挖格)
|
||||
var plan = AutoPlayer.choose_dig(minesweeper) # O4 收口:策略单一实现
|
||||
if not plan.is_empty():
|
||||
if plan.hero_cell != minesweeper.hero_pos():
|
||||
main.board_view.click_cell(plan.hero_cell, MOUSE_BUTTON_LEFT)
|
||||
main.board_view.click_cell(plan.dig_cell, MOUSE_BUTTON_LEFT)
|
||||
main.ui.battle.board_zone.board_view.click_cell(plan.hero_cell, MOUSE_BUTTON_LEFT)
|
||||
main.ui.battle.board_zone.board_view.click_cell(plan.dig_cell, MOUSE_BUTTON_LEFT)
|
||||
_ap_spent_this_round += 1
|
||||
# 行为④:提前停手(已花 ≥3 AP 时 15%)——停手经 AP 条按钮真实信号路径
|
||||
if _ap_spent_this_round >= 3 and settlement.action_economy.phase == "Active" and _rand.randf() < 0.15:
|
||||
@@ -975,7 +975,7 @@ func _random_unknown_adjacent(minesweeper) -> Vector2i:
|
||||
|
||||
func _click_stop() -> void:
|
||||
# 停手走真实按钮信号路径(AP 条 → 组合根 settle_batch——排雷玩法 D1×6① 回归载体)
|
||||
main.ap_bar._btn_stop.pressed.emit()
|
||||
main.ui.battle.board_zone.ap_bar._btn_stop.pressed.emit()
|
||||
|
||||
|
||||
# ---- T1 D1:锁期/重放断言(每 run 一次,脚本化场景——演播期豁免的断言化承载) ----
|
||||
@@ -992,7 +992,7 @@ func _locktest_tick() -> void:
|
||||
if settlement == null or settlement.minesweeper.current_board() == null:
|
||||
return
|
||||
if _locktest_state == 0:
|
||||
if main._row_queue.is_empty() or main._input_locked():
|
||||
if main.ui.screens.row_pacer.pending_rows().is_empty() or main.ui.modals.flow.locks_input():
|
||||
return
|
||||
if String(settlement.battle_result) != "" or String(settlement.action_economy.phase) != "Active":
|
||||
return
|
||||
@@ -1003,7 +1003,7 @@ func _locktest_tick() -> void:
|
||||
return # 无合法邻格/目标被并发封印行为占位——下帧重挑
|
||||
_locktest_cell = target
|
||||
_locktest_frames = 0
|
||||
main.board_view.click_cell(target, MOUSE_BUTTON_RIGHT)
|
||||
main.ui.battle.board_zone.board_view.click_cell(target, MOUSE_BUTTON_RIGHT)
|
||||
if settlement.minesweeper.has_seal(target):
|
||||
_problems.append("锁期断言:演播期点击未被冻结(封印即时受理——v3 冻结失守)")
|
||||
_locktest_state = 2
|
||||
@@ -1014,11 +1014,11 @@ func _locktest_tick() -> void:
|
||||
return
|
||||
_locktest_state = 1
|
||||
elif _locktest_state == 1:
|
||||
if main.curio_overlay.visible or main._inter_overlay.visible or main._end_overlay.is_open() \
|
||||
if main.ui.modals.curio_overlay.visible or main.ui.screens.inter_overlay.visible or main.ui.modals.end_overlay.is_open() \
|
||||
or String(settlement.battle_result) != "":
|
||||
_locktest_state = 2 # 前提消失守卫:终结演播竞态——静默弃测
|
||||
return
|
||||
if not main._row_queue.is_empty():
|
||||
if not main.ui.screens.row_pacer.pending_rows().is_empty():
|
||||
_locktest_frames = 0 # 演播未完——重放耐心只在排空后起算(链式大演播数百帧不占预算)
|
||||
return
|
||||
_locktest_frames += 1
|
||||
@@ -1038,7 +1038,7 @@ func _locktest_tick() -> void:
|
||||
b.width, b.height, str(settlement.minesweeper.hero_pos())])
|
||||
_problems.append("重放断言:锁期缓存点击未受理恰一次(封印未置位——零发或双发)")
|
||||
_locktest_state = 2
|
||||
main.board_view.click_cell(_locktest_cell, MOUSE_BUTTON_RIGHT) # 撤封印恢复现场(尽力而为不判定)
|
||||
main.ui.battle.board_zone.board_view.click_cell(_locktest_cell, MOUSE_BUTTON_RIGHT) # 撤封印恢复现场(尽力而为不判定)
|
||||
|
||||
|
||||
func _locktest_pick_cell(minesweeper) -> Vector2i:
|
||||
|
||||
@@ -31,15 +31,15 @@ func _ready() -> void:
|
||||
# 同 ui_click_probe 隔离纪律。首次冒险布尔位置位=对齐真实档游玩史口径,模板抽签路径不变;
|
||||
# isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_input_probe_save", true, false, true)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,探针确定性禁依赖默认分支)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,探针确定性禁依赖默认分支)
|
||||
main._start_adventure_from_menu()
|
||||
await get_tree().process_frame
|
||||
# ---- ⑫ 图上命中真实输入(回归闸):入口停点候选节点中心
|
||||
# push_input 真实鼠标路径 motion+click → 悬停预承诺置位 → route_chosen 到达组合根。
|
||||
# 由来=真机 bug:深层节点(L1 Boss 内容 x=802)在固定画布命中区外,press_candidate 直调
|
||||
# 命中函数探针全绿、真鼠标点不中——本 case 封住输入管线,⑬封住几何拟合。
|
||||
if main.level_screen.visible:
|
||||
var ro = main.level_screen
|
||||
if main.ui.modals.level_screen.visible:
|
||||
var ro = main.ui.modals.level_screen
|
||||
var cands: Array = main.run_scope.runner.next_candidate_ids()
|
||||
# ---- ⑱ 信息迷雾呈现接线(回归闸):真机 display_probe 读实际绘制记录(雾下节点类型未画、
|
||||
# Boss 描边画了、候选类型画了)+点击雾下非候选节点零提交(雾不扩大可点域——候选集即合法集)----
|
||||
@@ -90,9 +90,9 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
ro.route_chosen.disconnect(on_route)
|
||||
if not submitted.is_empty() or not main.level_screen.visible or not main.run_scope.runner.awaiting_choice():
|
||||
if not submitted.is_empty() or not main.ui.modals.level_screen.visible or not main.run_scope.runner.awaiting_choice():
|
||||
_pre_fail("case⑱ 雾下节点点击产生提交(提交=%s visible=%s 选路态=%s——雾不扩大可点域)〔fv9〕"
|
||||
% [str(submitted), str(main.level_screen.visible), str(main.run_scope.runner.awaiting_choice())])
|
||||
% [str(submitted), str(main.ui.modals.level_screen.visible), str(main.run_scope.runner.awaiting_choice())])
|
||||
return
|
||||
var nds: Array = main.run_scope.runner.nodes()
|
||||
var tgt := {}
|
||||
@@ -116,13 +116,13 @@ func _ready() -> void:
|
||||
main.get_viewport().push_input(evc, true)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
if main.level_screen.visible or main.run_scope.runner.awaiting_choice():
|
||||
_pre_fail("图上真实点击未提交选路(overlay 可见=%s 选路态=%s)" % [str(main.level_screen.visible), str(main.run_scope.runner.awaiting_choice())])
|
||||
if main.ui.modals.level_screen.visible or main.run_scope.runner.awaiting_choice():
|
||||
_pre_fail("图上真实点击未提交选路(overlay 可见=%s 选路态=%s)" % [str(main.ui.modals.level_screen.visible), str(main.run_scope.runner.awaiting_choice())])
|
||||
for i in range(3):
|
||||
await get_tree().process_frame
|
||||
# ---- ⑬ 画布随图拟合(同 bug 的几何面回归):复刻分层公式独立推导最深节点内容 x,
|
||||
# 断言画布 min 尺寸覆盖之(open_choices 每 stop 拟合——L1 单链 Boss x=70+6*122=802 曾越界)
|
||||
var ro2 = main.level_screen
|
||||
var ro2 = main.ui.modals.level_screen
|
||||
var nds2: Array = main.run_scope.runner.nodes()
|
||||
var layer2 := {}
|
||||
var inbound2 := {}
|
||||
@@ -155,7 +155,7 @@ func _ready() -> void:
|
||||
var cvmin: Dictionary = ro2.canvas_probe()
|
||||
if float(cvmin["min_size"].x) < need_x2 - 0.5:
|
||||
_pre_fail("画布拟合不足(min.x=%.0f < 最深内容 %.0f——深层节点不可点回归)" % [cvmin["min_size"].x, need_x2])
|
||||
var bv = main.board_view
|
||||
var bv = main.ui.battle.board_zone.board_view
|
||||
var ms = main.run_scope.settlement.minesweeper if main.run_scope.settlement != null else null
|
||||
if ms == null or bv.cells_probe().is_empty():
|
||||
_pre_fail("战斗未就绪(cells=%d settlement=%s)" % [bv.cells_probe().size(), str(main.run_scope.settlement)])
|
||||
@@ -517,29 +517,29 @@ func _ready() -> void:
|
||||
if status_dig.x == -9:
|
||||
_problems.append("case⑯ 无可挖无封印格(板面异常——锁断言中断)")
|
||||
else:
|
||||
var pbtn16: Button = main.board_zone.status_badge
|
||||
var pbtn16: Button = main.ui.battle.board_zone.status_badge
|
||||
var bpos: Vector2 = pbtn16.get_global_rect().get_center()
|
||||
_push_click(bpos, MOUSE_BUTTON_LEFT)
|
||||
_push_release(bpos, MOUSE_BUTTON_LEFT) # Button pressed 信号走 release 沿——按压+释放成对
|
||||
await get_tree().process_frame
|
||||
if not main.status_panel.visible or not main._input_locked():
|
||||
if not main.ui.modals.status_panel.visible or not main.ui.modals.flow.locks_input():
|
||||
_problems.append("case⑯ 角标真点击未开状态面板(visible=%s locked=%s——入口可达性失守)"
|
||||
% [str(main.status_panel.visible), str(main._input_locked())])
|
||||
% [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())])
|
||||
else:
|
||||
_push_click(_pos(bv, status_dig), MOUSE_BUTTON_LEFT)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
if _has_action(status_dig, "dig") or ms.is_revealed(status_dig):
|
||||
_problems.append("case⑯ 状态面板开启时挖掘穿透(R3 输入锁破——目标=%s)" % status_dig)
|
||||
var resume_btn: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix")
|
||||
var resume_btn: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix")
|
||||
if resume_btn == null:
|
||||
_problems.append("case⑯ 状态面板关闭钮缺失")
|
||||
else:
|
||||
resume_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.status_panel.visible or main._input_locked():
|
||||
if main.ui.modals.status_panel.visible or main.ui.modals.flow.locks_input():
|
||||
_problems.append("case⑯ 关闭后未恢复(visible=%s locked=%s——R3 残留)"
|
||||
% [str(main.status_panel.visible), str(main._input_locked())])
|
||||
% [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())])
|
||||
_push_click(_pos(bv, status_dig), MOUSE_BUTTON_LEFT)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
|
||||
+51
-51
@@ -16,7 +16,7 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
var problems: Array = []
|
||||
# ---- 放弃入口(台账 119):默认隐藏(非冒险模式无冒险可放弃——开始层/调试两层)----
|
||||
if main.level_screen.abandon_btn == null or main.level_screen.abandon_btn.visible:
|
||||
if main.ui.modals.level_screen.abandon_btn == null or main.ui.modals.level_screen.abandon_btn.visible:
|
||||
print("[ui_probe] FAIL — 放弃按钮默认应隐藏(非冒险模式关)")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
@@ -28,99 +28,99 @@ func _ready() -> void:
|
||||
problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
else:
|
||||
await get_tree().process_frame
|
||||
if not main.camp_overlay.visible:
|
||||
if not main.ui.modals.camp_overlay.visible:
|
||||
problems.append("营地层未打开(进档开层新路径)")
|
||||
elif main._start_overlay.visible:
|
||||
elif main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)")
|
||||
else:
|
||||
if not String(main.camp_overlay._balance_label.text).begins_with("钥石"):
|
||||
problems.append("营地余额文案缺失(%s)" % main.camp_overlay._balance_label.text)
|
||||
if main.camp_overlay._tree_buy_btns.is_empty():
|
||||
if not String(main.ui.modals.camp_overlay._balance_label.text).begins_with("钥石"):
|
||||
problems.append("营地余额文案缺失(%s)" % main.ui.modals.camp_overlay._balance_label.text)
|
||||
if main.ui.modals.camp_overlay._tree_buy_btns.is_empty():
|
||||
problems.append("树淬炼柜台购买钮缺失(四柜台重排后树=主界面)")
|
||||
else:
|
||||
var btn_by_id := {}
|
||||
for card2 in main.camp_overlay._tree_buy_btns:
|
||||
for card2 in main.ui.modals.camp_overlay._tree_buy_btns:
|
||||
btn_by_id[String(card2.node_id)] = card2.btn
|
||||
if not btn_by_id.has("sp_hp") or bool(btn_by_id["sp_hp"].disabled):
|
||||
problems.append("脊柱生命 L1 白板可购钮未启用(无门槛——can_buy 不含余额面语义)")
|
||||
if not btn_by_id.has("att_socket2") or not bool(btn_by_id["att_socket2"].disabled):
|
||||
problems.append("同调位 2 前置未亮应禁用(点亮前置语义)")
|
||||
if main.camp_overlay._workshop_rows.size() != 12:
|
||||
problems.append("秘宝匣柜台行数错误(%d ≠ 12——迭代 22 秘宝批换代)" % main.camp_overlay._workshop_rows.size())
|
||||
if main.ui.modals.camp_overlay._workshop_rows.size() != 12:
|
||||
problems.append("秘宝匣柜台行数错误(%d ≠ 12——迭代 22 秘宝批换代)" % main.ui.modals.camp_overlay._workshop_rows.size())
|
||||
main._on_camp_closed()
|
||||
await get_tree().process_frame
|
||||
main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点)
|
||||
await get_tree().process_frame
|
||||
if main.camp_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.camp_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("营地关闭后未恢复开始层(开合往返失守)")
|
||||
# 换回全新零档(营地段购买态不泄入后续冒险段——确定性;先删后建防陈旧档泄入)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", true, false, true)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机;本探针 start_single 第五路同吃此框,确定性禁依赖默认分支)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机;本探针 start_single 第五路同吃此框,确定性禁依赖默认分支)
|
||||
main._start_adventure_from_menu()
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
# ---- 入口选路停点(关卡开始即出节点图——level_started 后、首战前弹选路层;
|
||||
# press_candidate 图上命中路径 pick 入口单候选进首战)----
|
||||
# ---- 独立性断言(独立屏激活期战斗屏宿主整体隐藏,零透出)----
|
||||
if not main.level_screen.visible:
|
||||
if not main.ui.modals.level_screen.visible:
|
||||
problems.append("关卡开始未弹入口选路停点(level_screen 应可见)")
|
||||
elif main._battle_root.visible:
|
||||
elif main.ui.screens.battle_root.visible:
|
||||
problems.append("独立屏激活期战斗屏宿主未隐藏(零透出契约失守)")
|
||||
elif main.run_scope.runner == null or not main.run_scope.runner.awaiting_choice() \
|
||||
or main.run_scope.runner.next_candidate_ids().size() != 1:
|
||||
problems.append("入口停点状态失守(选路态 + 入口单候选)")
|
||||
else:
|
||||
main.level_screen.press_candidate(0)
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
if not main._battle_root.visible or main.level_screen.visible:
|
||||
if not main.ui.screens.battle_root.visible or main.ui.modals.level_screen.visible:
|
||||
problems.append("选路提交战斗节点后未切回战斗屏(屏级互斥恢复失守)")
|
||||
# ---- 放弃入口·冒险模式可见 + 战斗中按下被拒(真实按钮路径——入口挂选路停点,战斗中拒绝为防御分支)----
|
||||
if main.level_screen.abandon_btn == null or not main.level_screen.abandon_btn.visible:
|
||||
if main.ui.modals.level_screen.abandon_btn == null or not main.ui.modals.level_screen.abandon_btn.visible:
|
||||
problems.append("冒险模式放弃按钮未开放(选路停点入口在案)")
|
||||
else:
|
||||
main.level_screen.abandon_btn.pressed.emit()
|
||||
main.ui.modals.level_screen.abandon_btn.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if main.run_scope.adventure == null or String(main.run_scope.adventure.state) != "running":
|
||||
problems.append("首战中放弃未被拒(战斗域单事务在飞——state 应保持 running)")
|
||||
# ---- 既有断言(回归基线;A 区无进度条——节点链进度由关卡画面独立屏承载)----
|
||||
if main.board_view.cells_probe().is_empty():
|
||||
if main.ui.battle.board_zone.board_view.cells_probe().is_empty():
|
||||
problems.append("棋盘格未生成(cells=0)")
|
||||
if main.board_view.board() == null:
|
||||
if main.ui.battle.board_zone.board_view.board() == null:
|
||||
problems.append("board 未装载")
|
||||
if main.info_panel.attack_text().contains("—") or main.info_panel.armor_text().contains("—"):
|
||||
problems.append("C 区攻击/护甲未显示(%s | %s)" % [main.info_panel.attack_text(), main.info_panel.armor_text()])
|
||||
if main.ui.battle.info_panel.attack_text().contains("—") or main.ui.battle.info_panel.armor_text().contains("—"):
|
||||
problems.append("C 区攻击/护甲未显示(%s | %s)" % [main.ui.battle.info_panel.attack_text(), main.ui.battle.info_panel.armor_text()])
|
||||
if main.run_scope.settlement == null or main.run_scope.settlement.battle_result != "":
|
||||
problems.append("首战未处于进行中")
|
||||
if not main.log_view.parsed_text().contains("节点 1/"):
|
||||
if not main.ui.battle.log_view.parsed_text().contains("节点 1/"):
|
||||
problems.append("节点头日志缺失")
|
||||
# ---- 四区布局断言(battle.md §Layout)----
|
||||
if not main.stage.round_label.text.contains("第 1 轮"):
|
||||
problems.append("B 区轮次·阶段指示缺失(%s)" % main.stage.round_label.text)
|
||||
if main.stage.enemy_box.get_child_count() < 1:
|
||||
problems.append("B 区敌方站桩未构建(%d 个)" % main.stage.enemy_box.get_child_count())
|
||||
if main.ap_bar._dots.get_child_count() != int(main.db.run.action_economy.ap_per_round):
|
||||
problems.append("D 区 AP 点阵数量错误(%d)" % main.ap_bar._dots.get_child_count())
|
||||
if main.ap_bar.refresh_remaining_text() == "":
|
||||
if not main.ui.battle.stage.round_label.text.contains("第 1 轮"):
|
||||
problems.append("B 区轮次·阶段指示缺失(%s)" % main.ui.battle.stage.round_label.text)
|
||||
if main.ui.battle.stage.enemy_box.get_child_count() < 1:
|
||||
problems.append("B 区敌方站桩未构建(%d 个)" % main.ui.battle.stage.enemy_box.get_child_count())
|
||||
if main.ui.battle.board_zone.ap_bar._dots.get_child_count() != int(main.db.run.action_economy.ap_per_round):
|
||||
problems.append("D 区 AP 点阵数量错误(%d)" % main.ui.battle.board_zone.ap_bar._dots.get_child_count())
|
||||
if main.ui.battle.board_zone.ap_bar.refresh_remaining_text() == "":
|
||||
problems.append("D 区弃图刷新按钮文案缺失")
|
||||
if main.board_zone.toast == null:
|
||||
if main.ui.battle.board_zone.toast == null:
|
||||
problems.append("D 区 toast 槽缺失")
|
||||
if main.info_panel.item_slots == null or main.info_panel.item_slots.get_child_count() != 4:
|
||||
if main.ui.battle.info_panel.item_slots == null or main.ui.battle.info_panel.item_slots.get_child_count() != 4:
|
||||
problems.append("C 区秘宝槽(预留)缺失")
|
||||
if main.stage.hero_standee.slot_row == null:
|
||||
if main.ui.battle.stage.hero_standee.slot_row == null:
|
||||
problems.append("B 区站桩状态行缺失(状态效果唯一展示面——2026-09-26 C 区精简批)")
|
||||
# B 区明细带挖掘期收起断言(T17 视觉扩展批适配):结算演播期 settling 视觉延伸=mode=settle
|
||||
# 属新语义(演播放完回 dig——main._is_settling 排播项,判据=纯队列),本断言仅在排播空闲态判定。
|
||||
if main._row_queue.is_empty() \
|
||||
and (main.log_view.mode() != "dig" or main.log_view.custom_minimum_size.y != 24.0):
|
||||
problems.append("B 区明细带挖掘期应收起 24px(mode=%s h=%s)" % [main.log_view.mode(), str(main.log_view.custom_minimum_size.y)])
|
||||
if main.ui.screens.row_pacer.pending_rows().is_empty() \
|
||||
and (main.ui.battle.log_view.mode() != "dig" or main.ui.battle.log_view.custom_minimum_size.y != 24.0):
|
||||
problems.append("B 区明细带挖掘期应收起 24px(mode=%s h=%s)" % [main.ui.battle.log_view.mode(), str(main.ui.battle.log_view.custom_minimum_size.y)])
|
||||
# E 区无道具面板——四区构建由其余断言覆盖
|
||||
|
||||
# ---- 敌方站桩投影(顺位徽标/意图三档动作动画占位——敌人系统×4,全站桩)----
|
||||
# B 区站桩子件伴随件化(壳层 tooltip 载体)——断言口径见台账 171:
|
||||
# 伴随件化(壳层 tooltip 载体),穿透明(子件 _name_label/_armor_icon/_intent_badge)改本体
|
||||
# 探针出口 stage.enemy_body_at(i).name_line_probe()/intent_probe()——呈现口径逐字保持
|
||||
var standee = main.stage.enemy_body_at(0)
|
||||
var standee = main.ui.battle.stage.enemy_body_at(0)
|
||||
if standee == null:
|
||||
problems.append("敌立绘本体缺失(enemy_body_at(0) 无世界站桩)")
|
||||
else:
|
||||
@@ -138,12 +138,12 @@ func _ready() -> void:
|
||||
# 站桩纹理非空断言(八敌真图落位;缺位回退链保持的对照面)
|
||||
if not bool(standee.portrait_probe().get("has_tex", false)):
|
||||
problems.append("敌立绘纹理未落位(素材管线 48px 档——迭代 6 T2;缺位回退链不应在此态触发)")
|
||||
if main.stage.hero_standee == null or not main.stage.hero_standee.has_world_hp_bar:
|
||||
if main.ui.battle.stage.hero_standee == null or not main.ui.battle.stage.hero_standee.has_world_hp_bar:
|
||||
problems.append("B 区主角站桩缺失(世界血条面缺失)")
|
||||
elif not bool(main.stage.hero_standee.portrait_probe().get("has_tex", false)):
|
||||
elif not bool(main.ui.battle.stage.hero_standee.portrait_probe().get("has_tex", false)):
|
||||
problems.append("主角立绘纹理未落位(48px 档——迭代 6 T2/T3)")
|
||||
# ---- BUG-0001 回归(tooltip 入树后 set_effects 槽位不错位——结算软锁修复沉淀)----
|
||||
var slots_row = main.stage.hero_standee.slot_row # BUG-0001 回归随组件迁入站桩(tooltip/槽位错位同源)
|
||||
var slots_row = main.ui.battle.stage.hero_standee.slot_row # BUG-0001 回归随组件迁入站桩(tooltip/槽位错位同源)
|
||||
var probe_eff: Array = [{"effect_id": "attack_up", "display_name": "增伤", "layers": 1,
|
||||
"remaining_settlements": 1, "source_class": "safe_cell", "round_index": 1, "effective_value": 50}]
|
||||
slots_row.set_effects(probe_eff)
|
||||
@@ -196,12 +196,12 @@ func _ready() -> void:
|
||||
main._debug.start_single(2)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
if main.board_view.board() == null:
|
||||
if main.ui.battle.board_zone.board_view.board() == null:
|
||||
problems.append("L2 单场未启动(board=null)")
|
||||
else:
|
||||
# 地形变异:L2 板允许带变异(岩浆/烬覆)——布置域断言 =
|
||||
# 变异格全在初始展开区外(closed_only 结构保障)
|
||||
var b0 = main.board_view.board()
|
||||
var b0 = main.ui.battle.board_zone.board_view.board()
|
||||
var r0p := {}
|
||||
for c in b0.compute_r0():
|
||||
r0p[c] = true
|
||||
@@ -209,8 +209,8 @@ func _ready() -> void:
|
||||
if r0p.has(c):
|
||||
problems.append("L2 板变异格落在初始展开区(地形变异 布置域违例)")
|
||||
break
|
||||
main.board_view.set_desaturated(true)
|
||||
main.board_view.set_desaturated(false)
|
||||
main.ui.battle.board_zone.board_view.set_desaturated(true)
|
||||
main.ui.battle.board_zone.board_view.set_desaturated(false)
|
||||
# ---- GP 终局行:summary gp_final 键取数+胜利面板 GP 行呈现(C5:GP 行只上冒险终局面板;
|
||||
# 断言面 = summary 键,无影子账清零转存)----
|
||||
# (置于战斗态断言之后——合成收束会灰化站桩并清 settlement 引用,具污染性)
|
||||
@@ -223,17 +223,17 @@ func _ready() -> void:
|
||||
main._on_adventure_finished({"result": "completed", "cleared": true, "levels_cleared": 3,
|
||||
"stopped_at": 0, "keystone_total": 30, "master_seed": 42, "gp_final": 57})
|
||||
main.meta.save = meta_saved
|
||||
if not main._end_overlay.is_open():
|
||||
if not main.ui.modals.end_overlay.is_open():
|
||||
problems.append("冒险终局面板未开(合成摘要路径)")
|
||||
elif not main._end_overlay._gp_row.visible or not main._end_overlay._gp_val.text.contains("57"):
|
||||
problems.append("终局面板 GP 行缺失或取数不对(%s)" % main._end_overlay._gp_val.text)
|
||||
main._end_overlay.close()
|
||||
main._close_modal(main._end_overlay)
|
||||
elif not main.ui.modals.end_overlay._gp_row.visible or not main.ui.modals.end_overlay._gp_val.text.contains("57"):
|
||||
problems.append("终局面板 GP 行缺失或取数不对(%s)" % main.ui.modals.end_overlay._gp_val.text)
|
||||
main.ui.modals.end_overlay.close()
|
||||
main.ui.modals.flow.close(main.ui.modals.end_overlay)
|
||||
if problems.is_empty():
|
||||
print("[ui_probe] PASS — cells=%d board=%dx%d hp='%s' enemies=%d ap_dots=%d" % [
|
||||
main.board_view.cells_probe().size(), main.board_view.board().width, main.board_view.board().height,
|
||||
main.info_panel.attack_text(),
|
||||
main.stage.enemy_box.get_child_count(), main.ap_bar._dots.get_child_count()])
|
||||
main.ui.battle.board_zone.board_view.cells_probe().size(), main.ui.battle.board_zone.board_view.board().width, main.ui.battle.board_zone.board_view.board().height,
|
||||
main.ui.battle.info_panel.attack_text(),
|
||||
main.ui.battle.stage.enemy_box.get_child_count(), main.ui.battle.board_zone.ap_bar._dots.get_child_count()])
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for p in problems:
|
||||
|
||||
@@ -28,13 +28,13 @@ func _ready() -> void:
|
||||
# ---- 菜单簇(开始层可见态;逐屏开→采样→关,模态恢复走组合根真实路径)----
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_menu", true, false, true)
|
||||
_settle()
|
||||
await _audit("start", [main._start_overlay])
|
||||
await _audit("start", [main.ui.screens.start_overlay])
|
||||
_start_columns_contract(main)
|
||||
_modal_registry_contract(main)
|
||||
|
||||
main._open_settings()
|
||||
_settle()
|
||||
await _audit("settings", [main.settings_overlay])
|
||||
await _audit("settings", [main.ui.modals.settings_overlay])
|
||||
main._on_settings_closed()
|
||||
_settle()
|
||||
|
||||
@@ -42,7 +42,7 @@ func _ready() -> void:
|
||||
problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)")
|
||||
else:
|
||||
_settle()
|
||||
await _audit("camp", [main.camp_overlay])
|
||||
await _audit("camp", [main.ui.modals.camp_overlay])
|
||||
# 营地灰字源 token 扫描(迭代36 wc4 静态半——源内零裸 UiKit.STONE;判别式=
|
||||
# STONE 出现次数 == STONE_DEEP 出现次数,即每处 STONE 都带 _DEEP 后缀)
|
||||
var camp_src := FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd")
|
||||
@@ -59,15 +59,15 @@ func _ready() -> void:
|
||||
# 生涯页驱动=函数直调中间态(卡② mn9/mn10——careerbtn 已撤,卡④ 营地笔记本改挂前无 UI 路径)
|
||||
main._open_career()
|
||||
_settle()
|
||||
if not main.career_overlay.visible or main._start_overlay.visible:
|
||||
if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 生涯页未打开或开始层未隐藏(函数直调中间态断裂)")
|
||||
await _audit("career", [main.career_overlay])
|
||||
await _audit("career", [main.ui.modals.career_overlay])
|
||||
_career_readonly_contract(main)
|
||||
_career_zero_motion_contract(main)
|
||||
_career_nojump_contract(main)
|
||||
main._on_career_closed()
|
||||
_settle()
|
||||
if main.career_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 关闭后页未隐藏或开始层未恢复(模态栈单口失守)")
|
||||
|
||||
# ---- 选档界面(迭代 33 卡③):函数直调开层(「进入存档」钮归卡② mn3——点击链在卡② 承接),
|
||||
@@ -93,39 +93,39 @@ func _ready() -> void:
|
||||
problems.append("[savesel] 开层前激活态非零(sl9 重启链——boot/注入面后应无激活)")
|
||||
main._open_save_select()
|
||||
_settle()
|
||||
if not main.save_select_overlay.visible or main._start_overlay.visible:
|
||||
if not main.ui.modals.save_select_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[savesel] 选档层未打开或开始层未隐藏(开层函数断裂)")
|
||||
await _audit("savesel", [main.save_select_overlay])
|
||||
await _audit("savesel", [main.ui.modals.save_select_overlay])
|
||||
_save_select_contract(main, ssel_tpl)
|
||||
main._on_save_select_closed()
|
||||
_settle()
|
||||
if main.save_select_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("[savesel] 关闭后层未隐藏或开始层未恢复(模态栈单口失守)")
|
||||
|
||||
main.confirm_dialog.open("结构探针采样", "确认")
|
||||
main.ui.modals.confirm_dialog.open("结构探针采样", "确认")
|
||||
_settle()
|
||||
await _audit("confirm", [main.confirm_dialog])
|
||||
main.confirm_dialog.close()
|
||||
await _audit("confirm", [main.ui.modals.confirm_dialog])
|
||||
main.ui.modals.confirm_dialog.close()
|
||||
_settle()
|
||||
|
||||
main._show_debug_overlay()
|
||||
_settle()
|
||||
await _audit("debug", [main.debug_overlay])
|
||||
await _audit("debug", [main.ui.modals.debug_overlay])
|
||||
main._close_debug_overlay()
|
||||
_settle()
|
||||
|
||||
# ---- 冒险簇(真流推进:开冒险→地图选路态→选战斗节点→战斗 HUD)----
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_adv", true, false, true)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,结构采样确定性禁依赖默认分支)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,结构采样确定性禁依赖默认分支)
|
||||
main._start_adventure_from_menu()
|
||||
_settle(4)
|
||||
await _audit("level_map", [main.level_screen])
|
||||
await _audit("level_map", [main.ui.modals.level_screen])
|
||||
|
||||
var battle_id := _pick_battle_node(main)
|
||||
if battle_id >= 0:
|
||||
main.level_screen.route_chosen.emit(battle_id)
|
||||
main.ui.modals.level_screen.route_chosen.emit(battle_id)
|
||||
_settle(6)
|
||||
_audit("battle_hud", [main.board_zone, main.info_panel, main.stage])
|
||||
_audit("battle_hud", [main.ui.battle.board_zone, main.ui.battle.info_panel, main.ui.battle.stage])
|
||||
_standeefit_contract(main)
|
||||
# ---- 状态面板:组合根真实路径开层→结构断言→关闭。附只读性契约
|
||||
#(R4——子树信号连接白名单巡检)与零动效契约(R5/P4 甲——零 AnimationPlayer/零 tween)。
|
||||
@@ -133,74 +133,74 @@ func _ready() -> void:
|
||||
# 一律红,须先入册审查);白名单={视图自身 _emit_closed, 组合根 _on_status_closed}。
|
||||
main._open_status_panel()
|
||||
_settle()
|
||||
await _audit("status", [main.status_panel])
|
||||
await _audit("status", [main.ui.modals.status_panel])
|
||||
_status_readonly_contract(main)
|
||||
_status_zero_motion_contract(main)
|
||||
main._on_status_closed()
|
||||
_settle()
|
||||
if main.status_panel.visible:
|
||||
if main.ui.modals.status_panel.visible:
|
||||
problems.append("[status] 关闭后层未隐藏(模态栈单口失守)")
|
||||
|
||||
# ---- 图例层(旧模态、首过结构族):ap_bar legend_toggled
|
||||
# 信号真路径开(board_zone 信号→组合根 _on_legend_toggled→_open_modal)→结构断言族→
|
||||
# closed 信号真路径关(close()→组合根 _on_modal_closed)。
|
||||
main.board_zone.legend_toggled.emit()
|
||||
main.ui.battle.board_zone.legend_toggled.emit()
|
||||
_settle()
|
||||
if not main.legend_overlay.visible:
|
||||
if not main.ui.modals.legend_overlay.visible:
|
||||
problems.append("[legend] 图例层未打开(legend_toggled 路径中断)")
|
||||
await _audit("legend", [main.legend_overlay])
|
||||
main.legend_overlay.close()
|
||||
await _audit("legend", [main.ui.modals.legend_overlay])
|
||||
main.ui.modals.legend_overlay.close()
|
||||
_settle()
|
||||
if main.legend_overlay.visible:
|
||||
if main.ui.modals.legend_overlay.visible:
|
||||
problems.append("[legend] 关闭后层未隐藏(模态栈单口失守)")
|
||||
else:
|
||||
problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.level_screen._cand_ids))
|
||||
problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.ui.modals.level_screen._cand_ids))
|
||||
|
||||
# ---- 兑付屏(真实 open 面 + 合成最小数据——结构断言只看矩形/宽度/可见性)----
|
||||
main._end_overlay.open_victory("结构探针·胜利演算", "返回", Rect2(200, 200, 400, 300), {"gp_summary": "GP 0"})
|
||||
main.ui.modals.end_overlay.open_victory("结构探针·胜利演算", "返回", Rect2(200, 200, 400, 300), {"gp_summary": "GP 0"})
|
||||
_settle()
|
||||
await _audit("battle_end", [main._end_overlay])
|
||||
main._end_overlay.close()
|
||||
await _audit("battle_end", [main.ui.modals.end_overlay])
|
||||
main.ui.modals.end_overlay.close()
|
||||
_settle()
|
||||
|
||||
# ---- 节点结算层(旧模态、首过结构族):rest 兑付真路径开
|
||||
#(组合根 _open_modal 登记册+show_rest 合成喂入)→结构断言族→continue_pressed 信号真路径关
|
||||
#(press_continue→组合根 _on_node_continue→_close_modal)。
|
||||
main._open_modal(main.node_overlay)
|
||||
main.node_overlay.show_rest(3, 15, 20)
|
||||
main.ui.modals.flow.open(main.ui.modals.node_overlay)
|
||||
main.ui.modals.node_overlay.show_rest(3, 15, 20)
|
||||
_settle()
|
||||
if not main.node_overlay.visible:
|
||||
if not main.ui.modals.node_overlay.visible:
|
||||
problems.append("[node] 节点结算层未打开(rest 兑付路径中断)")
|
||||
await _audit("node_overlay", [main.node_overlay])
|
||||
main.node_overlay.press_continue()
|
||||
await _audit("node_overlay", [main.ui.modals.node_overlay])
|
||||
main.ui.modals.node_overlay.press_continue()
|
||||
_settle()
|
||||
if main.node_overlay.visible:
|
||||
if main.ui.modals.node_overlay.visible:
|
||||
problems.append("[node] 关闭后层未隐藏(continue_pressed 路径中断)")
|
||||
|
||||
main.curio_overlay.open_pick("结构探针·三选一", [
|
||||
main.ui.modals.curio_overlay.open_pick("结构探针·三选一", [
|
||||
{"id": "probe_heal", "text": "回血\n恢复 3 点"},
|
||||
{"id": "probe_magic", "text": "爆裂轰击\n下一击 +5"},
|
||||
{"id": "probe_pierce", "text": "穿甲\n无视护甲"},
|
||||
])
|
||||
_settle()
|
||||
await _audit("curio_pick", [main.curio_overlay])
|
||||
main.curio_overlay.visible = false
|
||||
await _audit("curio_pick", [main.ui.modals.curio_overlay])
|
||||
main.ui.modals.curio_overlay.visible = false
|
||||
_settle()
|
||||
|
||||
main.shop_overlay.open_shop([
|
||||
main.ui.modals.shop_overlay.open_shop([
|
||||
{"product_id": "probe_p1", "display_name": "探针商品甲", "attr": "atk", "flat_value": 1, "price": 10, "purchased": 0},
|
||||
{"product_id": "probe_p2", "display_name": "探针商品乙", "attr": "hp", "flat_value": 2, "price": 20, "purchased": 0},
|
||||
], 100)
|
||||
_settle()
|
||||
await _audit("shop", [main.shop_overlay])
|
||||
await _audit("shop", [main.ui.modals.shop_overlay])
|
||||
|
||||
# ---- 术语传导:货架卡与三选一卡片的玩家面文案(物品名=专长获取、分类=专长凭证)
|
||||
main.shop_overlay.open_shelf([
|
||||
main.ui.modals.shop_overlay.open_shelf([
|
||||
{"kind_id": "feat_ticket", "display_name": "专长获取", "value": 0, "price": 5},
|
||||
{"kind_id": "heal", "display_name": "应急回复", "value": 6, "price": 5},
|
||||
], 20)
|
||||
_settle()
|
||||
var ticket_row: Dictionary = main.shop_overlay._shelf_rows[0]
|
||||
var ticket_row: Dictionary = main.ui.modals.shop_overlay._shelf_rows[0]
|
||||
if String(main.db.run.shop.pool_row("feat_ticket").get("display_name", "")) != "专长获取":
|
||||
problems.append("[shop_term] shop.tres 专长凭证件名未换代(现=%s)" % String(main.db.run.shop.pool_row("feat_ticket").get("display_name", "")))
|
||||
if ticket_row.card.name_l.text != "专长获取":
|
||||
@@ -211,7 +211,7 @@ func _ready() -> void:
|
||||
problems.append("[shop_term] 专长凭证件描述未用规范流程句(现=%s)" % ticket_row.card.desc_l.text)
|
||||
if ticket_row.card.foot_l.text != "专长凭证":
|
||||
problems.append("[shop_term] 专长凭证件分类标注未用「专长凭证」(现=%s)" % ticket_row.card.foot_l.text)
|
||||
var heal_row: Dictionary = main.shop_overlay._shelf_rows[1]
|
||||
var heal_row: Dictionary = main.ui.modals.shop_overlay._shelf_rows[1]
|
||||
if heal_row.card.desc_l.text != "回复 +6(本冒险)":
|
||||
problems.append("[shop_term] 回复件描述漏了 heal 词表(现=%s)" % heal_row.card.desc_l.text)
|
||||
if heal_row.card.foot_l.text != "回复":
|
||||
@@ -223,7 +223,7 @@ func _ready() -> void:
|
||||
problems.append("[curio_term] 专长凭证三选一卡未用物品名/规范流程句(现=%s)" % ticket_text)
|
||||
if ticket_text.contains("门票") or ticket_text.contains("T5") or ticket_text.contains("持有集") or ticket_text.contains("奇物三选一"):
|
||||
problems.append("[curio_term] 专长凭证三选一卡残留退役词/内部任务号(现=%s)" % ticket_text)
|
||||
main.shop_overlay.visible = false
|
||||
main.ui.modals.shop_overlay.visible = false
|
||||
_settle()
|
||||
|
||||
# ---- ⑦resume 专项:续玩停点恢复(main.gd:717 报错回归闸)----
|
||||
@@ -234,11 +234,11 @@ func _ready() -> void:
|
||||
main.run_scope.runner = null # 全新启动等价态(真机=菜单直进续玩,无前置冒险——runner 从未赋值;探针此前推流会残留旧 runner)
|
||||
main._resume_from_menu()
|
||||
_settle(8)
|
||||
if not main._end_overlay.visible:
|
||||
if not main.ui.modals.end_overlay.visible:
|
||||
problems.append("[resume] 续玩恢复后胜利总结面未开(三选一停点分支中断)")
|
||||
else:
|
||||
var found := {"ok": false}
|
||||
_find_label_text(main._end_overlay, "合计 24", found)
|
||||
_find_label_text(main.ui.modals.end_overlay, "合计 24", found)
|
||||
if not found.ok:
|
||||
problems.append("[resume] 胜利总结面 GP 行未见快照合计「合计 24」(runner 缺位守卫缺位——717 回归)")
|
||||
var s: String = main._gp_summary_text({"gp_dropped": 0})
|
||||
@@ -280,7 +280,7 @@ func _find_label_text(node: Node, needle: String, found: Dictionary) -> void:
|
||||
func _pick_battle_node(main: Node) -> int:
|
||||
# 入口/兑付停点走 level_screen.open_choices 直传(不写 _pending_candidates)——
|
||||
# 候选从视图态取:_cand_ids × runner.nodes() 的 type(编号解析=node_id_of 单一出口)
|
||||
var ls: Node = main.level_screen
|
||||
var ls: Node = main.ui.modals.level_screen
|
||||
if ls._map._runner == null:
|
||||
return -1
|
||||
for nd in ls._map._runner.nodes():
|
||||
@@ -306,7 +306,7 @@ const _PAUSE_READONLY_SIGNALS: Array[String] = [
|
||||
|
||||
|
||||
func _status_readonly_contract(main: Node) -> void:
|
||||
var overlay: Control = main.status_panel
|
||||
var overlay: Control = main.ui.modals.status_panel
|
||||
var stack: Array = [overlay]
|
||||
var conn_count := 0
|
||||
while not stack.is_empty():
|
||||
@@ -332,7 +332,7 @@ func _status_readonly_contract(main: Node) -> void:
|
||||
# ⑨status 零动效契约(R5/P4 甲——零动效直开直关路径存在断言):
|
||||
# 子树零 AnimationPlayer + 视图源码零 tween/动效包装调用(源码 token 扫描=ui_contrast 静态段同手法)
|
||||
func _status_zero_motion_contract(main: Node) -> void:
|
||||
var overlay: Control = main.status_panel
|
||||
var overlay: Control = main.ui.modals.status_panel
|
||||
var stack: Array = [overlay]
|
||||
while not stack.is_empty():
|
||||
var n: Node = stack.pop_back()
|
||||
@@ -352,7 +352,7 @@ func _status_zero_motion_contract(main: Node) -> void:
|
||||
# 清单外零连接。白名单={视图自身 _emit_closed〔返回钮〕, 组合根 _on_career_closed};页内任何
|
||||
# 购买/导航/写入方向的新连接一律红,须先入册审查。
|
||||
func _career_readonly_contract(main: Node) -> void:
|
||||
var overlay: Control = main.career_overlay
|
||||
var overlay: Control = main.ui.modals.career_overlay
|
||||
var stack: Array = [overlay]
|
||||
var conn_count := 0
|
||||
while not stack.is_empty():
|
||||
@@ -377,7 +377,7 @@ func _career_readonly_contract(main: Node) -> void:
|
||||
|
||||
# ⑪career 零动效契约(状态面板 ⑨ 同形制——P4 甲直开直关)
|
||||
func _career_zero_motion_contract(main: Node) -> void:
|
||||
var overlay: Control = main.career_overlay
|
||||
var overlay: Control = main.ui.modals.career_overlay
|
||||
var stack: Array = [overlay]
|
||||
while not stack.is_empty():
|
||||
var n: Node = stack.pop_back()
|
||||
@@ -392,13 +392,13 @@ func _career_zero_motion_contract(main: Node) -> void:
|
||||
|
||||
|
||||
# ⑫模态登记/树序全量合同(V1——career 专属登记序契约推广到全部模态+屏壳;
|
||||
# 判据=main.LAYER_CONTRACT 单一事实源:登记册序==合同模态序、直接子层树序==合同序逐对相等。
|
||||
# 判据=main.ui.LAYER_CONTRACT 单一事实源:登记册序==合同模态序、直接子层树序==合同序逐对相等。
|
||||
# 两特例按合同数据表达:level_screen 宿住冒险屏壳(树序随宿主=全模态之下,遮挡语义归模态栈
|
||||
# screen_state 登记)、camp_overlay 成员与 camp_screen 屏壳同槽(屏壳树序代言成员登记位)。
|
||||
# _career_registry_contract 的断言(career=状态面板后/确认弹窗前、树序同步)由本全量合同覆盖)
|
||||
# ⑭选档界面契约(卡③ sl1/sl2/sl3/sl4/sl9——呈现面全部来自喂入数据,视图零自判)
|
||||
func _save_select_contract(main: Node, tpl: String) -> void:
|
||||
var sso: Node = main.save_select_overlay
|
||||
var sso: Node = main.ui.modals.save_select_overlay
|
||||
# sl1 三槽呈现:槽卡计数==3+零滚动容器(禁滚动长列表形态——契约 1)
|
||||
var rows: VBoxContainer = sso._rows_box
|
||||
var live_rows := 0
|
||||
@@ -477,13 +477,13 @@ func _collect_labels(node: Node, out: Array[String]) -> void:
|
||||
|
||||
|
||||
func _modal_registry_contract(main: Node) -> void:
|
||||
var list: Array = main._modals._list
|
||||
var list: Array = main.ui.modals.flow.stack._list
|
||||
# ① 登记册序==合同模态序(逐位点名——合同是登记序唯一事实源)
|
||||
var contract_modals: Array = []
|
||||
for entry: Dictionary in main.LAYER_CONTRACT:
|
||||
for entry: Dictionary in main.ui.LAYER_CONTRACT:
|
||||
if String(entry["kind"]) != "modal":
|
||||
continue
|
||||
var n: Node = main._layer_node(String(entry["key"]))
|
||||
var n: Node = main.ui.node(String(entry["key"]))
|
||||
if n == null:
|
||||
problems.append("[registry] 合同层缺失: %s〔装配生命批 V1〕" % String(entry["key"]))
|
||||
continue
|
||||
@@ -498,10 +498,10 @@ func _modal_registry_contract(main: Node) -> void:
|
||||
i, list[i].name, contract_modals[i].name])
|
||||
# ② 直接子层树序==合同序逐对相等(屏壳/模态/全屏态/非视觉节点全链;宿住层随宿主不参与)
|
||||
var prev: Node = null
|
||||
for entry: Dictionary in main.LAYER_CONTRACT:
|
||||
for entry: Dictionary in main.ui.LAYER_CONTRACT:
|
||||
if entry.has("host"):
|
||||
continue
|
||||
var node: Node = main._layer_node(String(entry["shell"]) if entry.has("shell") else String(entry["key"]))
|
||||
var node: Node = main.ui.node(String(entry["shell"]) if entry.has("shell") else String(entry["key"]))
|
||||
if node == null:
|
||||
problems.append("[registry] 合同层缺失: %s〔装配生命批 V1〕" % String(entry["key"]))
|
||||
continue
|
||||
@@ -510,23 +510,23 @@ func _modal_registry_contract(main: Node) -> void:
|
||||
prev.name, node.name, node.get_index(), prev.get_index()])
|
||||
prev = node
|
||||
# ③ 宿住层特例:level_screen 宿主=冒险屏壳且在全直接子模态之下(遮挡语义归模态栈 screen_state)
|
||||
var lv: Node = main.level_screen
|
||||
if lv.get_parent() != main._adventure_screen:
|
||||
var lv: Node = main.ui.modals.level_screen
|
||||
if lv.get_parent() != main.ui.screens.adventure_screen:
|
||||
problems.append("[registry] level_screen 宿主应=冒险屏壳(实测 %s)〔装配生命批 V1〕" % lv.get_parent().name)
|
||||
elif lv.get_parent().get_index() >= main.legend_overlay.get_index():
|
||||
elif lv.get_parent().get_index() >= main.ui.modals.legend_overlay.get_index():
|
||||
problems.append("[registry] 冒险屏壳应在全模态之下(宿主 idx=%d legend idx=%d)〔装配生命批 V1〕" % [
|
||||
lv.get_parent().get_index(), main.legend_overlay.get_index()])
|
||||
lv.get_parent().get_index(), main.ui.modals.legend_overlay.get_index()])
|
||||
# ④ 营地同槽特例:camp_screen 屏壳树序夹商店层与设置层之间(成员 camp_overlay 登记位代言)
|
||||
var cs: Node = main._camp_screen
|
||||
if cs.get_index() <= main.shop_overlay.get_index() or cs.get_index() >= main.settings_overlay.get_index():
|
||||
var cs: Node = main.ui.screens.camp_screen
|
||||
if cs.get_index() <= main.ui.modals.shop_overlay.get_index() or cs.get_index() >= main.ui.modals.settings_overlay.get_index():
|
||||
problems.append("[registry] camp_screen 屏壳树序应夹商店与设置层之间(idx=%d shop=%d settings=%d)〔装配生命批 V1〕" % [
|
||||
cs.get_index(), main.shop_overlay.get_index(), main.settings_overlay.get_index()])
|
||||
cs.get_index(), main.ui.modals.shop_overlay.get_index(), main.ui.modals.settings_overlay.get_index()])
|
||||
|
||||
|
||||
# ⑬career 零跳转契约(提纲 P4 甲——页内零「前往他处」入口:除「返回」外任何导航钮=红)
|
||||
func _career_nojump_contract(main: Node) -> void:
|
||||
for nav in ["营地", "商店", "设置", "冒险", "继续上局", "退出", "前往"]:
|
||||
if ProbeUiUtil.find_button(main.career_overlay, nav, "contains") != null:
|
||||
if ProbeUiUtil.find_button(main.ui.modals.career_overlay, nav, "contains") != null:
|
||||
problems.append("[career][nojump] 页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav)
|
||||
|
||||
|
||||
@@ -534,7 +534,7 @@ func _career_nojump_contract(main: Node) -> void:
|
||||
# (名行基线离盒底 1px 同型高危写法返修回归闸;
|
||||
# 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘)
|
||||
func _standeefit_contract(main: Node) -> void:
|
||||
var st: Node = main.stage
|
||||
var st: Node = main.ui.battle.stage
|
||||
if st == null or st.hero_standee == null:
|
||||
problems.append("[standee] 舞台/主角站桩缺失(断言中断)")
|
||||
return
|
||||
@@ -711,8 +711,8 @@ func _dump(c: Control, depth: int) -> void:
|
||||
|
||||
# ⑤start 专项:左列/右列矩形不相交(M2 双列契约——装配件命名 start_box/start_right 为探针锚点)
|
||||
func _start_columns_contract(main: Node) -> void:
|
||||
var box: Control = main._start_overlay.find_child("start_box", true, false)
|
||||
var right: Control = main._start_overlay.find_child("start_right", true, false)
|
||||
var box: Control = main.ui.screens.start_overlay.find_child("start_box", true, false)
|
||||
var right: Control = main.ui.screens.start_overlay.find_child("start_right", true, false)
|
||||
if box == null or right == null:
|
||||
problems.append("[start][columns] 左列/右列节点缺失(start_box/start_right——装配命名失守)")
|
||||
return
|
||||
|
||||
+41
-39
@@ -43,35 +43,35 @@ func _ready() -> void:
|
||||
# ① 全局浮层角标开窗(调试入口=UI 最顶层浮层,任何 UI 不遮挡——
|
||||
# main 直接子层、LAYER_CONTRACT 末位 float 类;boot 菜单优先不开浮窗,角标真实点击路径开窗;
|
||||
# 悬浮于菜单之上——开始层不隐藏;树序新基准=登记册末位(确认弹窗之后=绘制序最上))
|
||||
main.debug_badge.pressed.emit()
|
||||
main.ui.screens.debug_badge.pressed.emit()
|
||||
await get_tree().process_frame
|
||||
if not main.debug_overlay.visible:
|
||||
if not main.ui.modals.debug_overlay.visible:
|
||||
problems.append("调试浮层角标点击后浮窗未开(全局浮层入口失守)")
|
||||
if not main._start_overlay.visible:
|
||||
if not main.ui.screens.start_overlay.visible:
|
||||
problems.append("开始覆盖层被隐藏——浮窗应悬浮于菜单之上(14.5 形态失守)")
|
||||
if main.debug_overlay.get_index() < main.confirm_dialog.get_index():
|
||||
if main.ui.modals.debug_overlay.get_index() < main.ui.modals.confirm_dialog.get_index():
|
||||
problems.append("调试浮窗树序不在确认弹窗之后——登记册末位失守")
|
||||
# ② 滑条 → 生效预览联动
|
||||
main.debug_overlay.set_sliders(100, 50)
|
||||
if str(boosted_atk) not in main.debug_overlay.effect_text() or str(boosted_hp) not in main.debug_overlay.effect_text():
|
||||
problems.append("生效预览未联动(%s)" % main.debug_overlay.effect_text())
|
||||
main.ui.modals.debug_overlay.set_sliders(100, 50)
|
||||
if str(boosted_atk) not in main.ui.modals.debug_overlay.effect_text() or str(boosted_hp) not in main.ui.modals.debug_overlay.effect_text():
|
||||
problems.append("生效预览未联动(%s)" % main.ui.modals.debug_overlay.effect_text())
|
||||
# ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照
|
||||
main.debug_overlay.press_apply_for_probe()
|
||||
main.ui.modals.debug_overlay.press_apply_for_probe()
|
||||
# ④ 重置(重开浮窗 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线)
|
||||
main._show_debug_overlay()
|
||||
main.debug_overlay.press_reset_for_probe()
|
||||
main.ui.modals.debug_overlay.press_reset_for_probe()
|
||||
# ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位;
|
||||
# 菜单态上下文未锁定=按钮可点,is_running 语义=模拟在跑〔18卡⑦ 与对局禁用态拆分〕)
|
||||
main.debug_overlay.set_sliders(100, 0)
|
||||
main.debug_overlay.set_sim_count(10)
|
||||
main.debug_overlay.press_simulate_for_probe()
|
||||
main.ui.modals.debug_overlay.set_sliders(100, 0)
|
||||
main.ui.modals.debug_overlay.set_sim_count(10)
|
||||
main.ui.modals.debug_overlay.press_simulate_for_probe()
|
||||
var guard := SIM_GUARD_FRAMES
|
||||
while main.debug_overlay.is_running() and guard > 0:
|
||||
while main.ui.modals.debug_overlay.is_running() and guard > 0:
|
||||
guard -= 1
|
||||
await get_tree().process_frame
|
||||
if main.debug_overlay.is_running():
|
||||
if main.ui.modals.debug_overlay.is_running():
|
||||
problems.append("模拟未在预算帧内完成(疑似挂死)")
|
||||
var out: String = main.debug_overlay.output_text()
|
||||
var out: String = main.ui.modals.debug_overlay.output_text()
|
||||
if not out.contains("关卡完成率"):
|
||||
problems.append("模拟输出缺关卡完成率")
|
||||
# 口径换代 V7:胜局统计行措辞钉子(atk+100% 加成档 10 局必有胜局——hp_left_win 非空路径恒出)
|
||||
@@ -83,7 +83,7 @@ func _ready() -> void:
|
||||
problems.append("模拟输出 N 标注错误(%s)" % out.substr(0, 40))
|
||||
# ⑥ 关闭 → 浮窗收起、开始层全程在屏
|
||||
main._close_debug_overlay()
|
||||
if main.debug_overlay.visible or not main._start_overlay.visible:
|
||||
if main.ui.modals.debug_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
problems.append("关闭后层级状态错误")
|
||||
# ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级)
|
||||
if int(main.db.combat.hero.atk) != base_atk or int(main.db.combat.hero.max_hp) != base_hp:
|
||||
@@ -93,20 +93,20 @@ func _ready() -> void:
|
||||
# 存档隔离(停点断点写盘随 main.save_manager;探针禁碰真实档,同族探针隔离纪律;
|
||||
# isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退)
|
||||
ProbeUiUtil.isolate_save(main, "smoke_tune_probe_save", true, false, true) # 末参=swap_save(卡②签名换代——断点写盘经 autoload 落隔离档)
|
||||
main.debug_overlay.set_sliders(100, 50)
|
||||
main.debug_overlay.press_apply_for_probe()
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(18卡⑦审计🔴——空框=非首局随机,显式优先经真实路径断言 master_seed)
|
||||
main.ui.modals.debug_overlay.set_sliders(100, 50)
|
||||
main.ui.modals.debug_overlay.press_apply_for_probe()
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(18卡⑦审计🔴——空框=非首局随机,显式优先经真实路径断言 master_seed)
|
||||
main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
# ⑧b 开冒险路径显式关浮窗(18卡⑦ T2 防护——调试会话默认开态下开局不带窗,对局中经角标再开)
|
||||
if main.debug_overlay.visible:
|
||||
if main.ui.modals.debug_overlay.visible:
|
||||
problems.append("开冒险路径未显式关浮窗(T2 防护失守)")
|
||||
if int(main.run_scope.adventure.master_seed) != 42:
|
||||
problems.append("显式种子未达冒险(master_seed=%s ≠ 42——真实路径显式优先失守)" % str(main.run_scope.adventure.master_seed))
|
||||
# 关卡开始 = 入口选路停点——真实按钮路径选入口候选进首战再读呈现快照
|
||||
if main.level_screen.visible:
|
||||
main.level_screen.press_candidate(0)
|
||||
if main.ui.modals.level_screen.visible:
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var hero: Dictionary = main.run_scope.settlement.presentation_snapshot().hero
|
||||
@@ -116,23 +116,23 @@ func _ready() -> void:
|
||||
# ⑨ 对局中可开(18卡⑦ P2 甲/T2 防护——S1+ 停机点回归闸):战斗屏角标真实信号路径开窗;
|
||||
# 棋盘输入不锁、状态角标不置灰(「默认开×遮挡复活」下必锁死的破约回归面);
|
||||
# 对局中批量模拟/两层调试两钮禁用;当局数据六项与只读访问器逐项一致;关闭钮真实路径收窗恢复
|
||||
var dbg_badge: Button = main.debug_badge
|
||||
var dbg_badge: Button = main.ui.screens.debug_badge
|
||||
if dbg_badge == null or dbg_badge.visible:
|
||||
problems.append("对局中调试浮层角标非恒隐(发布态语义失守——卡⑦ 删写死行批)")
|
||||
else:
|
||||
main._show_debug_overlay() # 对局中开浮窗回归经直调承载(:61 既有直调先例——发布态角标恒隐,按钮信号路径退役)
|
||||
if not main.debug_overlay.visible:
|
||||
if not main.ui.modals.debug_overlay.visible:
|
||||
problems.append("对局中角标开浮窗失败(P2 甲对局中可达失守)")
|
||||
if main._input_locked():
|
||||
if main.ui.modals.flow.locks_input():
|
||||
problems.append("对局中开浮窗后棋盘输入锁死(T2 防护失守——S1+ 停机点)")
|
||||
if main.board_zone.status_badge.disabled:
|
||||
if main.ui.battle.board_zone.status_badge.disabled:
|
||||
problems.append("对局中开浮窗后状态角标置灰(T2 防护失守——S1+ 停机点)")
|
||||
if main.debug_overlay.is_running():
|
||||
if main.ui.modals.debug_overlay.is_running():
|
||||
problems.append("对局中上下文锁定误判为模拟在跑(is_running 语义互扰)")
|
||||
if not main.debug_overlay._sim_btn.disabled or not main.debug_overlay._dbg_btn.disabled:
|
||||
if not main.ui.modals.debug_overlay._sim_btn.disabled or not main.ui.modals.debug_overlay._dbg_btn.disabled:
|
||||
problems.append("对局中批量模拟/两层调试按钮未禁用(T2 并存态不开放失守)")
|
||||
# 当局数据六项值对(T3——数值与只读访问器逐项一致;种子=adventure.master_seed 现值非 _seed_value 重算)
|
||||
var rd: Dictionary = main.debug_overlay.run_data_probe()
|
||||
var rd: Dictionary = main.ui.modals.debug_overlay.run_data_probe()
|
||||
var r = main.run_scope.adventure.runner()
|
||||
if String(rd.get("seed", "")) != "42":
|
||||
problems.append("当局数据种子 ≠ master_seed 现值(%s)" % str(rd.get("seed")))
|
||||
@@ -146,27 +146,27 @@ func _ready() -> void:
|
||||
problems.append("当局数据 GP 失对(%s ≠ %s)" % [str(rd.get("gp")), str(r.gp)])
|
||||
if int(rd.get("keystone", -1)) != main.run_scope.adventure.journal().keystone_gained():
|
||||
problems.append("当局数据钥石失对(%s ≠ %s)" % [str(rd.get("keystone")), str(main.run_scope.adventure.journal().keystone_gained())])
|
||||
var close_btn: Button = ProbeUiUtil.find_button(main.debug_overlay, "关闭", "prefix")
|
||||
var close_btn: Button = ProbeUiUtil.find_button(main.ui.modals.debug_overlay, "关闭", "prefix")
|
||||
if close_btn == null:
|
||||
problems.append("浮窗关闭钮缺失(真实收窗路径中断)")
|
||||
else:
|
||||
close_btn.pressed.emit()
|
||||
if main.debug_overlay.visible or main._input_locked():
|
||||
if main.ui.modals.debug_overlay.visible or main.ui.modals.flow.locks_input():
|
||||
problems.append("对局中关闭浮窗后态失守(visible=%s locked=%s)"
|
||||
% [str(main.debug_overlay.visible), str(main._input_locked())])
|
||||
% [str(main.ui.modals.debug_overlay.visible), str(main.ui.modals.flow.locks_input())])
|
||||
# ⑨b 只读巡检(17卡③形制)+零动效契约(17卡③ R5——零 AnimationPlayer+源码零 tween)
|
||||
_overlay_readonly_contract(main, problems)
|
||||
_overlay_zero_motion_contract(main, problems)
|
||||
# ⑩ 种子三分单点断言(18卡⑦ C1——判定只落 _seed_value() 单点;对局态不影响本判定面):
|
||||
# 显式合法整数全场景优先(含首局)→ 首局(存档布尔 false)恒 42 → 非首局空框引擎熵连抽不全等 42
|
||||
main.debug_overlay.seed_edit.text = "777"
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "777"
|
||||
if main._seed_value() != 777:
|
||||
problems.append("显式种子输入未优先(_seed_value != 777——C1 ①失守)")
|
||||
# SaveManager 是 autoload 单例——换注走原地 attach(首局态挂载+恢复隔离档);
|
||||
# --script 模式无裸 autoload 标识符——经 main 节点路径解析
|
||||
var sm = main.get_node("/root/SaveManager")
|
||||
sm.slots.attach_path("user://smoke_tune_probe_seed.json") # 全新空档——first_adventure_done=false(首局态;新档缺省面)
|
||||
main.debug_overlay.seed_edit.text = ""
|
||||
main.ui.modals.debug_overlay.seed_edit.text = ""
|
||||
if main._seed_value() != 42:
|
||||
problems.append("首局空框种子 != 42(P5 甲失守——教学态可复现破约)")
|
||||
sm.slots.attach_path("user://smoke_tune_probe_save.json") # 恢复隔离档(非首局态——isolate_save 已建档置位)
|
||||
@@ -181,7 +181,7 @@ func _ready() -> void:
|
||||
all42 = false
|
||||
if all42:
|
||||
problems.append("非首局空框连抽 4 次全等 42(随机化失守——C1 ③)")
|
||||
main.debug_overlay.seed_edit.text = "42" # 钉回显式值(现场整洁)
|
||||
main.ui.modals.debug_overlay.seed_edit.text = "42" # 钉回显式值(现场整洁)
|
||||
for f0 in ["user://smoke_tune_probe_seed.json", "user://smoke_tune_probe_seed.json.tmp"]:
|
||||
if FileAccess.file_exists(f0):
|
||||
DirAccess.remove_absolute(f0)
|
||||
@@ -196,10 +196,11 @@ func _ready() -> void:
|
||||
|
||||
|
||||
# 只读巡检(18卡⑦ T3——17卡③「白名单外零连接」形制沿用):浮窗子树全控件 × 游戏语义信号名清单
|
||||
# 全连接巡检——白名单={视图自身八方法 + 组合根 _on_modal_closed(closed 外发消费)};
|
||||
# 全连接巡检——白名单={视图自身八方法 + 注册面模态编排口 ui.modals.flow.close(closed 外发消费——
|
||||
# 注册面批把该连接目标从组合根转发壳改为 flow 真方法,机检白名单随批登记同一连接)};
|
||||
# 当局数据区块=纯标签零连接在此被一并约束(新增任何连接=红须入册审查)。
|
||||
func _overlay_readonly_contract(main: Node, problems: Array) -> void:
|
||||
var overlay: Control = main.debug_overlay
|
||||
var overlay: Control = main.ui.modals.debug_overlay
|
||||
var stack: Array = [overlay]
|
||||
var conn_count := 0
|
||||
while not stack.is_empty():
|
||||
@@ -210,8 +211,9 @@ func _overlay_readonly_contract(main: Node, problems: Array) -> void:
|
||||
var cb: Callable = conn["callable"]
|
||||
var host: Object = cb.get_object()
|
||||
var m := String(cb.get_method())
|
||||
var flow_host: Object = main.ui.modals.flow # 注册面模态编排口(closed 外发消费白名单宿主)
|
||||
var ok := (host == overlay and m in _OVERLAY_VIEW_METHODS) \
|
||||
or (host == main and m == "_on_modal_closed") \
|
||||
or (host == flow_host and m == "close") \
|
||||
or (host is VScrollBar or host is HScrollBar) # 引擎滚动机构内部 drag 接线(RichTextLabel 自动滚动条——17卡③布局生命线同类豁免,零游戏写通路)
|
||||
if not ok:
|
||||
problems.append("[readonly] 非法信号连接 %s.%s → %s.%s(白名单外零连接巡检——18卡⑦ T3)"
|
||||
@@ -224,7 +226,7 @@ func _overlay_readonly_contract(main: Node, problems: Array) -> void:
|
||||
|
||||
# 零动效契约(17卡③ R5 形制——子树零 AnimationPlayer + 视图源码零 tween/动效包装调用)
|
||||
func _overlay_zero_motion_contract(main: Node, problems: Array) -> void:
|
||||
var overlay: Control = main.debug_overlay
|
||||
var overlay: Control = main.ui.modals.debug_overlay
|
||||
var stack: Array = [overlay]
|
||||
while not stack.is_empty():
|
||||
var n: Node = stack.pop_back()
|
||||
|
||||
@@ -13,9 +13,13 @@
|
||||
"exit": "1",
|
||||
"fp": "f9865f1a5a44"
|
||||
},
|
||||
"03 场景探针": {
|
||||
"exit": "0",
|
||||
"fp": "green"
|
||||
"03 探针 --click": {
|
||||
"exit": "1",
|
||||
"fp": "8a3cae0d0446"
|
||||
},
|
||||
"04 探针 --full": {
|
||||
"exit": "1",
|
||||
"fp": "591b27250b36"
|
||||
},
|
||||
"05 渲染闸": {
|
||||
"exit": "1",
|
||||
|
||||
@@ -33,7 +33,7 @@ tests/
|
||||
| **单元测试** | 领域类的原子行为对不对(公式/状态机/纯逻辑)——从对外方法进、断言钉对外可观测面(写法判据见 test-conventions §8.2) | `tests/unit/` | `run_all.gd` manifest(闸 02) |
|
||||
| **集成测试** | 跨系统的闭环对不对 | `tests/integration/` | 同上(与单元测试同一入口、同一协议) |
|
||||
| **系统测试** | 启动真实工程走真实玩法路径 | 过渡期 `game/probe/smoke_all.gd`(断言迁移批后=`tests/system_test.gd`) | `run_system_test.py`(闸 01) |
|
||||
| **UI 探针+渲染 golden** | 跑起来之后 UI 与场景切换对不对;画面基线 | `game/probe/*.tscn` 探针 | `probe_gate.sh`(闸 03)、`render_gate.sh`(闸 05) |
|
||||
| **UI 探针+渲染 golden** | 跑起来之后 UI 与场景切换对不对;画面基线 | `game/probe/*.tscn` 探针 | `probe_gate.sh`(闸 03/04)、`render_gate.sh`(闸 05) |
|
||||
|
||||
> **integration/ 已建承载**(迭代 29 T3——此前只有章程句无实体,空壳曾于迭代 19 卡① T4 删过一次)。
|
||||
> 首个集成用例 = `save_rng_roundtrip_test.gd`(存档⇄RNG 快照往返)。章程登记的四类集成用例里,
|
||||
@@ -194,7 +194,7 @@ tests/
|
||||
| `tools/run_system_test.py`(系统测试) | 真实玩法路径的行为断言面(推理→战斗→结算→进度推进) | 退出码+脚本错误扫描 | 玩法链路改动的调试期确认 |
|
||||
| `tests/run_all.gd`(单元与集成) | 公式/状态机/契约/跨系统闭环(65 套件 manifest) | 退出码+三硬闸(见下);每套件子进程带截止时间守护——超时先 taskkill /T 整树回收再判该套件 FAIL、批继续(默认 120s,环境变量 `RUN_ALL_SUITE_TIMEOUT_SEC` 可覆盖);连续 3 个套件超时=红树整批挂死,第 3 个即中止整轮、汇总已见失败与超时清单后非 0 退出(阈值=可调常量,不连续的超时照旧逐个回收) | 新套件单跑调试;逻辑改动的最小单元复跑;开发期选跑——追加 `-- --only=<词[,词…]>` 只跑路径命中词的套件(零命中=用法错 exit 2;此形态仅供开发期最窄确认,不产闸回执、不作验收凭据);闸机关联档选跑——同一过滤机制加 `-- --only-source=gates`,由 `tools/run_all_gates.sh` 在 02 闸传入(回执 scope=related,是验收跑批本体) |
|
||||
| `tools/probe_gate.sh`(场景探针,`--click`/`--full` 两模式) | UI 可达性、可见性、状态——真实实例化场景+真实输入路径 | 探针自检+退出码 | 新增 UI 区域的配套探针直跑与断言复核 |
|
||||
| `tools/render_gate.sh`(渲染 golden) | 画面基线——截图与基线图逐像素容差比对 | golden 比对脚本(依赖 PIL)+退出码/取证双检/三段探针日志脚本错误扫描(`error_scan.py`) | 主题/素材/排版类改动;窗口化运行,需开显示器 |
|
||||
| `tools/render_gate.sh`(渲染 golden) | 画面基线——截图与基线图逐像素容差比对 | golden 比对脚本(依赖 PIL) | 主题/素材/排版类改动;窗口化运行,需开显示器 |
|
||||
| `game/tests/static_scan.sh`(S 域) | 项目纪律+第三方质量规则:RNG 单点/core 纯度/类型标注/文件头/注释禁词/旧符号残留;S13=gdstyle 质量规则(重复 load、循环内 await、循环内分配、不可达代码等真缺陷类;基线键=文件|规则|函数名(类/文件级=文件|规则,无数字条目带消息),数字漂移不假红,同键指标恶化超基线登记值即红) | 退出码(逐规则 FAIL 行) | 改产品码前后的快速自检;新增纪律先补扫描规则再修码 |
|
||||
| 测量件(`game/sim/`,逐件见下「游离脚本台账」) | 期望/分布读数——只给读数,不判对错 | 人工读 stdout 或配套工具脚本 | 调参与归因;MC 类一律用户发起(档位表见 `sim-data/README.md`「MC 采集档位表」) |
|
||||
|
||||
|
||||
@@ -67,9 +67,9 @@ func _check_main_scene(main: Node) -> void:
|
||||
return
|
||||
var run_scope: Node = main.get("run_scope")
|
||||
check(run_scope != null and run_scope.is_inside_tree(), "闲时会话 run_scope 在树")
|
||||
var start_overlay: Control = main.get("_start_overlay")
|
||||
var start_overlay: Control = main.get("ui").screens.start_overlay
|
||||
check(start_overlay != null and start_overlay.visible, "启动态菜单可见(boot 后玩家见菜单)")
|
||||
check(main.get("settings_store") != null, "系统设置存储已装配(settings_store)")
|
||||
check(main.get("debug_badge") != null, "调试入口角标已装配(debug_badge)")
|
||||
check(main.get("legend_overlay") != null, "图例覆盖层已装配(legend_overlay)")
|
||||
check(main.get("status_panel") != null, "局内状态面板已装配(status_panel)")
|
||||
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)")
|
||||
+41
-41
@@ -81,20 +81,20 @@ func _smoke_scene_split() -> void:
|
||||
# V1a 启动态顶层断言:boot 后自树顶向下第一个可见内容层=start_overlay(headless 可判)
|
||||
#(内容层遍历——屏壳是容器不当候选:camp 壳 boot 态 vis=true 但成员恒隐,裸树遍历会误命中)
|
||||
var top: Node = _topmost_visible_layer(main)
|
||||
check(top == main._start_overlay,
|
||||
check(top == main.ui.screens.start_overlay,
|
||||
"V1a 启动态顶层可见层=start_overlay(实测顶层=%s——菜单被冒险屏盖住即红)〔装配生命批 V1〕"
|
||||
% (top.name if top != null else "null"))
|
||||
# V1b 语义关系:start_overlay 在冒险屏内容之上(菜单屏壳在冒险屏壳之上——装配序修复判据)
|
||||
check(main._menu_screen.get_index() > main._adventure_screen.get_index(),
|
||||
check(main.ui.screens.menu_screen.get_index() > main.ui.screens.adventure_screen.get_index(),
|
||||
"V1b 菜单屏壳在冒险屏壳之上(start_overlay 盖住战斗内容)〔装配生命批 V1〕")
|
||||
# V1c 合同树序链逐对保序(直接子层全链=屏壳/模态/全屏态/非视觉节点;宿住层树序随宿主不参与)
|
||||
var chain_detail := ""
|
||||
var chain_prev: Node = null
|
||||
var chain_ok := true
|
||||
for entry: Dictionary in main.LAYER_CONTRACT:
|
||||
for entry: Dictionary in main.ui.LAYER_CONTRACT:
|
||||
if entry.has("host"):
|
||||
continue
|
||||
var node: Node = main._layer_node(String(entry["shell"]) if entry.has("shell") else String(entry["key"]))
|
||||
var node: Node = main.ui.node(String(entry["shell"]) if entry.has("shell") else String(entry["key"]))
|
||||
if node == null:
|
||||
chain_ok = false
|
||||
chain_detail += " 缺失:%s" % String(entry["key"])
|
||||
@@ -105,7 +105,7 @@ func _smoke_scene_split() -> void:
|
||||
chain_prev = node
|
||||
check(chain_ok, "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕" % chain_detail)
|
||||
# ③ 探针挂载假设句柄在座(main.X 一级路径——探针迁移面收口证明)
|
||||
check(main._start_overlay == main._menu_screen.start_overlay and main.camp_overlay == main._camp_screen.camp_overlay and main.level_screen == main._adventure_screen.level_screen and main._battle_root == main._adventure_screen.battle_root and main.board_view == main._adventure_screen.board_view,
|
||||
check(main.ui.screens.start_overlay == main.ui.screens.menu_screen.start_overlay and main.ui.modals.camp_overlay == main.ui.screens.camp_screen.camp_overlay 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,
|
||||
"V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)")
|
||||
check(main.run_scope != null and not main.run_scope.is_adventure,
|
||||
"V2 闲时会话自 _ready 即在(永非 null;迭代 27 T3——gp 影子账退役无账本成员)")
|
||||
@@ -116,10 +116,10 @@ func _smoke_scene_split() -> void:
|
||||
# 顶层可见内容层(V1a 用——候选=合同模态/全屏态+两屏壳的 boot 内容成员;屏壳本身是容器不当候选:
|
||||
# camp 壳 boot 态 vis=true 但成员恒隐,裸树遍历自顶向下会先命中透明壳)
|
||||
func _topmost_visible_layer(main: Node) -> Node:
|
||||
var candidates: Array[Node] = [main._start_overlay, main._battle_root, main._inter_overlay,
|
||||
main.legend_overlay, main.node_overlay, main.level_screen, main.curio_overlay,
|
||||
main._end_overlay, main.shop_overlay, main.camp_overlay, main.settings_overlay,
|
||||
main.status_panel, main.career_overlay, main.confirm_dialog, main.debug_overlay]
|
||||
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_overlay, 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]
|
||||
var best: Node = null
|
||||
var best_x := -1
|
||||
var best_y := -1
|
||||
@@ -167,7 +167,7 @@ func _smoke_boot_chain() -> void:
|
||||
"V2b 设置已应用·减动效(UiKit.motion_scale=%s=settings_store.reduced_motion=%s)〔装配生命批 V2〕" % [
|
||||
str(ui_kit.motion_scale), str(ss.reduced_motion)])
|
||||
# ③ 启动态菜单可见
|
||||
check(main._start_overlay.visible,
|
||||
check(main.ui.screens.start_overlay.visible,
|
||||
"V2c 启动态 _start_overlay.visible=true(boot 后玩家见菜单)〔装配生命批 V2〕")
|
||||
# ④ 续玩钮可见态==SaveManager 断点在档态
|
||||
var sm = main.get_node("/root/SaveManager")
|
||||
@@ -186,22 +186,22 @@ func _smoke_boot_chain() -> void:
|
||||
if FileAccess.file_exists(p):
|
||||
DirAccess.remove_absolute(p)
|
||||
sm.slots.attach_path("user://smoke_boot_chain.json")
|
||||
check(main._menu_screen.entersavebtn != null and main._menu_screen.settingsbtn != null
|
||||
and main._menu_screen.quitbtn != null,
|
||||
check(main.ui.screens.menu_screen.entersavebtn != null and main.ui.screens.menu_screen.settingsbtn != null
|
||||
and main.ui.screens.menu_screen.quitbtn != null,
|
||||
"V2d 三钮在位·进入存档/设置/退出游戏(续玩钮族退役后主菜单操作组承接)〔卡② mn1/mn8〕")
|
||||
for old_btn in ["lvbtn", "resumebtn", "campbtn", "careerbtn"]:
|
||||
check(not (old_btn in main._menu_screen),
|
||||
check(not (old_btn in main.ui.screens.menu_screen),
|
||||
"V2d 四旧钮句柄零命中·%s(彻底撤出——禁隐藏态禁置灰态残留)〔卡② mn2〕" % old_btn)
|
||||
check(_count_visible_buttons(main._start_overlay) == 3,
|
||||
check(_count_visible_buttons(main.ui.screens.start_overlay) == 3,
|
||||
"V2d 渲染面可见按钮计数==3(旧钮接回即红——渲染面锚,句柄缝盲区闭合)〔卡② mn1/mn2〕")
|
||||
# ⑤ 闲时会话(_ready 即建、永非 null、在树)
|
||||
check(main.run_scope != null and not main.run_scope.is_adventure and main.run_scope.is_inside_tree(),
|
||||
"V2e run_scope 非空且为闲时会话(is_adventure=false+is_inside_tree)〔装配生命批 V2〕")
|
||||
# ⑥ 调试浮窗 boot 不开+菜单角标在(菜单优先——boot 不自动开浮窗,
|
||||
# 入口=开始屏右下角「调试」角标+对局中两屏角标)
|
||||
check(not main.debug_overlay.visible,
|
||||
check(not main.ui.modals.debug_overlay.visible,
|
||||
"V2f 调试浮窗 boot 不自动开(菜单优先)〔2026-10-16 用户令〕")
|
||||
check(main.debug_badge != null and not main.debug_badge.visible,
|
||||
check(main.ui.screens.debug_badge != null and not main.ui.screens.debug_badge.visible,
|
||||
"V2f 调试浮层角标发布态恒隐(2026-10-25 删写死行批)")
|
||||
main.queue_free()
|
||||
await process_frame
|
||||
@@ -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.debug_overlay.seed_edit.text = "42"
|
||||
main.ui.modals.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,22 +243,22 @@ func _smoke_run_lifecycle() -> void:
|
||||
break
|
||||
await process_frame
|
||||
check(finished.done, "V3 冒险终局到达(abandon→adventure_finished 同步链)")
|
||||
check(main._end_overlay.is_open(), "V3 终局面板弹起")
|
||||
check(main.ui.modals.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,
|
||||
"V3 会话收束(新闲时会话零冒险/关卡对象)")
|
||||
check(main.run_scope.hints_shown.is_empty() and not main.run_scope.boss_active and main.run_scope.last_gain_amount == 0,
|
||||
"V3 局态封印/账本全复位(queue_free 物化——注入脏状态零残留)")
|
||||
check(main._start_overlay.visible, "V3 回开始菜单(终局出口路径)")
|
||||
check(main.ui.screens.start_overlay.visible, "V3 回开始菜单(终局出口路径)")
|
||||
# ---- 第二局:放弃终局无断点残留→直进开局→全新会话 ----
|
||||
check(not sm.has_breakpoint(), "V3 放弃终局清断点(无残留断点)")
|
||||
main.debug_overlay.seed_edit.text = "43"
|
||||
main.ui.modals.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(),
|
||||
"V3 第二连局全新会话(两连局零泄漏)")
|
||||
check(not main._start_overlay.visible, "V3 直进入局(菜单隐)")
|
||||
check(not main.ui.screens.start_overlay.visible, "V3 直进入局(菜单隐)")
|
||||
main.queue_free()
|
||||
await process_frame
|
||||
for p in ["user://smoke_run_lifecycle.json", "user://smoke_run_lifecycle.json.tmp"]:
|
||||
@@ -281,14 +281,14 @@ func _smoke_sim_guards() -> void:
|
||||
# wd6:闲时态 + 模拟运行态 → 调试两层入口守卫早退(开始层不收起、无结算对象)
|
||||
check(main.run_scope.settlement == null and not main.run_scope.is_adventure,
|
||||
"前置:闲时会话(settlement 空 + is_adventure=false)")
|
||||
main._start_overlay.visible = true
|
||||
main.ui.screens.start_overlay.visible = true
|
||||
main._tune_sim._running = true
|
||||
main._start_debug_levels()
|
||||
check(main.run_scope.settlement == null and main._start_overlay.visible,
|
||||
check(main.run_scope.settlement == null and main.ui.screens.start_overlay.visible,
|
||||
"模拟运行态下调试两层入口守卫早退(开始层不收起)〔wd6〕")
|
||||
main._tune_sim._running = false
|
||||
# wd7:真实冒险关间态(is_adventure=true、settlement==null)→ 模拟入口守卫早退
|
||||
main.debug_overlay.seed_edit.text = "42"
|
||||
main.ui.modals.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,8 +313,8 @@ func _face_snapshot(main: Node) -> Dictionary:
|
||||
var tu = main._treasure_ui
|
||||
var bo = main._bonus
|
||||
var cp = main._camp_pres
|
||||
var mf = main._modal_flow
|
||||
var rp = main._row_pacer
|
||||
var mf = main.ui.modals.flow
|
||||
var rp = main.ui.screens.row_pacer
|
||||
return {
|
||||
"treasure_settlement": tu._settlement_get.call(),
|
||||
"treasure_runner": tu._runner_get.call(),
|
||||
@@ -381,13 +381,13 @@ 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.level_screen.visible != bool(extra[k]):
|
||||
if main.ui.modals.level_screen.visible != bool(extra[k]):
|
||||
bad += " level_screen.visible 应=%s" % str(extra[k])
|
||||
"end_open":
|
||||
if main._end_overlay.is_open() != bool(extra[k]):
|
||||
if main.ui.modals.end_overlay.is_open() != bool(extra[k]):
|
||||
bad += " _end_overlay.is_open 应=%s" % str(extra[k])
|
||||
"start_vis":
|
||||
if main._start_overlay.visible != bool(extra[k]):
|
||||
if main.ui.screens.start_overlay.visible != bool(extra[k]):
|
||||
bad += " _start_overlay.visible 应=%s" % str(extra[k])
|
||||
check(bad.is_empty(), "%s(引用面违例:%s)〔装配生命批 V3〕" % [tag, bad])
|
||||
|
||||
@@ -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.debug_overlay.seed_edit.text = "42"
|
||||
main.ui.modals.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.level_screen.press_candidate(0)
|
||||
main.ui.modals.level_screen.press_candidate(0)
|
||||
for i in range(10):
|
||||
await process_frame
|
||||
await _stage_faces(main, "V3c 战斗中", "alive", "alive", "alive", {"mf_start": false})
|
||||
@@ -465,9 +465,9 @@ func _smoke_lifecycle_matrix() -> void:
|
||||
check(main.run_scope.settlement == null or not main.run_scope.settlement.is_battle_live(),
|
||||
"V3d 活战斗驱动完毕(战斗已收场——%d 轮,finished=%s)〔装配生命批 V3〕" % [guard, str(finished.done)])
|
||||
await process_frame
|
||||
main._flush_row_queue()
|
||||
main.ui.screens.row_pacer.flush()
|
||||
for i in range(12):
|
||||
if main._end_overlay.is_open():
|
||||
if main.ui.modals.end_overlay.is_open():
|
||||
break
|
||||
await process_frame
|
||||
# 分支甲:战斗胜利停点(结算后)→确认进选路→放弃→终局面
|
||||
@@ -482,13 +482,13 @@ func _smoke_lifecycle_matrix() -> void:
|
||||
if finished.done:
|
||||
break
|
||||
await process_frame
|
||||
main._flush_row_queue()
|
||||
main.ui.screens.row_pacer.flush()
|
||||
for i in range(8):
|
||||
if main._end_overlay.is_open():
|
||||
if main.ui.modals.end_overlay.is_open():
|
||||
break
|
||||
await process_frame
|
||||
# 阶段⑤终局面(败北/放弃皆达)
|
||||
check(finished.done and main._end_overlay.is_open(),
|
||||
check(finished.done and main.ui.modals.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.save_select_overlay._new_btns.get(1)
|
||||
var ssel_new: Button = main.ui.modals.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.debug_overlay.seed_edit.text = "43"
|
||||
main.ui.modals.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._end_overlay.is_open():
|
||||
if main.ui.modals.end_overlay.is_open():
|
||||
break
|
||||
await process_frame
|
||||
main._flush_row_queue()
|
||||
main.ui.screens.row_pacer.flush()
|
||||
for i in range(8):
|
||||
if main._end_overlay.is_open():
|
||||
if main.ui.modals.end_overlay.is_open():
|
||||
break
|
||||
await process_frame
|
||||
await _stage_faces(main, "V3h 两连局·终局面", "alive", "alive", "null", {"mf_start": false, "end_open": true})
|
||||
|
||||
@@ -66,12 +66,33 @@ func _smoke_hygiene_gates() -> void:
|
||||
var mv: String = FileAccess.get_file_as_string("res://presentation/main.gd")
|
||||
Kit.check("V8 GP 影子账残留零命中(main 源码面——文件已删+委托缝已拆)〔迭代27 T3〕",
|
||||
not mv.contains("gp_shadow_ledger") and not mv.contains("_gp_ledger"))
|
||||
# V1d 层序落位单口:main.gd 的 move_child 调用全部收在 _apply_layer_contract 内(合同装配单口——
|
||||
# 函数体之外零调用点;出现第二处落位=硬编码层序,须改合同表而非加调用)
|
||||
var fn_start: int = mv.find("func _apply_layer_contract")
|
||||
var fn_end: int = mv.find("\nfunc ", fn_start + 10)
|
||||
if fn_end < 0:
|
||||
fn_end = mv.length()
|
||||
var outside_calls: int = (mv.substr(0, fn_start) + mv.substr(fn_end)).count("move_child(")
|
||||
Kit.check("V1d 层序落位单口(_apply_layer_contract 之外 move_child 调用点=%d 应=0)〔装配生命批 V1〕" % outside_calls,
|
||||
outside_calls == 0)
|
||||
# V1d 层序落位单口:注册面建件后扫全 presentation——move_child 只允许出现在 ui_registry.gd 的
|
||||
# apply_layer_contract 函数体内(合同装配单口——出现第二处落位=硬编码层序,须改合同表而非加调用)
|
||||
var scanned: int = 0
|
||||
var outside_calls: int = 0
|
||||
for fn_path: String in _list_scripts("res://presentation"):
|
||||
var src: String = FileAccess.get_file_as_string(fn_path)
|
||||
scanned += 1
|
||||
var fn_start: int = src.find("func apply_layer_contract") if fn_path.contains("ui_registry.gd") else -1
|
||||
var fn_end: int = src.find("\nfunc ", fn_start + 10) if fn_start >= 0 else -1
|
||||
if fn_end < 0:
|
||||
fn_end = src.length()
|
||||
outside_calls += (src.substr(0, fn_start) + src.substr(fn_end)).count("move_child(")
|
||||
Kit.check("V1d 层序落位单口(presentation %d 文件——apply_layer_contract 之外 move_child 调用点=%d 应=0)〔注册面 V1〕" % [scanned, outside_calls],
|
||||
scanned > 0 and outside_calls == 0)
|
||||
|
||||
|
||||
# 目录 .gd 清单(DirAccess 平铺——presentation 无子目录;扫描面>0 前置由调用侧断言承载)
|
||||
static func _list_scripts(dir_path: String) -> PackedStringArray:
|
||||
var out: PackedStringArray = PackedStringArray()
|
||||
var dir := DirAccess.open(dir_path)
|
||||
if dir == null:
|
||||
return out
|
||||
dir.list_dir_begin()
|
||||
var fname := dir.get_next()
|
||||
while fname != "":
|
||||
if fname.ends_with(".gd"):
|
||||
out.append(dir_path + "/" + fname)
|
||||
fname = dir.get_next()
|
||||
dir.list_dir_end()
|
||||
return out
|
||||
@@ -58,7 +58,7 @@ func _init() -> void:
|
||||
main._start_adventure_from_menu()
|
||||
await process_frame
|
||||
# 入口停点选路进首战(真实停点链)
|
||||
if not main.level_screen.visible:
|
||||
if not main.ui.modals.level_screen.visible:
|
||||
_problems.append("入口停点未呈现(前置失守)")
|
||||
_finish()
|
||||
return
|
||||
@@ -100,12 +100,12 @@ func _init() -> void:
|
||||
# ---- 断言①:排空前后核心状态逐位一致(演出关停零分叉)----
|
||||
var snap_before: String = JSON.stringify(battle.diagnostic_snapshot())
|
||||
var hp_before: int = int(battle.action_economy.ap) + int(battle.action_economy.rap) * 0 # 观测面双录
|
||||
main._flush_row_queue()
|
||||
main.ui.screens.row_pacer.flush()
|
||||
var snap_after: String = JSON.stringify(battle.diagnostic_snapshot())
|
||||
check(snap_before == snap_after, "排空前后核心诊断快照逐位一致(AC⑤ 演出关停零分叉)")
|
||||
# ---- 断言②:零丢行(结构判据:排空后队列恒空 + 末信号行原文在带——append_row 直落不吞行)----
|
||||
check(rows_seen.size() > 0, "结算行已产生(断言前提)")
|
||||
check(main._row_queue.is_empty(), "排空后队列恒空(flush 完整性)")
|
||||
check(main.ui.screens.row_pacer.pending_rows().is_empty(), "排空后队列恒空(flush 完整性)")
|
||||
var last_text := ""
|
||||
for i in range(rows_seen.size() - 1, -1, -1):
|
||||
var cand_row: Dictionary = rows_seen[i]
|
||||
@@ -114,7 +114,7 @@ func _init() -> void:
|
||||
last_text = String(cand_row.get("display_name", ""))
|
||||
if last_text != "":
|
||||
break
|
||||
check(last_text != "" and main.log_view.parsed_text().contains(last_text),
|
||||
check(last_text != "" and main.ui.battle.log_view.parsed_text().contains(last_text),
|
||||
"末信号行原文在明细带(排空不吞行:『%s』)" % last_text)
|
||||
# ---- 断言③:段间延迟取数面双态 ----
|
||||
UiKit.motion_scale = 1.0
|
||||
@@ -129,7 +129,7 @@ func _init() -> void:
|
||||
var fstage := FakeStage.new(null)
|
||||
var ffx := FakeFx.new()
|
||||
rp.stage = fstage
|
||||
rp.board_view = main.board_view
|
||||
rp.board_view = main.ui.battle.board_zone.board_view
|
||||
rp.settlement_fx = ffx
|
||||
rp._play_settlement_mark({"type": "damage", "target": "地精", "target_id": "goblin_b", "value": 3})
|
||||
check(fstage.calls.size() == 1 and fstage.calls[0] == ["goblin_b", "地精"],
|
||||
@@ -144,9 +144,9 @@ func _init() -> void:
|
||||
UiKit.motion_scale = 1.0 # 常态入队(减动效快路同帧直落不入队——夹具须真落队列)
|
||||
main._on_step_row({"type": "damage", "target": "hero", "value": 1})
|
||||
main._on_step_row({"type": "heal", "target": "hero", "value": 2})
|
||||
check(main._row_queue.size() == 2, "会话更替夹具已入队(前提:%d 行)〔wr4〕" % main._row_queue.size())
|
||||
check(main.ui.screens.row_pacer.pending_rows().size() == 2, "会话更替夹具已入队(前提:%d 行)〔wr4〕" % main.ui.screens.row_pacer.pending_rows().size())
|
||||
main._begin_session(false)
|
||||
check(main._row_queue.is_empty(), "会话更替后待播队列清零(_begin_session 内机制 flush)〔wr4〕")
|
||||
check(main.ui.screens.row_pacer.pending_rows().is_empty(), "会话更替后待播队列清零(_begin_session 内机制 flush)〔wr4〕")
|
||||
_finish()
|
||||
|
||||
|
||||
|
||||
@@ -722,8 +722,8 @@ func _smoke_diag_button_retired() -> void:
|
||||
var main_src: String = FileAccess.get_file_as_string("res://presentation/main.gd")
|
||||
Kit.check("V6 主区无 build_diag_button 调用残留(旧实现同批清理)",
|
||||
not main_src.contains("build_diag_button"))
|
||||
Kit.check("V6 转储入口连接随迁(组合根接浮窗 diagnose_requested 信号)",
|
||||
main_src.contains("debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)"))
|
||||
Kit.check("V6 转储入口连接随迁(组合根经注册面接浮窗 diagnose_requested 信号)〔注册面 V2〕",
|
||||
main_src.contains("ui.modals.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")
|
||||
|
||||
Reference in new issue
Block a user