Files
gplan-2/game/probe/t17_defer_probe.gd
T

118 lines
5.6 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
# 演播后排窗定点探针(规则=「伤害过程演完才切战斗胜利结算窗」——该行为的唯一关联测试):
# 真实信号路径驱动一场战斗到击杀胜利(结算行在排播队列),断言两条:
# ①胜利同帧:终局面未开 + 挂起态在案 + 队列非空(旧行为=同帧 flush+开窗,本断言即红——判别性);
# ②排播放完:终局面开启(演播完边沿派发——_on_pump_ended)。
# 存档/设置/元进度三面隔离(isolate_save 现行事实源,meta 经 autoload 链同源),零真实档触碰。headless 可跑(不抓帧)。
extends Node
const ProbeUiUtil := preload("res://probe/probe_ui_util.gd") # 隔离样板单点
var main
var _problems: Array = []
func _ready() -> void:
await get_tree().process_frame
main = load("res://presentation/main.tscn").instantiate()
add_child(main)
await get_tree().process_frame
# 存档/设置两面隔离(ProbeUiUtil.isolate_save 现行事实源,先删后建防陈旧档泄入)——
# 隔离面走 attach_path 原地重置 autoload 单例:组合根 save_manager/meta 是getter-only
# 读视图,直接赋值无效,隔离必须落在单例上。
ProbeUiUtil.isolate_save(main, "t17_defer_probe", false, true, true) # 末参=swap_save(卡②签名换代——原 refresh_resume 槽位语义退役)
# 本探针验证完整动画档的演播后排窗——强制 motion_scale=1.0(隔离档替换在 boot 后,
# 减动效已随真档 settings.cfg 应用于引擎态;减动效档=consume_now 快路、开窗即时,另属语义)
UiKit.motion_scale = 1.0
for i in 8:
await get_tree().process_frame
main._start_adventure_from_menu() # 函数直调中间态(卡② mn10——开始冒险钮已撤)
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("关卡画面未出现")
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
if main.run_scope.settlement == null:
_fail("战斗未启动")
return
# 直挖至击杀胜利(settle_batch 真实管线——step_row 信号→排播队列,与点击路径同链)
var ms = main.run_scope.settlement.minesweeper
var guard := 200
var t_ap_out := -1 # AP 用完/停手帧
var ap_clock := "" # 墙上时钟时间戳
var settle_clock := ""
while guard > 0 and String(main.run_scope.settlement.battle_result) == "":
guard -= 1
var plan = load("res://sim/auto_player.gd").choose_dig(ms)
if plan.is_empty():
main.run_scope.settlement.settle_batch([{"action": "stop", "cell": Vector2i(-1, -1)}])
await get_tree().process_frame
continue
if plan.hero_cell != ms.hero_pos():
ms.settle_batch([{"action": "move", "cell": plan.hero_cell}])
ms.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
if t_ap_out < 0 and main.run_scope.settlement.action_economy.phase != "Active":
t_ap_out = Time.get_ticks_msec()
ap_clock = _clock_ms()
settle_clock = ap_clock # 触发结算与 AP 用完同帧(核心同步)——同一墙上时刻
await get_tree().process_frame
if String(main.run_scope.settlement.battle_result) != "victory":
_fail("战斗未胜利(result='%s')" % str(main.run_scope.settlement.battle_result))
return
var t_settle := Time.get_ticks_msec()
var _rows_at_victory: int = main._row_pacer.pending_rows().size() # 时间戳①:结算入队完成帧(AP 耗尽/停手→五行段同步跑完→行全在队)
# 断言①胜利同帧:终局面未开 + 挂起在案 + 队列非空(判别性——旧行为同帧开窗即红)
if main._end_overlay.is_open():
_problems.append("胜利同帧终局面已开(演播后排窗失效——同帧 flush 旧路径回潮)")
if main._deferred_modal == null:
_problems.append("胜利同帧挂起态缺失(_deferred_modal == null)")
if main._row_pacer.pending_rows().is_empty():
_problems.append("胜利同帧排播队列已空(结算行未走排播链)")
var _played_rows: int = _rows_at_victory
# 断言②排播放完:终局面开启(20s 守卫——0.22s×行数 ×10 余量)
var waited := 0.0
while not main._end_overlay.is_open() and waited < 20.0:
await get_tree().process_frame
waited += get_process_delta_time()
var t_popup := Time.get_ticks_msec()
var popup_clock := _clock_ms() # 弹窗出现:墙上时钟时间戳
print("[t17_defer] AP点用完: %s" % ap_clock)
print("[t17_defer] 触发结算: %s(与 AP 用完同帧——核心同步,相差 0ms)" % settle_clock)
print("[t17_defer] 弹窗出现: %s(距触发结算 %d ms = 逐行演播时长,行数 %d)" % [popup_clock, t_popup - t_ap_out, _played_rows])
if not main._end_overlay.is_open():
_problems.append("排播放完后终局面未开(%.1fs——演播完边沿派发失守)" % waited)
if _problems.is_empty():
print("[t17_defer] PASS — 胜利同帧挂起+排播完开窗 两断言全过(waited=%.1fs)" % waited)
get_tree().quit(0)
else:
for p in _problems:
print("[t17_defer] FAIL — " + p)
get_tree().quit(2)
func _clock_ms() -> String:
var unix := Time.get_unix_time_from_system()
return "%s.%03d" % [Time.get_datetime_string_from_system(), int(fmod(unix, 1.0) * 1000.0) % 1000]
func _fail(why: String) -> void:
print("[t17_defer] FAIL — " + why)
get_tree().quit(2)
func _find_button_by_sub(root: Node, sub: String) -> Button:
if root is Button and String(root.text).contains(sub):
return root
for c in root.get_children():
var r := _find_button_by_sub(c, sub)
if r != null:
return r
return null