Files
gplan-2/game/smoke/ui_audit_probe.gd
T
DKChan 5f9d8e7b77 chore(代码卫生): 文件头零工作日志红线落文+全仓头注变更叙事清偿(2026-10-15 用户令)
- CLAUDE.md 硬性红线新增「文件头零工作日志」;code-conventions.md 新增 §1.4.1(含反面案例)
- 全仓 .gd 文件头注清偿:删除全部带日期条目、批次叙事段与迁移退役叙述,保留并改写为
  现在时的职责、接口契约、GDD 锚内容(main.gd 头注 42→31 行、save_manager.gd 49→45 行)
- 纯注释改动零行为变更,按用户令免测试免冒烟
2026-10-15 04:08:38 +08:00

162 lines
6.4 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VERTICAL SLICE - NOT FOR PRODUCTION
# 全屏 UI 审计截图探针(实机每一屏 vs demo 逐屏对比;shot_probe 扩展形)。
# 必须**窗口化**运行(真渲染面;headless 守卫同 shot_probe):
# Godot_console.exe --path . res://smoke/ui_audit_probe.tscn -- <输出目录>
# 覆盖 = demo 八映射屏(start/camp/settings/map/battle/bonus/shop/end 胜败两态)+ demo 无映射三屏
# (legend 图例/confirm 确认框/debug 调试浮窗)。真实信号路径驱动 + 合成面开窗(ui_struct_probe 同款
# 真实 open API+拟真数据);双帧 frame_post_draw 同步抓视口纹理。
extends Node
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd")
const Narrator := preload("res://presentation/battle_narrator.gd")
var main: Node
var out_dir := ""
var _problems: Array = []
func _ready() -> void:
if DisplayServer.get_name() == "headless":
print("[ui_audit] FAIL — 本探针必须窗口化运行(headless 无渲染面)")
get_tree().quit(1)
return
var args := OS.get_cmdline_user_args()
out_dir = args[0] if args.size() >= 1 and args[0] != "" else "user://ui-audit"
DirAccess.make_dir_recursive_absolute(out_dir)
await get_tree().process_frame
main = load("res://presentation/main.tscn").instantiate()
add_child(main)
await get_tree().process_frame
ProbeUiUtil.isolate_save(main, "smoke_ui_audit", true, true, false)
await get_tree().process_frame
# 断点种子(2026-10-06 判官 checkpoint——demo 标题屏=有档态「继续上局」主钮亮出;无种子=
# 续玩钮隐藏,主操作语义对齐⑤无从呈现。最小合法断点快照;后续「开始冒险」流程自然覆盖)
main.save_manager.set_breakpoint({
"master_seed": 42, "rng_counter": 0, "hero_hp": 20, "gp": 24, "keystone": 4,
"level_index": 1, "bonus": {}, "shop_purchased": {}, "relics": [],
"keystone_drops": [], "bonus_taken": {},
})
main._refresh_resume_button()
await get_tree().process_frame
# 1 标题屏(启动态=有档态)
await _snap("1_start")
# 2 调试浮窗(P3 仅开始菜单可开——趁 _level_active=false 先截)
main._show_debug_overlay()
await _settle()
await _snap("9_debug")
main._close_debug_overlay()
await _settle()
# 3 设置
main._open_settings()
await _settle()
await _snap("3_settings")
main._on_settings_closed()
await _settle()
# 4 营地
var campbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "营地", "contains")
if campbtn != null:
campbtn.pressed.emit()
await _settle()
await _snap("2_camp")
var close_btn: Button = ProbeUiUtil.find_button(main.camp_overlay, "关闭营地", "contains")
if close_btn != null:
close_btn.pressed.emit()
await _settle()
# 5 关卡地图(开始冒险→停点出图)
# 收种子断点(有断点时「开始冒险」=清档确认互斥弹窗——不收则断流,5_battle 截不上)
main.save_manager.clear_breakpoint()
main._refresh_resume_button()
await get_tree().process_frame
var lvbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "开始冒险", "contains")
if lvbtn != null:
lvbtn.pressed.emit()
for i in 4:
await get_tree().process_frame
await _settle()
await _snap("4_map")
# 6 战斗(真实选路提交)
if main.level_screen != null and main.level_screen.visible:
main.level_screen.press_candidate(0)
for i in 10:
await get_tree().process_frame
await _settle()
await _snap("5_battle")
# 7 战斗结算·胜利(真实签名的拟真载荷——main.gd:697 同序)
main._end_overlay.open_victory(Narrator.battle_end_text("victory", true), "返回",
main.board_view.get_global_rect(), "战利品 GP +5 · 合计 24",
"钥石 · 累计 27", "本关加成:攻击 +1", 5, 24)
await _settle()
await _snap("8_end_victory")
main._end_overlay.close()
await _settle()
# 8 战斗结算·失败
main._end_overlay.open_defeat(Narrator.battle_end_text("defeat", false), "返回",
main.board_view.get_global_rect())
await _settle()
await _snap("8_end_defeat")
main._end_overlay.close()
await _settle()
# 9 三选一(拟真三卡)
main.curio_overlay.open_pick(Narrator.node_curio_header(), [
{"id": "heal_rare", "text": "回血\n恢复 3 点"},
{"id": "blast_rare", "text": "爆裂轰击\n下一击 +5"},
{"id": "pierce_rare", "text": "穿甲\n无视护甲"},
])
await _settle()
await _snap("6_bonus")
main.curio_overlay.visible = false
await _settle()
# 10 商店(买前态——pool 货架面=玩家正主形态,demo #v-shop 对比基线;T6 全屏页)
main.shop_overlay.open_shelf([
{"kind_id": "attack", "display_name": "攻击强化", "value": 1, "price": 12},
{"kind_id": "max_hp", "display_name": "生命强化", "value": 3, "price": 12},
{"kind_id": "armor", "display_name": "护甲片", "value": 1, "price": 15},
], 27)
main.shop_overlay.set_visit_info(0, 1.8) # 爬价规则行(T6——presenter 同款投喂)
await _settle()
await _snap("7_shop")
main.shop_overlay.visible = false
await _settle()
# 11 图例
main.legend_overlay.open()
await _settle()
await _snap("10_legend")
main.legend_overlay.close()
await _settle()
# 12 确认框(放弃二次确认实文案)
main.confirm_dialog.open("放弃冒险", "确认放弃本次冒险?已入账钥匙与 GP 将结算保留")
await _settle()
await _snap("11_confirm")
main.confirm_dialog.close()
await _settle()
for s in ["1_start", "9_debug", "3_settings", "2_camp", "4_map", "5_battle",
"8_end_victory", "8_end_defeat", "6_bonus", "7_shop", "10_legend", "11_confirm"]:
var p := out_dir.path_join("real_%s.png" % s)
if not FileAccess.file_exists(p):
_problems.append("截图缺失: %s" % s)
else:
var fa := FileAccess.open(p, FileAccess.READ)
if fa == null or fa.get_length() < 1000:
_problems.append("截图疑似空文件: %s" % s)
print("[ui_audit] 完成 out=%s 问题=%d %s" % [out_dir, _problems.size(), str(_problems)])
get_tree().quit(1 if _problems.size() > 0 else 0)
func _settle() -> void:
for i in 3:
await get_tree().process_frame
await RenderingServer.frame_post_draw
func _snap(key: String) -> void:
await RenderingServer.frame_post_draw
await RenderingServer.frame_post_draw
var img: Image = get_viewport().get_texture().get_image()
if img == null:
_problems.append("视口纹理为空: %s" % key)
return
img.save_png(out_dir.path_join("real_%s.png" % key))
print("[ui_audit] saved %s (%dx%d)" % [key, img.get_width(), img.get_height()])