Files
gplan-2/game/smoke/ui_tune_probe.gd
T

93 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
# Date: 2026-09-11(2026-09-12 code-review 修复批重写——加成应用契约改为组合根单一应用路径)
# 数值调整板场景探针(playtest 缺陷⑤回归载体——面板此前被不透明开始覆盖层遮挡 = "点击无反应"):
# 驱动真实 UI 路径——打开可见性 / 滑条联动 / 应用(apply_requested → 组合根)/
# 重置 / 批量模拟全链路(N=10)/ E4 回归断言(hero 表运行期不可变 + 单次加成经 encounter 生效)。
# 运行:Godot_console.exe --headless --path . res://smoke/ui_tune_probe.tscn
extends Node
func _ready() -> void:
await get_tree().process_frame
var main = 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%
# ① 打开可见性(缺陷⑤回归:打开面板必须隐藏不透明的开始覆盖层)
main._show_tune_panel()
if not main.tune_panel.visible:
problems.append("面板未打开")
if main._start_overlay.visible:
problems.append("开始覆盖层未隐藏——面板被遮挡(缺陷⑤复发)")
# ② 滑条 → 生效预览联动
main.tune_panel.set_sliders(100, 50)
if str(boosted_atk) not in main.tune_panel.effect_text() or str(boosted_hp) not in main.tune_panel.effect_text():
problems.append("生效预览未联动(%s)" % main.tune_panel.effect_text())
# ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照
main.tune_panel.press_apply_for_probe()
# ④ 重置(重开面板 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线)
main._show_tune_panel()
main.tune_panel.press_reset_for_probe()
# ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位)
main.tune_panel.set_sliders(100, 0)
main.tune_panel.set_sim_count(10)
main.tune_panel.press_simulate_for_probe()
var guard := 1800 # N=10 headless 模拟防挂死上限(ADR-0004:探针内停滞检测第一道)
while main.tune_panel.is_running() and guard > 0:
guard -= 1
await get_tree().process_frame
if main.tune_panel.is_running():
problems.append("模拟未在预算帧内完成(疑似挂死)")
var out: String = main.tune_panel.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_tune_panel()
if main.tune_panel.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;探针禁碰真实档,同族探针隔离纪律)
for p0 in ["user://smoke_tune_probe_save.json", "user://smoke_tune_probe_save.json.tmp"]:
if FileAccess.file_exists(p0):
DirAccess.remove_absolute(p0) # 段首清场——上次运行的残留断点会触发互斥确认早退
var iso_tune = load("res://core/save_manager.gd").new("user://smoke_tune_probe_save.json")
iso_tune.load_from_disk()
iso_tune.mark_first_adventure_done()
main.save_manager = iso_tune
main.meta = load("res://core/meta_progression.gd").new(main.db, iso_tune)
main.tune_panel.set_sliders(100, 50)
main.tune_panel.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)