107 lines
5.4 KiB
GDScript
107 lines
5.4 KiB
GDScript
## VERTICAL SLICE - NOT FOR PRODUCTION
|
|
## 调整板批量模拟控制器:逐次驱动关卡自动策略(与 headless 扫描同口径),进度/报告经面板 setter 注入。
|
|
## 零核心写——表基线复位由组合根在移交前完成,加成施加只走自动策略内部 runner.set_hero_override 单路径。
|
|
## presentation 不 preload smoke:驱动实例由调用方注入(须供与 level_autoplay 同款 run_level 签名),缺省 null=运行时 load 兜底。
|
|
## 逐帧步进:每局恰占一帧——第 0 局在调用点同步段执行,第 i 局(i≥1)在第 i 帧信号发射点执行,收尾恒在第 n 帧发射点;
|
|
## n=0 退化路径为调用点同步收尾零帧。
|
|
## _batch_finished 信号契约:整批收尾放行——发射时点=报告注入与运行态复位之后;消费方=run() 自身收尾放行,
|
|
## 外部 await run(n) 经此挂起至整批完成(协程身份契约)。
|
|
extends RefCounted
|
|
|
|
const SEED_BASE := 4200 # 模拟种子基(与历史实现同源——同种子可回放)
|
|
const PlaytestLogger := preload("res://presentation/playtest_logger.gd") # 试玩日志(同层依赖——运行时 load 收敛为 preload 常量)
|
|
|
|
signal _batch_finished
|
|
|
|
var db
|
|
var panel # 调试浮窗 setter 面:atk_pct/hp_pct/set_sim_running/set_sim_progress/set_sim_report
|
|
var _base_hp := 0 # 报告中的"基础 HP"口径注记值
|
|
var _driver = null # 关卡自动策略驱动(调用方注入;null = 旧行为运行时 load 兜底)
|
|
var _running := false # 运行态(run 全程 true——组合根互查守卫用,与面板置灰解耦)
|
|
var _sim_n := 0 # 本批局数(run 期活成员——批末失效、下次 run 重写)
|
|
var _sim_i := 0 # 已执行局数(步进计数)
|
|
var _sim_stats: Dictionary[Variant, Variant] = {} # stats 六键束(run 期承载)
|
|
var _sim_autoplay = null # 自动策略驱动实例(run 期承载)
|
|
var _sim_logger = null # 共享 JSONL 日志会话(run 期承载)
|
|
var _finished := false # 批完成旗标(_finish_batch 置位——收尾段同步承载)
|
|
|
|
|
|
func _init(db_ref, panel_ref, base_hp: int, driver = null) -> void:
|
|
db = db_ref
|
|
panel = panel_ref
|
|
_base_hp = base_hp
|
|
_driver = driver
|
|
|
|
|
|
# 驱动解析:注入优先;缺省回退运行时 load(旧行为——编译期反向依赖已解除,运行时口径与拆分前逐位一致)
|
|
func _autoplay() -> Variant:
|
|
if _driver != null:
|
|
return _driver
|
|
return load("res://sim/level_autoplay.gd") # 与 headless 扫描同口径的自动策略(P3 兜底路径)
|
|
|
|
|
|
# 运行态查询出口(组合根互查守卫:模拟在跑期禁开调试对局/二次模拟)
|
|
func is_running() -> bool:
|
|
return _running
|
|
|
|
|
|
func run(n: int) -> void:
|
|
_running = true
|
|
# 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/关卡完成全量 JSONL;
|
|
# 一文件多局,run 行携带种子切分局次)
|
|
var logger = PlaytestLogger.new()
|
|
logger.ap_per_round = int(db.run.action_economy.ap_per_round)
|
|
logger.start_session(SEED_BASE)
|
|
panel.set_sim_running(true)
|
|
panel.set_sim_progress("模拟中:0/%d …" % n)
|
|
var stats := {"wins": 0, "dmg_all": [], "hp_left_win": [], "hp_boss": [], "rounds_all": [], "death_nodes": {}}
|
|
var autoplay = _autoplay()
|
|
_sim_n = n
|
|
_sim_i = 0
|
|
_sim_stats = stats
|
|
_sim_autoplay = autoplay
|
|
_sim_logger = logger
|
|
_finished = false
|
|
if n > 0:
|
|
if not panel.get_tree().process_frame.is_connected(_step_frame):
|
|
panel.get_tree().process_frame.connect(_step_frame) # 恰好连接一次(迭代 39 先例同款守卫)
|
|
_step_frame() # 首步同步执行(第 0 局在调用栈内跑完——与 await 形态对齐)
|
|
await _batch_finished
|
|
else:
|
|
_finish_batch() # n=0:循环零进同步收尾,不挂起(与 await 形态一致零帧)
|
|
|
|
|
|
# 逐帧步进回调:i<n 执行一局+进度串+计数推进(每局恰占一帧);i 达 n 次帧断连→收尾→放行
|
|
func _step_frame() -> void:
|
|
if _sim_i < _sim_n:
|
|
_run_one_level(_sim_autoplay, _sim_stats, _sim_i, panel, _sim_logger)
|
|
panel.set_sim_progress("模拟中:%d/%d …" % [_sim_i + 1, _sim_n])
|
|
_sim_i += 1
|
|
else:
|
|
panel.get_tree().process_frame.disconnect(_step_frame) # 先断连防再入
|
|
_finish_batch()
|
|
_batch_finished.emit()
|
|
|
|
|
|
# 整批收尾单点(收尾四语句同帧同相位:close_session→报告→运行态复位——次序与 run() 尾现行形态逐句对齐)
|
|
func _finish_batch() -> void:
|
|
_finished = true
|
|
_sim_logger.close_session()
|
|
panel.set_sim_report(HudPresenter.tune_sim_report(_sim_n, panel.atk_pct(), panel.hp_pct(), _sim_stats, _base_hp))
|
|
panel.set_sim_running(false)
|
|
_running = false
|
|
|
|
|
|
## 单局模拟域(循环体提函数——结果四桶记账;u19 stats 六键同构承载)
|
|
func _run_one_level(autoplay, stats: Dictionary, i: int, panel, logger) -> void: # type-hint: ignore(驱动/面板/日志件鸭子型注入——同 run 构造缝)
|
|
var st = autoplay.run_level(db, SEED_BASE + i, 1, panel.atk_pct(), panel.hp_pct(), false, logger)
|
|
(stats["dmg_all"] as Array).append(st.hero_dmg)
|
|
(stats["rounds_all"] as Array).append(st.rounds)
|
|
if st.result == "victory":
|
|
stats["wins"] = int(stats["wins"]) + 1
|
|
(stats["hp_left_win"] as Array).append(st.hp_left)
|
|
(stats["hp_boss"] as Array).append(st.hp_at_boss)
|
|
elif st.result == "defeat":
|
|
var key := "%d.%s" % [st.defeat_node_index, st.defeat_node_type]
|
|
stats["death_nodes"][key] = int(stats["death_nodes"].get(key, 0)) + 1
|