95 lines
6.0 KiB
GDScript
95 lines
6.0 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Date: 2026-09-11(2026-09-12 code-review 修复批重写——加成应用契约改为组合根单一应用路径)
|
||
# 调试浮窗场景探针(2026-10-04 迭代 14.5 调试整合批随批改接——原数值调整板整体收进 debug_overlay,
|
||
# 浮窗形态悬浮于开始菜单之上不隐藏菜单;playtest 缺陷⑤回归载体沿袭):
|
||
# 驱动真实 UI 路径——打开可见性(浮于菜单之上+树序在后)/ 滑条联动 / 应用(apply_requested → 组合根)/
|
||
# 重置 / 批量模拟全链路(N=10)/ E4 回归断言(hero 表运行期不可变 + 单次加成经 encounter 生效)。
|
||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_tune_probe.tscn
|
||
extends Node
|
||
|
||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # T6 收口:隔离样板单点
|
||
|
||
const SIM_GUARD_FRAMES := 1800 # ⑤ N=10 headless 模拟防挂死帧预算上限(ADR-0004:探针内停滞检测第一道——坏态不等满预算才报,超时即断言失败)
|
||
|
||
|
||
func _ready() -> void:
|
||
await get_tree().process_frame
|
||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||
add_child(main)
|
||
await get_tree().process_frame
|
||
var problems: Array = []
|
||
# 基线读表(2026-09-18 随数值压缩批改表值驱动——旧字面量 6/50、12/75 已随 hero 表压缩失效)
|
||
var base_atk := int(main.db.hero.atk)
|
||
var base_hp := int(main.db.hero.max_hp)
|
||
var boosted_atk := base_atk * 2 # 攻击滑条 100 → ×2
|
||
var boosted_hp := int(round(base_hp * 1.5)) # 生命滑条 50 → +50%
|
||
# ① 打开可见性(14.5 浮窗形态:悬浮于菜单之上——开始层不隐藏,树序必须在开始层之后 = 绘制在上;
|
||
# 旧「打开必须隐藏开始层」断言随互斥形态退役)
|
||
main._show_debug_overlay()
|
||
if not main.debug_overlay.visible:
|
||
problems.append("调试浮窗未打开")
|
||
if not main._start_overlay.visible:
|
||
problems.append("开始覆盖层被隐藏——浮窗应悬浮于菜单之上(14.5 形态失守)")
|
||
if main.debug_overlay.get_index() < main._start_overlay.get_index():
|
||
problems.append("调试浮窗树序在开始层之前——遮挡关系失守")
|
||
# ② 滑条 → 生效预览联动
|
||
main.debug_overlay.set_sliders(100, 50)
|
||
if str(boosted_atk) not in main.debug_overlay.effect_text() or str(boosted_hp) not in main.debug_overlay.effect_text():
|
||
problems.append("生效预览未联动(%s)" % main.debug_overlay.effect_text())
|
||
# ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照
|
||
main.debug_overlay.press_apply_for_probe()
|
||
# ④ 重置(重开浮窗 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线)
|
||
main._show_debug_overlay()
|
||
main.debug_overlay.press_reset_for_probe()
|
||
# ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位)
|
||
main.debug_overlay.set_sliders(100, 0)
|
||
main.debug_overlay.set_sim_count(10)
|
||
main.debug_overlay.press_simulate_for_probe()
|
||
var guard := SIM_GUARD_FRAMES
|
||
while main.debug_overlay.is_running() and guard > 0:
|
||
guard -= 1
|
||
await get_tree().process_frame
|
||
if main.debug_overlay.is_running():
|
||
problems.append("模拟未在预算帧内完成(疑似挂死)")
|
||
var out: String = main.debug_overlay.output_text()
|
||
if not out.contains("通关率"):
|
||
problems.append("模拟输出缺通关率")
|
||
if not out.contains("败北节点"):
|
||
problems.append("模拟输出缺败北节点")
|
||
if "N=10" not in out:
|
||
problems.append("模拟输出 N 标注错误(%s)" % out.substr(0, 40))
|
||
# ⑥ 关闭 → 浮窗收起、开始层全程在屏
|
||
main._close_debug_overlay()
|
||
if main.debug_overlay.visible or not main._start_overlay.visible:
|
||
problems.append("关闭后层级状态错误")
|
||
# ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级)
|
||
if int(main.db.hero.atk) != base_atk or int(main.db.hero.max_hp) != base_hp:
|
||
problems.append("hero 表被运行时改写(E4 违例:atk=%s hp=%s,期望恒基线 %d/%d)" % [str(main.db.hero.atk), str(main.db.hero.max_hp), base_atk, base_hp])
|
||
# ⑧ 单次加成生效回归断言:应用加成 + 启动单场战斗 → 呈现快照 hero 必须是"基线 × 单次加成"
|
||
# (攻击 ×2 / 生命 +50%)——旧缺陷(残留值被当基线再乘)在 encounter 参数链上同样被抓获
|
||
# 存档隔离(迭代 8 卡①——停点断点写盘随 main.save_manager;探针禁碰真实档,同族探针隔离纪律;
|
||
# isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退)
|
||
ProbeUiUtil.isolate_save(main, "smoke_tune_probe_save", true, false, false)
|
||
main.debug_overlay.set_sliders(100, 50)
|
||
main.debug_overlay.press_apply_for_probe()
|
||
main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言)
|
||
await get_tree().process_frame
|
||
await get_tree().process_frame
|
||
# 停点前移批 2026-09-23:关卡开始 = 入口选路停点——真实按钮路径选入口候选进首战再读呈现快照
|
||
if main.level_screen.visible:
|
||
main.level_screen.press_candidate(0)
|
||
await get_tree().process_frame
|
||
await get_tree().process_frame
|
||
var hero: Dictionary = main.settlement.presentation_snapshot().hero
|
||
if int(hero.get("atk", -1)) != boosted_atk or int(hero.get("max_hp", -1)) != boosted_hp:
|
||
problems.append("战斗加成单次生效违例(atk=%s max_hp=%s,期望 %d/%d)" % [
|
||
str(hero.get("atk", "?")), str(hero.get("max_hp", "?")), boosted_atk, boosted_hp])
|
||
if problems.is_empty():
|
||
print("[tune_probe] PASS — 打开/联动/应用/重置/模拟/关闭/基线无残留/单次加成 全链路通过(模拟 N=10 输出 %.30s…)" % out)
|
||
get_tree().quit(0)
|
||
else:
|
||
for p in problems:
|
||
push_error("[tune_probe] FAIL — " + p)
|
||
print("[tune_probe] FAIL — " + "; ".join(problems))
|
||
get_tree().quit(1)
|