卡①T3(组合根注册面批): 探针十一件(九件+probe_ui_util+shot/fx_shot两取证件)+system_test+pacing_queue_test 批量改址 main.ui.*——422行机械改写+接缝三件(_modals→flow.stack/_deferred_modal→flow._deferred/_settlement_stop_pending读→settlement_stop_timer.is_armed)+V1c/V2/V3矩阵取址改指;实现决定2条: ①shot_probe/fx_shot_probe/render_gate取证件不在卡片探针九件清单但为main.X消费方,改址完整性自检grep抓漏后纳入;②ui_tune_probe零连接巡检白名单登记closed外发消费新宿主flow.close(表D申报的bind目标改写之机检联动);layout_probe三区get()字符串键改容器字段同名键取值;probe_gate全段+系统测试69项+冒烟+两套件全绿
This commit is contained in:
1 parent
b89ee8505d
commit
784d581bd1
13 files changed
+463
-460
No files matched your search
+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})
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user