Files
gplan-2/game/smoke/t17_defer_probe.gd
T
DKChan 7a0d57ddb0 feat(迭代27 T1): 波一 场景拆分+局生命周期物化——逐位一致对拍全同
P0-1 三屏场景化(契约2 只拆不重写):
- menu_screen/camp_screen/adventure_screen 三 .tscn 屏壳+壳脚本(视图代码原样构建,
  40+ views 零 .tscn 化);adventure 屏壳内含 level_map/battle 两容器
- main.gd _build_ui 解体=三屏实例化+模态装配+接线;组合根句柄全部保留(探针挂载
  假设 main.X 一级路径零迁移);树序=屏壳在前全模态之下(视觉共存对逐对验算)
- 屏壳根 mouse_filter=IGNORE(ui_input 实证:全屏 STOP 壳吃下层模态鼠标输入)

P0-2 会话物化(裁点①甲 autoload+手工清单消亡):
- run_scope.gd 节点持全部局态(adventure/runner/settlement/7+ 局态旗/账本);
  _begin_session 建会话、终局整节点 queue_free()——_reset_run_scope 三项+
  _on_battle_end_continue 置空四件手工清单删除
- SaveManager/MetaProgression 升格 autoload 3/4 号位(类名加 Core 后缀沿 RngService
  先例;--script 模式编译器不解析裸 autoload 标识符——组合根留读视图属性 get_node 形态)
- 探针隔离改 attach_path 原地重置(set_master_seed 先例);10 探针+pacing 测试迁移

验收(V1 核心=逐位一致对拍):
- parity_anchor 三变体 base/attune/shop×40 局 RUNSUM+JSONL 双面逐位全同(零行为漂移)
- 冒烟 1055→1077/0(+V2 屏壳独立实例化 11 断言+V3 两连局复位 11 断言)
- run_all 25 套+probe click 全绿+render_gate golden 0.00%×3
2026-10-14 20:28:53 +08:00

124 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
# 演播后排窗定点探针(T17 用户令「伤害过程演完才切战斗胜利结算窗」——对应本改动的唯一关联测试):
# 真实信号路径驱动一场战斗到击杀胜利(结算行在排播队列),断言两条:
# ①胜利同帧:终局面未开 + 挂起态在案 + 队列非空(旧行为=同帧 flush+开窗,本断言即红——判别性);
# ②排播放完:终局面开启(演播完边沿派发——_on_pump_ended)。
# 存档/设置/元进度三面隔离(fx_shot_probe 同款),零真实档触碰。headless 可跑(不抓帧)。
extends Node
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
for p0 in ["user://t17_defer_probe.json", "user://t17_defer_probe_settings.cfg"]:
if FileAccess.file_exists(p0):
DirAccess.remove_absolute(p0)
var iso_c = load("res://core/save_manager.gd").new("user://t17_defer_probe.json")
iso_c.load_from_disk()
main.save_manager = iso_c
main.meta = load("res://core/meta_progression.gd").new(main.db, iso_c)
main.settings_store = load("res://presentation/settings_store.gd").new("user://t17_defer_probe_settings.cfg")
# 本探针验证完整动画档的演播后排窗——强制 motion_scale=1.0(隔离档替换在 boot 后,
# 减动效已随真档 settings.cfg 应用于引擎态;减动效档=consume_now 快路、开窗即时,另属语义)
UiKit.motion_scale = 1.0
for i in 8:
await get_tree().process_frame
var lvbtn: Button = _find_button_by_sub(main._start_overlay, "开始冒险")
if lvbtn == null:
_fail("开始冒险按钮缺失")
return
lvbtn.pressed.emit()
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://smoke/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