## VERTICAL SLICE - NOT FOR PRODUCTION ## 开发调试浮窗:全局可开(组合根启动即开+两屏角标再开),当局数据只读区块为本局种子唯一查看位。 ## 扩展调试项按区块往下追加(超高滚动);模拟报告文案装配归 HudPresenter——本类零动效零文案装配。 ## 内部数据类 _DebugState=宿主画面登记表(只被本文件填读,文件外零消费者——字段为公开数据成员)。 class_name DebugOverlay extends Control signal apply_requested(atk_pct: int, hp_pct: int) ## 应用并关闭——加成落表归组合根单一应用路径 signal simulate_requested(count: int) ## 批量模拟编排归组合根(表基线复位 + 单路径施加) signal debug_levels_requested # 两层调试对局入口(编排归 debug_session.gd) signal diagnose_requested # 诊断转储入口(卡死取证编排归组合根) signal closed ## 内部状态件(u7 子域拆件——契约 9 职责语句「调试浮窗运行态」):单一变化原因=调试面板状态与 ## 当局数据投影。探针直读冻结成员(seed_edit/_sim_btn/_dbg_btn)住宿主; ## st.run_data/st.run_labels 经 run_data_probe()/update_run_data() 方法出口(字段直访面零外部依赖)。 class _DebugState extends RefCounted: var db var base_atk := 0 var base_hp := 0 var atk: HSlider var hp: HSlider var atk_val: Label var hp_val: Label var eff: Label var sim_count: SpinBox var sim_out: RichTextLabel var sim_running := false # 批量模拟进行中(is_running 判据——与对局中禁用态分离防互扰) var ctx_locked := false # 对局中上下文锁定(组合根 _sync_debug_overlay 喂入——批量模拟/两层调试入口禁用) var run_labels := {} # 当局数据六项标签(只读投影,组合根 update_run_data 喂入) var run_data := {} # 最近一次投喂快照(探针值对面 run_data_probe) var st := _DebugState.new() var seed_edit: LineEdit # 组合根 _seed_value 唯一读取面(探针直读冻结) var _sim_btn: Button # 两层调试批量模拟入口钮(探针直读冻结) var _dbg_btn: Button # 两层调试入口钮(对局中禁用承载面;探针直读冻结) func _init(db_ref, base_atk: int, base_hp: int) -> void: st.db = db_ref st.base_atk = base_atk st.base_hp = base_hp _build() _sync_labels() visible = false # 显式初始化——不靠树序巧合(缺陷⑤家族纪律) # ---- 对外语义面(组合根/探针)---- func atk_pct() -> int: return int(st.atk.value) func hp_pct() -> int: return int(st.hp.value) func set_sliders(atk: int, hp: int) -> void: st.atk.value = atk st.hp.value = hp func reset_sliders() -> void: st.atk.value = 0 st.hp.value = 0 _sync_labels() func effect_text() -> String: return st.eff.text func set_sim_count(n: int) -> void: st.sim_count.value = n func is_running() -> bool: return st.sim_running # 批量模拟进行中(与对局中禁用态分离,置灰 ≠ 在跑) func set_sim_running(running: bool) -> void: st.sim_running = running _sync_sim_button() # 对局中上下文锁定(组合根 _sync_debug_overlay 喂入):批量模拟与两层调试入口 # 在活跃对局并存态不开放(长程异步模拟/并发对局编排与活跃结算并存态破坏状态机) func set_context_locked(locked: bool) -> void: st.ctx_locked = locked _sync_sim_button() # 当局数据只读投影喂入(六项——组合根 _debug_run_data 构造;纯标签重查零动效) func update_run_data(data: Dictionary) -> void: st.run_data = data.duplicate() for key in st.run_labels: (st.run_labels[key] as Label).text = str(data.get(key, "—")) # 当局数据快照读面(探针值对面——ui_tune_probe 断言六项与只读访问器逐项一致) func run_data_probe() -> Dictionary: return st.run_data.duplicate() # 两钮同判据(模拟运行 / 对局上下文锁定对「模拟」「两层调试」两入口等价)—— # 判据单点:两 setter 都经此同步,禁在调用点各写一份 func _sync_sim_button() -> void: _sim_btn.disabled = st.sim_running or st.ctx_locked _sim_btn.text = "模拟中…" if st.sim_running else "运行模拟" _dbg_btn.disabled = st.sim_running or st.ctx_locked func set_sim_progress(text: String) -> void: st.sim_out.text = text func set_sim_report(text: String) -> void: st.sim_out.text = text func output_text() -> String: return st.sim_out.get_parsed_text() # ---- 内部(信号 → 外发)---- func _apply_btn_pressed() -> void: apply_requested.emit(atk_pct(), hp_pct()) func _reset_btn_pressed() -> void: reset_sliders() func effective_atk() -> int: return UiKit.scaled(st.base_atk, atk_pct()) func effective_max_hp() -> int: return UiKit.scaled(st.base_hp, hp_pct()) # ---- 内部构建(标题/关闭固定于浮窗骨架,可扩展列表区居中滚动)---- func _build() -> void: var shell: Dictionary[Variant, Variant] = UiKit.modal_shell(self, UiKit.BG_OVERLAY, Vector2(640, 0), 8) var box: VBoxContainer = shell.box _build_intro(box) # 可扩展列表页(后续调试项按区块往下追加——超高滚动,滚轮下探) var scroll := ScrollContainer.new() scroll.custom_minimum_size = Vector2(0, 430) scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED box.add_child(scroll) var list := VBoxContainer.new() list.size_flags_horizontal = Control.SIZE_EXPAND_FILL list.add_theme_constant_override("separation", 8) scroll.add_child(list) _build_run_data(list) _build_fx_row(list) _build_seed_row(list) _build_slider_block(list) _build_sim_block(list) _build_debug_diag(list) var close_btn := UiKit.button("关闭") close_btn.pressed.connect(_emit_closed) box.add_child(close_btn) ## 头部域:标题+说明(百分比叠加基础值——影响所有模式,不落盘) func _build_intro(box: VBoxContainer) -> void: var title := UiKit.label("调试",UiKit.FS_TITLE, true, UiKit.WARM) box.add_child(title) var desc := UiKit.label( "开发调试专用(UI 最顶层「调试」浮层角标随时可开——任何界面不遮挡)。当局数据=本局只读投影,种子即本局种子唯一查看位。\n百分比叠加基础值:攻击 %d / 生命 %d。影响所有模式(手动与模拟),不落盘。" % [st.base_atk, st.base_hp], 13, false, UiKit.WARM_SOFT) box.add_child(desc) box.add_child(HSeparator.new()) ## 区块·当局数据(六项只读投影:种子=本局种子唯一查看位〔adventure ## .master_seed 现值,非 _seed_value() 重算〕/关卡/节点/HP/GP/钥石;数据由组合根开窗与模态开合 ## 时 update_run_data 喂入;纯标签零信号连接零动效) func _build_run_data(list: VBoxContainer) -> void: var run_head := UiKit.label("当局数据(只读)",UiKit.FS_HINT, true, UiKit.WARM) list.add_child(run_head) var run_grid := GridContainer.new() run_grid.columns = 2 run_grid.add_theme_constant_override("h_separation", 14) run_grid.add_theme_constant_override("v_separation", 2) list.add_child(run_grid) for item in [["seed", "种子"], ["level", "关卡"], ["node", "节点"], ["hp", "HP"], ["gp", "GP"], ["keystone", "钥石"]]: var cap := UiKit.label(String(item[1]),UiKit.FS_HINT, false, UiKit.WARM_FAINT) cap.custom_minimum_size = Vector2(52, 0) run_grid.add_child(cap) var val := UiKit.label("—",UiKit.FS_HINT, false, UiKit.WARM) st.run_labels[item[0]] = val run_grid.add_child(val) list.add_child(HSeparator.new()) ## 区块⓪胜利演出风格(两套并存——纹章拼合[默认]/尘金粒子;会话级不落盘) func _build_fx_row(list: VBoxContainer) -> void: var fx_row := HBoxContainer.new() fx_row.add_theme_constant_override("separation", 10) list.add_child(fx_row) fx_row.add_child(UiKit.label("胜利演出",UiKit.FS_HINT, false, UiKit.WARM)) var fx_btn := UiKit.button("纹章拼合" if UiKit.victory_fx_style == "crest" else "尘金粒子") fx_btn.tooltip_text = "两套演出并存——点击切换(会话级,不落盘)" fx_btn.pressed.connect(_on_fx_toggle.bind(fx_btn)) # lambda 转命名方法(只读巡检白名单方法名精确可判) fx_row.add_child(fx_btn) ## 区块①随机种子(种子三分判定归组合根 _seed_value 单点): ## 默认空框(空=随全局规则:首局恒 42/非首局随机;显式填入全场景优先) func _build_seed_row(list: VBoxContainer) -> void: var seed_row := HBoxContainer.new() list.add_child(seed_row) seed_row.add_child(UiKit.label("随机种子",UiKit.FS_BODY, false)) seed_edit = LineEdit.new() seed_edit.text = "" seed_edit.placeholder_text = "留空=随机 · 首局恒 42" seed_edit.custom_minimum_size = Vector2(120, 0) seed_row.add_child(seed_edit) list.add_child(HSeparator.new()) ## 区块②角色数值调整板(攻击/生命两滑轨+生效值行+应用/重置钮) func _build_slider_block(list: VBoxContainer) -> void: var atk_row := _slider_row(list, "攻击加成") st.atk = atk_row.slider st.atk_val = atk_row.value_label var hp_row := _slider_row(list, "生命加成") st.hp = hp_row.slider st.hp_val = hp_row.value_label st.eff = UiKit.label("",UiKit.FS_HINT, false) list.add_child(st.eff) var btn_row := HBoxContainer.new() list.add_child(btn_row) var apply_btn := UiKit.button("应用并关闭") apply_btn.pressed.connect(_apply_btn_pressed) btn_row.add_child(apply_btn) var reset_btn := UiKit.button("重置为 0") reset_btn.pressed.connect(_reset_btn_pressed) btn_row.add_child(reset_btn) list.add_child(HSeparator.new()) ## 区块③批量模拟(与 headless 扫描同口径——编排归 tune_sim_controller) func _build_sim_block(list: VBoxContainer) -> void: var sim_row := HBoxContainer.new() list.add_child(sim_row) sim_row.add_child(UiKit.label("批量模拟第一关",UiKit.FS_BODY, false)) st.sim_count = SpinBox.new() st.sim_count.min_value = 10 st.sim_count.max_value = 200 st.sim_count.step = 10 st.sim_count.value = 30 st.sim_count.custom_minimum_size = Vector2(90, 0) sim_row.add_child(st.sim_count) sim_row.add_child(UiKit.label("次(理性玩家策略,含面板当前加成)",UiKit.FS_HINT, false)) _sim_btn = UiKit.button("运行模拟") _sim_btn.pressed.connect(_emit_simulate_requested) sim_row.add_child(_sim_btn) st.sim_out = RichTextLabel.new() st.sim_out.bbcode_enabled = true st.sim_out.custom_minimum_size = Vector2(0, 300) list.add_child(st.sim_out) list.add_child(HSeparator.new()) ## 区块④⑤两层调试入口与诊断转储(对局中禁用;转储编排经信号外发归组合根——纯视图边界; ## dump_diagnosis 卡死取证功能保留,不设门控隐藏——本浮窗本就可全局可开,双轨无意义) func _build_debug_diag(list: VBoxContainer) -> void: _dbg_btn = UiKit.button("调试 · 连续关卡两层(HP 关内延续)") _dbg_btn.pressed.connect(_emit_debug_levels) list.add_child(_dbg_btn) var diag_row := HBoxContainer.new() diag_row.add_theme_constant_override("separation", 10) list.add_child(diag_row) diag_row.add_child(UiKit.label("诊断转储",UiKit.FS_BODY, false)) var diag_btn := UiKit.button("转储") diag_btn.tooltip_text = "卡死取证:全量核心状态+环形缓冲 → user://logs/diag_*.json" diag_btn.pressed.connect(_emit_diagnose_requested) diag_row.add_child(diag_btn) func _slider_row(box: VBoxContainer, title: String) -> Dictionary: var row := HBoxContainer.new() box.add_child(row) row.add_child(UiKit.label(title,UiKit.FS_BODY, false)) var s := HSlider.new() s.min_value = 0 s.max_value = 300 s.step = 5 s.custom_minimum_size = Vector2(320, 24) # 皮肤走全局主题(与设置屏音量滑轨同一构建方式,消双轨) row.add_child(s) var v := UiKit.label("+0%",UiKit.FS_BODY, true) v.custom_minimum_size = Vector2(70, 0) row.add_child(v) s.value_changed.connect(_on_slider_value_changed) return {"slider": s, "value_label": v} func _sync_labels() -> void: st.atk_val.text = "+%d%%" % atk_pct() st.hp_val.text = "+%d%%" % hp_pct() st.eff.text = "生效值:攻击 %d → %d · 生命 %d → %d" % [ st.base_atk, effective_atk(), st.base_hp, effective_max_hp()] func _emit_simulate_requested() -> void: simulate_requested.emit(int(st.sim_count.value)) func _emit_diagnose_requested() -> void: diagnose_requested.emit() func _emit_debug_levels() -> void: debug_levels_requested.emit() func _emit_closed() -> void: closed.emit() func _on_slider_value_changed(_value: float) -> void: _sync_labels() # 胜利演出切换(命名方法,只读巡检白名单承载) func _on_fx_toggle(btn: Button) -> void: UiKit.victory_fx_style = "dust" if UiKit.victory_fx_style == "crest" else "crest" btn.text = "纹章拼合" if UiKit.victory_fx_style == "crest" else "尘金粒子"