iter44-卡1:卡①(组合根注册面批): ui_registry+三域容器建件·main.gd 49→15(28迁出+7别名消灭+ui新增)·六转发方法+flush退役·全消费面(探针11件/系统测试/流程件/冒烟)改址main.ui.*·V1d全目录重锚〔注册面V1〕·V6改串〔注册面V2〕·行为零变化四闸主证
This commit is contained in:
1 parent
14054d9dfb
commit
9fa93787b7
41 files changed
+1073
-1070
No files matched your search
@@ -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