- CLAUDE.md 硬性红线新增「文件头零工作日志」;code-conventions.md 新增 §1.4.1(含反面案例) - 全仓 .gd 文件头注清偿:删除全部带日期条目、批次叙事段与迁移退役叙述,保留并改写为 现在时的职责、接口契约、GDD 锚内容(main.gd 头注 42→31 行、save_manager.gd 49→45 行) - 纯注释改动零行为变更,按用户令免测试免冒烟
87 lines
4.2 KiB
GDScript
87 lines
4.2 KiB
GDScript
# 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)
|