124 lines
5.0 KiB
GDScript
124 lines
5.0 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 演出渲染证据探针(渲染层测试解冻批 2026-10-01——fx_shot_probe:真实渲染设备抓战斗演出瞬间帧)。
|
||
# 必须**窗口化**运行(真渲染设备——headless 无渲染面):Godot_console.exe --path . res://smoke/fx_shot_probe.tscn -- <输出目录>
|
||
# 流程 = shot_probe 同款隔离面(存档/设置/元进度换注隔离档,禁碰真实档)+ 真实按钮路径开冒险进首战;
|
||
# 自动玩家挖格驱动行结算,在 行到达/弹道飞行/结算排播 三时刻抓帧——演出链「逻辑在(fx_probe PASS)×
|
||
# 渲染在(本探针 PNG)」双证的第二半边。PNG 落输出目录(artwork/qa-fx-*/,素材纪律不入 git)。
|
||
extends Node
|
||
|
||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # T6 收口:树寻址/隔离样板单点
|
||
|
||
# ── 演出采样窗口口径(拍板点:headless/窗口化帧率不定,帧时刻以墙钟近似锚定;改窗口 = 改抓帧
|
||
# 时刻观测面,非断言语义。窗口值与原字面量逐一同值——T6 收口批仅提具名。) ──
|
||
const T_DIG_FLIGHT := 0.12 # 挖格行到达/弹道在飞窗口(settle_batch 回执后停留)
|
||
const T_SETTLE := 0.35 # 结算排播窗口(行到达后二连抓)
|
||
const T_ROUND_TICK := 0.25 # 停手轮末(敌方攻击弹道/受击闪白 0.2-1.6s 流过)连抓 6 帧的采样步距
|
||
const T_FINAL := 0.6 # 收尾帧前静置
|
||
|
||
var main: Node
|
||
var out_dir := ""
|
||
var _problems: Array = []
|
||
|
||
|
||
func _ready() -> void:
|
||
var args := OS.get_cmdline_user_args()
|
||
out_dir = args[0] if args.size() >= 1 and args[0] != "" else "user://qa-fx"
|
||
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, "fx_shot_probe", false, true, false)
|
||
for i in 8:
|
||
await get_tree().process_frame # UI 装配完成等足帧(canvas_item 空引用窗口)
|
||
var lvbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "开始冒险", "contains")
|
||
if lvbtn == null:
|
||
_fail("开始冒险按钮缺失")
|
||
return
|
||
lvbtn.pressed.emit()
|
||
# 等关卡画面出图(choices_presented 异步到达——最多 3s 轮询)
|
||
for i in 60:
|
||
await get_tree().process_frame
|
||
if main.level_screen != null and main.level_screen.visible:
|
||
break
|
||
if main.level_screen == null or not main.level_screen.visible:
|
||
_fail("关卡画面未出现(lvbtn 路径)")
|
||
return
|
||
for i in 5:
|
||
await get_tree().process_frame
|
||
main.level_screen.press_candidate(0)
|
||
for i in 20:
|
||
await get_tree().process_frame
|
||
var st = main.settlement
|
||
if st == null:
|
||
_fail("首战未开(settlement 空)")
|
||
return
|
||
var rules = main.settlement.minesweeper
|
||
var board = rules.current_board()
|
||
# 自有驱动(只挖/走——测试驱动可用板面数据挑安全格;新手策略档的插旗拒绝会耗尽预算):挖到 3 格停
|
||
var dug := 0
|
||
var guard := 40
|
||
while dug < 3 and guard > 0:
|
||
guard -= 1
|
||
var target = null
|
||
for n in board.neighbors8(rules.hero_pos()):
|
||
if not rules.is_revealed(n) and not board.is_mine(n):
|
||
target = n
|
||
break
|
||
if target != null:
|
||
var out: Array = rules.settle_batch([{"action": "dig", "cell": target}])
|
||
if out.is_empty() or not bool(out[0].get("accepted", false)):
|
||
continue
|
||
dug += 1
|
||
await get_tree().create_timer(T_DIG_FLIGHT).timeout
|
||
await _snap("fx_dig%d" % dug) # 行到达/弹道在飞窗口
|
||
await get_tree().create_timer(T_SETTLE).timeout
|
||
await _snap("fx_settle%d" % dug) # 结算排播窗口
|
||
continue
|
||
# 无相邻可挖 → 走向有未开安全格邻居的已揭开格(挖掘期移动免费)
|
||
var dest = null
|
||
for c in rules.revealed_cells():
|
||
if c == rules.hero_pos() or rules.compute_move_path(c).is_empty():
|
||
continue
|
||
for n in board.neighbors8(c):
|
||
if not rules.is_revealed(n) and not board.is_mine(n):
|
||
dest = c
|
||
break
|
||
if dest != null:
|
||
break
|
||
if dest == null:
|
||
break
|
||
rules.settle_batch([{"action": "move", "cell": dest}])
|
||
# 停手 → 轮末结算(敌方攻击弹道/受击闪白/结算排播在 0.2-1.6s 窗口流过)——连抓 6 帧
|
||
rules.settle_batch([{"action": "stop"}])
|
||
for i in 6:
|
||
await get_tree().create_timer(T_ROUND_TICK).timeout
|
||
await _snap("fx_round%d" % (i + 1))
|
||
if dug == 0:
|
||
_fail("无安全格可挖(首板全雷不可信)")
|
||
return
|
||
await get_tree().create_timer(T_FINAL).timeout
|
||
await _snap("fx_final")
|
||
if _problems.is_empty():
|
||
print("[fx_shot_probe] PASS — 演出帧 %d 张落 %s(挖格 %d)" % [dug * 2 + 1, out_dir, dug])
|
||
get_tree().quit(0)
|
||
else:
|
||
for p in _problems:
|
||
push_error("[fx_shot_probe] FAIL — " + p)
|
||
get_tree().quit(1)
|
||
|
||
|
||
func _snap(tag: String) -> void:
|
||
await RenderingServer.frame_post_draw
|
||
var img: Image = get_viewport().get_texture().get_image()
|
||
if img == null or img.is_empty():
|
||
_problems.append("抓帧空纹理: " + tag)
|
||
return
|
||
img.save_png("%s/%s.png" % [out_dir, tag])
|
||
|
||
|
||
func _fail(msg: String) -> void:
|
||
_problems.append(msg)
|
||
push_error("[fx_shot_probe] FAIL — " + msg)
|
||
get_tree().quit(1)
|