From dc2d514d97be568960d7a51d70791bc9e9362883 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Wed, 7 Oct 2026 09:01:22 +0800 Subject: [PATCH] =?UTF-8?q?=E2=91=A3T4:=20=E6=8E=A2=E9=92=88=E6=96=AD?= =?UTF-8?q?=E8=A8=80=E9=9D=A2=E2=80=94=E2=80=94ui=5Fstruct=20=E7=94=9F?= =?UTF-8?q?=E6=B6=AF=E5=B1=8F=E5=9B=9B=E5=A5=91=E7=BA=A6=EF=BC=88=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=E7=82=B9=E5=87=BB=E9=93=BE=3D=E5=90=88=E6=88=90?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E7=9C=9F=E8=B7=AF=E5=BE=84=20pressed.emit?= =?UTF-8?q?=E2=86=92=5Fopen=5Fcareer+=E5=BC=80=E5=90=88=E5=9B=9E=E8=BF=94?= =?UTF-8?q?=E6=96=AD=E8=A8=80/=E2=91=A9=E5=8F=AA=E8=AF=BB=E5=B7=A1?= =?UTF-8?q?=E6=A3=80=3D=E5=AD=90=E6=A0=91=E6=B8=B8=E6=88=8F=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E4=BF=A1=E5=8F=B7=E7=99=BD=E5=90=8D=E5=8D=95=E5=A4=96?= =?UTF-8?q?=E9=9B=B6=E8=BF=9E=E6=8E=A5=E3=80=9417=E5=8D=A1=E2=91=A2?= =?UTF-8?q?=E5=BD=A2=E5=88=B6=C2=B7=E6=B8=85=E5=8D=95=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=C2=B7=E6=81=B0=202=20=E8=BF=9E=E6=8E=A5=3D=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E9=92=AE+closed=20=E5=A4=96=E5=8F=91=E3=80=95/=E2=91=AA?= =?UTF-8?q?=E9=9B=B6=E5=8A=A8=E6=95=88=E5=A5=91=E7=BA=A6=3D=E5=AD=90?= =?UTF-8?q?=E6=A0=91=E9=9B=B6=20AnimationPlayer+=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E9=9B=B6=20tween/=E2=91=AB=E7=99=BB=E8=AE=B0=E5=BA=8F=E6=A0=91?= =?UTF-8?q?=E5=BA=8F=E5=A5=91=E7=BA=A6=3D=E7=99=BB=E8=AE=B0=E5=86=8C?= =?UTF-8?q?=E6=9A=82=E5=81=9C=E5=B1=82=E5=90=8E=E7=A1=AE=E8=AE=A4=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E5=89=8D+addChild=20=E4=BD=8D=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E3=80=94P3=20=E7=94=B2=E4=B8=A4=E5=BA=8F=E5=A4=B1=E5=AE=88?= =?UTF-8?q?=E5=8D=B3=E7=BA=A2=E3=80=95/=E2=91=AC=E9=9B=B6=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E5=A5=91=E7=BA=A6=3D=E9=A1=B5=E5=86=85=E9=99=A4?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=A4=96=E4=B8=83=E5=AF=BC=E8=88=AA=E8=AF=8D?= =?UTF-8?q?=E9=9B=B6=E9=92=AE=E3=80=94P4=20=E7=94=B2=E3=80=95=EF=BC=89+ui?= =?UTF-8?q?=5Fclick=20=E7=94=9F=E6=B6=AF=E6=AE=B5=EF=BC=88=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=E7=82=B9=E5=87=BB=E9=93=BE=E8=A1=8C=E4=B8=BA=E7=BA=A7?= =?UTF-8?q?=E5=A4=8D=E9=AA=8C+=E9=9B=B6=E8=B7=B3=E8=BD=AC+=E5=B1=80?= =?UTF-8?q?=E5=86=85=E9=9B=B6=E7=94=9F=E6=B6=AF=E5=85=A5=E5=8F=A3=E4=BB=B6?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E3=80=94board=5Fzone/level=5Fscreen=20?= =?UTF-8?q?=E6=97=A0=20career=5Fbadge=E3=80=95+=E5=85=B3=E9=97=AD=E5=9B=9E?= =?UTF-8?q?=E8=BF=94=E6=81=A2=E5=A4=8D=E5=BC=80=E5=A7=8B=E5=B1=82=EF=BC=89?= =?UTF-8?q?+ui=5Fcontrast=20=E6=B5=8B=E7=82=B9=E4=B8=89=E5=AF=B9=E9=9A=8F?= =?UTF-8?q?=E6=89=B9=E5=85=A5=E8=A1=A8=EF=BC=88=E7=94=9F=E6=B6=AF=E9=A1=B5?= =?UTF-8?q?=E8=A1=8C=E5=90=8D=20WARM/=E8=A1=8C=E5=80=BC=20CANDLE/=E7=BB=84?= =?UTF-8?q?=E5=B8=BD=20INK=5FL=5FGOLD=20=E5=AF=B9=20BG=5FBRIGHT=E2=80=94?= =?UTF-8?q?=E2=80=94=E6=B7=B1=E5=A2=A8=E6=97=8F=E5=8E=8B=E6=B7=B1=E6=A3=95?= =?UTF-8?q?=E5=BA=95=20~1.3:1=20=E8=BF=9D=20=C2=A77.2=20=E7=9A=84=E8=BF=94?= =?UTF-8?q?=E5=B7=A5=E8=90=BD=E9=97=B8=EF=BC=9Acareer=5Foverlay=20?= =?UTF-8?q?=E6=96=87=E5=AD=97=E5=85=A8=E6=8D=A2=E4=BA=AE=E5=A2=A8=E6=97=8F?= =?UTF-8?q?=20token=20=E9=9B=B6=E6=96=B0=E8=89=B2=E5=80=BC=EF=BC=89?= =?UTF-8?q?=E2=80=94=E2=80=94probe=5Fgate=20=E5=85=A8=E7=BB=BF=EF=BC=88ui?= =?UTF-8?q?=5Finput=20=E5=8D=81=E4=B8=83=E6=96=AD=E8=A8=80/ui=5Fstruct=20?= =?UTF-8?q?=E5=85=A8=E5=B1=8F=E9=9B=B6=E7=BA=A2/ui=5Fcontrast=2015=20?= =?UTF-8?q?=E5=AF=B9=EF=BC=89+ui=5Fclick=20done=3Dtrue=20=E9=97=AE?= =?UTF-8?q?=E9=A2=98=3D0+ui=5Flayout=20exit=200+=E5=86=92=E7=83=9F=20834/0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/views/career_overlay.gd | 11 ++- game/smoke/ui_click_probe.gd | 27 +++++++ game/smoke/ui_contrast_probe.gd | 3 + game/smoke/ui_struct_probe.gd | 90 +++++++++++++++++++++++ 4 files changed, 127 insertions(+), 4 deletions(-) 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(),最低绘制元素=底座下缘)