diff --git a/game/presentation/views/career_overlay.gd b/game/presentation/views/career_overlay.gd index c3a7d6b0..0f4670f0 100644 --- a/game/presentation/views/career_overlay.gd +++ b/game/presentation/views/career_overlay.gd @@ -47,6 +47,9 @@ func _init() -> void: box.add_child(hint) # 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、通关局数三关全通才计) + # 对比度口径:页底=暖亮档深棕(BG_BRIGHT),文字全用亮墨族 token(WARM 9.0:1 / WARM_SOFT 8.0:1 / + # CANDLE 7.2:1 / INK_L_GOLD 9.1:1 实测对 BG_BRIGHT 全过 §7.2 ≥4.5:1——深墨族〔INK_LABEL〕 + # 压深棕底仅 ~1.3:1 属违例,禁用) _section_head(box, "冒险战绩") _total_label = _row(box, "冒险总局数") _cleared_label = _row(box, "通关局数") @@ -59,7 +62,7 @@ func _init() -> void: var bal_row := HBoxContainer.new() bal_row.add_theme_constant_override("separation", 8) box.add_child(bal_row) - var bal_name := UiKit.label("钥石余额",UiKit.FS_BODY, false, UiKit.INK_LABEL) + var bal_name := UiKit.label("钥石余额",UiKit.FS_BODY, false, UiKit.WARM) bal_row.add_child(bal_name) _keystone_badge = UiKit.keystone_badge("钥石 0") bal_row.add_child(_keystone_badge) @@ -123,16 +126,16 @@ func _emit_closed() -> void: # ---- 行构件(名称列 + 数值列——暂停层行族同构;数值 Label 返回供探针/冒烟投影断言)---- func _section_head(box: VBoxContainer, text: String) -> void: - box.add_child(UiKit.label(text,UiKit.FS_BODY, true, UiKit.INK_LABEL)) + box.add_child(UiKit.label(text,UiKit.FS_BODY, true, UiKit.INK_L_GOLD)) func _row(box: VBoxContainer, name_text: String) -> Label: var line := HBoxContainer.new() line.add_theme_constant_override("separation", 8) box.add_child(line) - var name_l := UiKit.label(name_text,UiKit.FS_BODY, false, UiKit.INK_LABEL) + var name_l := UiKit.label(name_text,UiKit.FS_BODY, false, UiKit.WARM) name_l.custom_minimum_size = Vector2(150, 0) line.add_child(name_l) - var val_l := UiKit.label("—",UiKit.FS_BODY, true, UiKit.INK_TITLE) + var val_l := UiKit.label("—",UiKit.FS_BODY, true, UiKit.CANDLE) line.add_child(val_l) return val_l diff --git a/game/smoke/ui_click_probe.gd b/game/smoke/ui_click_probe.gd index 8e6e03a8..cab4cc11 100644 --- a/game/smoke/ui_click_probe.gd +++ b/game/smoke/ui_click_probe.gd @@ -154,6 +154,33 @@ func _ready() -> void: _problems.append("开始菜单缺退出游戏按钮(二轮 D1)") if ProbeUiUtil.find_button(main.settings_overlay, "退出游戏", "prefix") != null: _problems.append("设置面仍含退出游戏按钮(二轮 D1——只在一级菜单出现)") + # ---- 生涯页段(迭代 18 卡④——入口点击链+关闭回返,真实按钮信号路径;本页只读零写, + # 值断言归冒烟投影段与 ui_struct 结构族,此处只验开合路径与零跳转)---- + var career_btn: Button = ProbeUiUtil.find_button(main._start_overlay, "生涯", "prefix") + if career_btn == null: + _problems.append("生涯入口按钮缺失(开始层 careerbtn)") + else: + career_btn.pressed.emit() # 真实按钮信号路径 → 组合根 _open_career + await get_tree().process_frame + if not main.career_overlay.visible or main._start_overlay.visible: + _problems.append("生涯页未打开(点击路径)") + else: + # 零跳转契约(提纲 P4 甲):页内除「返回」外零导航钮 + for nav in ["营地", "商店", "设置", "退出", "冒险"]: + if ProbeUiUtil.find_button(main.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: + _problems.append("局内屏出现生涯入口件(局内不放生涯入口违约)") + var career_close: Button = ProbeUiUtil.find_button(main.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: + _problems.append("生涯页关闭后未恢复开始层") # 换回全新零档(营地购买态不泄入后续冒险段——确定性;档案残留下次段首清理兜底) ProbeUiUtil.isolate_save(main, "smoke_click_probe_reset", true, false, true) # 零档换注 + 继续上局按钮重刷(原三行样板收口) var resume_btn0: Button = ProbeUiUtil.find_button(main._start_overlay, "▶ 继续上局", "prefix") diff --git a/game/smoke/ui_contrast_probe.gd b/game/smoke/ui_contrast_probe.gd index 34c5141a..59679a0d 100644 --- a/game/smoke/ui_contrast_probe.gd +++ b/game/smoke/ui_contrast_probe.gd @@ -29,6 +29,9 @@ func _pairs() -> Array: ["锚·暖白/屏根", UiKit.WARM, UiKit.BG_ROOT, 1.0], # 全站锚点:暗底正文 ["锚·暖白弱/舞台底", UiKit.WARM_FAINT, UiKit.STAGE_BG, 1.0], # 全站锚点:B区辅助墨 ["钥石徽·深墨/烛光黄", UiKit.INK_ON_GOLD, UiKit.CANDLE, 1.0], # 迭代17卡③——badge 工厂件文字(营地徽+暂停层余额行同形制) + ["生涯页·行名墨/暖亮页底", UiKit.WARM, UiKit.BG_BRIGHT, 1.0], # 迭代18卡④——生涯页行名列/提示文(WARM_SOFT 同族亮墨) + ["生涯页·行值墨/暖亮页底", UiKit.CANDLE, UiKit.BG_BRIGHT, 1.0], # 迭代18卡④——生涯页数值列(烛光黄 bold) + ["生涯页·组帽墨/暖亮页底", UiKit.INK_L_GOLD, UiKit.BG_BRIGHT, 1.0],# 迭代18卡④——生涯页分组小节头(金亮 bold) ] diff --git a/game/smoke/ui_struct_probe.gd b/game/smoke/ui_struct_probe.gd index 26ce81f9..aa51a239 100644 --- a/game/smoke/ui_struct_probe.gd +++ b/game/smoke/ui_struct_probe.gd @@ -48,6 +48,27 @@ func _ready() -> void: else: problems.append("[camp] 营地入口按钮缺失(驱动中断)") + # ---- 生涯页(迭代 18 卡④):入口点击链(合成点击真路径=pressed.emit→组合根 _open_career)→ + # 结构断言族→只读巡检(契约 3)+零动效契约+登记序/树序契约(P3 甲营地同档)+零跳转契约 + #(提纲 P4 甲)→组合根真实路径关闭。---- + var careerbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "生涯", "prefix") + if careerbtn != null: + careerbtn.pressed.emit() + _settle() + if not main.career_overlay.visible or main._start_overlay.visible: + problems.append("[career] 生涯页未打开或开始层未隐藏(入口点击链断裂)") + await _audit("career", [main.career_overlay]) + _career_readonly_contract(main) + _career_zero_motion_contract(main) + _career_registry_contract(main) + _career_nojump_contract(main) + main._on_career_closed() + _settle() + if main.career_overlay.visible or not main._start_overlay.visible: + problems.append("[career] 关闭后页未隐藏或开始层未恢复(模态栈单口失守)") + else: + problems.append("[career] 生涯入口按钮缺失(开始层 careerbtn)") + main.confirm_dialog.open("结构探针采样", "确认") _settle() await _audit("confirm", [main.confirm_dialog]) @@ -233,6 +254,75 @@ func _pause_zero_motion_contract(main: Node) -> void: problems.append("[pause][motion] 视图源码含「%s」(R5 零动效直开直关破——P4 甲)" % token) +# ---- 生涯页契约族(迭代 18 卡④——任务书契约 3/4+提纲 P3/P4 甲)---- + +# ⑩career 只读性契约(契约 3——暂停层 ⑧ 同形制):生涯页子树全控件 × 游戏语义信号连接巡检—— +# 清单外零连接。白名单={视图自身 _emit_closed〔返回钮〕, 组合根 _on_career_closed};页内任何 +# 购买/导航/写入方向的新连接一律红,须先入册审查。 +func _career_readonly_contract(main: Node) -> void: + var overlay: Control = main.career_overlay + var stack: Array = [overlay] + var conn_count := 0 + while not stack.is_empty(): + var n: Node = stack.pop_back() + for sig_name in _PAUSE_READONLY_SIGNALS: + for conn in n.get_signal_connection_list(sig_name): + conn_count += 1 + var cb: Callable = conn["callable"] + var host: Object = cb.get_object() + var m := String(cb.get_method()) + if host == overlay and m == "_emit_closed": + continue + if host == main and m == "_on_career_closed": + continue + problems.append("[career][readonly] 非法信号连接 %s.%s → %s.%s(零写通路巡检——白名单=视图 _emit_closed/组合根 _on_career_closed)" + % [n.name, sig_name, host, m]) + for ch in n.get_children(): + stack.append(ch) + if conn_count != 2: + problems.append("[career][readonly] 信号连接数=%d 应=2(返回钮 pressed+closed 外发——多连接=写通路嫌疑)" % conn_count) + + +# ⑪career 零动效契约(暂停层 ⑨ 同形制——P4 甲直开直关) +func _career_zero_motion_contract(main: Node) -> void: + var overlay: Control = main.career_overlay + var stack: Array = [overlay] + while not stack.is_empty(): + var n: Node = stack.pop_back() + if n is AnimationPlayer: + problems.append("[career][motion] 子树含 AnimationPlayer(零动效破)") + for ch in n.get_children(): + stack.append(ch) + var src := FileAccess.get_file_as_string("res://presentation/views/career_overlay.gd") + for token in ["create_tween", "UiKit.motion"]: + if src.contains(token): + problems.append("[career][motion] 视图源码含「%s」(零动效直开直关破)" % token) + + +# ⑫career 登记/树序契约(P3 甲——登记序=营地同档:暂停层之后、确认弹窗之前;树序同步=addChild +# 同位。登记序=树序=z 序纪律〔缺陷⑤〕——两序任一失守即红) +func _career_registry_contract(main: Node) -> void: + var list: Array = main._modals._list + var ci: int = list.find(main.career_overlay) + var pi: int = list.find(main.pause_overlay) + var fi: int = list.find(main.confirm_dialog) + if ci < 0 or pi < 0 or fi < 0: + problems.append("[career][registry] 生涯页未入登记册(或暂停层/确认弹窗缺登记)") + return + if ci != pi + 1 or ci > fi: + problems.append("[career][registry] 登记序违约(应=暂停层后、确认弹窗前——P3 甲营地同档;pause=%d career=%d confirm=%d)" % [pi, ci, fi]) + var ti: int = main.career_overlay.get_index() + if ti <= main.pause_overlay.get_index() or ti > main.confirm_dialog.get_index(): + problems.append("[career][registry] 树序与登记序不同步(addChild 位应=暂停层后、确认弹窗前;tree_index=%d)" % ti) + + +# ⑬career 零跳转契约(提纲 P4 甲——页内零「前往他处」入口:除「返回」外任何导航钮=红) +func _career_nojump_contract(main: Node) -> void: + for nav in ["营地", "商店", "设置", "冒险", "继续上局", "退出", "前往"]: + if ProbeUiUtil.find_button(main.career_overlay, nav, "contains") != null: + problems.append("[career][nojump] 页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav) + + # ⑥standee 专项:B 区站桩内容矩形 ⊆ 252px 舞台矩形(内缩 8px)且不被裁切 # (UI 设计对齐批 N2 2026-10-06——名行基线离盒底 1px 同型高危写法返修回归闸; # 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘)