Files
gplan-2/game/smoke/board_shot_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

87 lines
4.2 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
# 棋盘渲染帧取证探针(目视验证面:机检≠看得清)。
# 必须**窗口化**运行(headless 无渲染面):
# Godot_console.exe --path . res://smoke/board_shot_probe.tscn -- <输出目录>
# 流程:L1/L2/L3 各起一板(真实 LevelRunner+settle_batch 驱动 ~18 步 novice——自然揭示数字/旗),
# BoardView 真渲染,frame_post_draw 同步后抓视口 PNG。零存档触碰(无 save_manager 引用)。
# 护栏:headless fail-fast(shot_probe 同款);空纹理取证 fail——取证段失败=闸红,
# 零字节/空纹理不允许静默落盘;board_meta.json 取样坐标落盘(棋盘视图全局矩形——
# golden 前置裁剪坐标源,禁硬编码像素)。
extends Node
const AutoPlayer := preload("res://smoke/auto_player.gd")
var _meta := {} # 棋盘视图实测几何(全局 rect——tools/golden_board_crop.py 取样坐标源)
func _ready() -> void:
# 运行前提=窗口化真渲染面(shot_probe 同款护栏——headless 下 frame_post_draw 永不触发+视口纹理恒空)
if DisplayServer.get_name() == "headless":
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);正主=tools/render_gate.sh 取证段")
get_tree().quit(1)
return
var args := OS.get_cmdline_user_args()
var out := args[0] if args.size() >= 1 and args[0] != "" else "user://board-shots"
DirAccess.make_dir_recursive_absolute(out)
await get_tree().process_frame
var db = load("res://core/config_db.gd").new()
var ok: bool = db.load_tables("res://data/tables", true)
if not ok:
push_error("[board_shot] 表加载失败")
get_tree().quit(1)
return
var ui_font: Font = load("res://presentation/views/ui_kit.gd").load_font(load("res://presentation/views/ui_kit.gd").FONT_UI)
var targets := [[1, "L1_dungeon"], [2, "L2_underdark"], [3, "L3_deep_underdark"]]
for t in targets:
var level_index: int = t[0]
var tag: String = t[1]
var rng = load("res://core/rng_service.gd").new()
rng.set_master_seed(420000 + level_index)
var runner = load("res://core/level_runner.gd").new(db, rng)
if not runner.start_level(level_index, 420000 + level_index, {}, 0):
push_error("[board_shot] 开关失败 level=%d" % level_index)
get_tree().quit(1)
return
var settlement = runner.current_battle()
for i in range(18):
var act: Dictionary = AutoPlayer.novice_action(settlement.minesweeper)
var kind := String(act.get("kind", "stop"))
if kind == "stop":
break
var op := kind
if kind == "flag":
op = "flag_place"
settlement.minesweeper.settle_batch([{"action": op, "cell": act.get("cell")}])
var view = load("res://presentation/views/board_view.gd").new()
view.size = Vector2(900, 900)
view.position = Vector2(40, 40)
if ui_font != null:
view.set_world_font(ui_font)
add_child(view)
view.rebuild(settlement.minesweeper.current_board(), settlement.minesweeper)
# 抓帧前等入场动画完结(迭代 19 卡① S2 观察项根因修复 2026-10-08):rebuild 有全板淡入
# motion(0.2)+R0 涟漪逐格延迟显形(motion(0.04×离板心距离)+motion(0.12),最远格 ≈0.56s),
# 原制 6 个 process_frame 热机后 ≈0.1s=捕在涟漪中途——逐跑帧时序抖动使揭示 alpha 漂移,
# golden L2/L3 跨 2% 容差线波动(实测 0.00-2.53%)。改时间制 1.2s(2×余量)=锚定动画完结态。
await get_tree().create_timer(1.2).timeout
await get_tree().process_frame
await RenderingServer.frame_post_draw
var img := get_viewport().get_texture().get_image()
if img == null or img.is_empty():
push_error("[board_shot] FAIL — 抓帧空纹理: %s(Q7 取证语义=闸红)" % tag)
get_tree().quit(1)
return
var path := "%s/board_%s.png" % [out, tag]
img.save_png(path)
_meta[tag] = {"x": view.global_position.x, "y": view.global_position.y,
"w": view.size.x, "h": view.size.y}
print("[board_shot] saved %s (%dx%d)" % [path, img.get_width(), img.get_height()])
view.queue_free()
await get_tree().process_frame
var mf := FileAccess.open(out.path_join("board_meta.json"), FileAccess.WRITE)
if mf != null:
mf.store_string(JSON.stringify(_meta))
mf.close()
print("[board_shot] done")
get_tree().quit(0)