Files
gplan-2/game/presentation/views/debug_overlay.gd
T
DKChan af2d834e78 feat(迭代25): UI 全局主题批 T0-T2——全局主题资源挂载+两处局部做法退役+冒烟 V1-V6 断言
- T0: game/assets/theme/main_theme.tres 挂 project.godot gui/theme/custom——
  15 类型全覆盖(V/HScrollBar·ScrollContainer·H/VSlider·TooltipPanel/Label·
  LineEdit·SpinBox·H/VSeparator)+预盖 CheckBox/CheckButton/OptionButton/
  PopupMenu 弹层;default_font 显式指现役同字体(V6 中文字形防御);
  位图生成脚本 tools/gen_theme_icons.py(9 张纯色/几何小位图→assets/art/ui/gen/,
  按 .gitignore 政策本地生成不入库);色值溯源表 game/assets/theme/README.md
- T1: ui_kit.gd 删 tooltip_theme()/_tip_theme 与 styled_slider()、ui_assembler.gd
  删挂载行(旧实现同批删除);settings_overlay 几何内联、debug_overlay 双轨消隐
- T2: 冒烟 _smoke_global_theme() 38 断言(V1 类型覆盖+Button/focus 双红线+
  V2 冻结值 5.49:1+V3 预盖证词+V4 退役零残留+V6 字体防御)
- 引擎事实(4.7.2 源码+默认主题链双查):CheckButton 与 CheckBox 同套
  checked/unchecked 图标名;Slider 无 grabber_pressed 槽;ScrollBar 柄=StyleBox
2026-10-13 21:39:01 +08:00

317 lines
12 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-10-04(迭代 14.5 调试整合批——主菜单三散件(随机种子/角色数值调整板/两层调试入口)收一张调试浮窗,
# 原 views/tune_panel.gd 整体吸收退役)
# 开发调试浮窗:全局可开(迭代 18 卡⑦——DS_DEBUG=1 会话启动即开〔组合根 _ready〕+战斗屏 HUD/关卡地图屏
# 两枚角标再开〔P2 甲,17卡③状态角标同形制〕;遮挡语义=ModalStack 登记制:他模态开时暂隐、关后自动恢复;
# 旧「仅开始菜单可开」门随入口迁宿主退役)。当局数据只读区块=本局种子唯一查看位(T3,P4 甲六项)。
# 列表页形态可扩展:后续调试项按区块往下追加,超高滚动。
# 纯视图边界(规范 1.3):数值应用/批量模拟/对局启动全部经信号外发由组合根承接
#(apply_requested / simulate_requested / debug_levels_requested / closed);
# 当局数据经 update_run_data setter 喂入(只读投影零回读);模拟进度与报告经 setter 注入——
# 零核心写、不拼报告文案(装配归 HudPresenter)。零动效(零 tween/AnimationPlayer——17卡③契约)。
# 探针接缝(ADR-0004"合成输入走真实翻译出口"口径):press_apply_for_probe 等三键 = 按钮真实信号路径。
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 closed
var seed_edit: LineEdit # 组合根 _seed_value 唯一读取面(原开始覆盖层种子行迁入)
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_btn: Button
var _dbg_btn: Button # 两层调试入口钮(迭代 18 卡⑦——对局中禁用承载面)
var _sim_out: RichTextLabel
var _sim_running := false # 批量模拟进行中(is_running 判据——与对局中禁用态分离防互扰)
var _ctx_locked := false # 对局中上下文锁定(组合根 _sync_debug_overlay 喂入——批量模拟/两层调试入口禁用)
var _run_labels := {} # 当局数据六项标签(P4 甲——只读投影,组合根 update_run_data 喂入)
var _run_data := {} # 最近一次投喂快照(探针值对面 run_data_probe)
func _init(db_ref, base_atk: int, base_hp: int) -> void:
_db = db_ref
_base_atk = base_atk
_base_hp = base_hp
_build()
_sync_labels()
visible = false # 显式初始化——不靠树序巧合(缺陷⑤家族纪律)
# ---- 对外语义面(组合根/探针)----
func atk_pct() -> int:
return int(_atk.value)
func hp_pct() -> int:
return int(_hp.value)
func set_sliders(atk: int, hp: int) -> void:
_atk.value = atk
_hp.value = hp
func reset_sliders() -> void:
_atk.value = 0
_hp.value = 0
_sync_labels()
func effect_text() -> String:
return _eff.text
func set_sim_count(n: int) -> void:
_sim_count.value = n
func is_running() -> bool:
return _sim_running # 批量模拟进行中(迭代 18 卡⑦——与对局中禁用态分离,置灰 ≠ 在跑)
func set_sim_running(running: bool) -> void:
_sim_running = running
_sync_sim_button()
# 对局中上下文锁定(迭代 18 卡⑦ T2——组合根 _sync_debug_overlay 喂入):批量模拟与两层调试入口
# 在活跃对局并存态不开放(长程异步模拟/并发对局编排与活跃结算并存态破坏状态机)
func set_context_locked(locked: bool) -> void:
_ctx_locked = locked
_dbg_btn.disabled = _ctx_locked
_sync_sim_button()
# 当局数据只读投影喂入(T3 P4 甲六项——组合根 _debug_run_data 构造;纯标签重查零动效)
func update_run_data(data: Dictionary) -> void:
_run_data = data.duplicate()
for key in _run_labels:
(_run_labels[key] as Label).text = str(data.get(key, "—"))
# 当局数据快照读面(探针值对面——ui_tune_probe 断言六项与只读访问器逐项一致)
func run_data_probe() -> Dictionary:
return _run_data.duplicate()
func _sync_sim_button() -> void:
_sim_btn.disabled = _sim_running or _ctx_locked
_sim_btn.text = "模拟中…" if _sim_running else "运行模拟"
func set_sim_progress(text: String) -> void:
_sim_out.text = text
func set_sim_report(text: String) -> void:
_sim_out.text = text
func output_text() -> String:
return _sim_out.get_parsed_text()
# ---- 探针接缝(真实翻译出口——按钮 pressed 信号路径,非直调组合根)----
func press_apply_for_probe() -> void:
_apply_btn_pressed()
func press_reset_for_probe() -> void:
_reset_btn_pressed()
func press_simulate_for_probe() -> void:
_sim_btn.pressed.emit()
# ---- 内部(信号 → 外发)----
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(_base_atk, atk_pct())
func effective_max_hp() -> int:
return UiKit.scaled(_base_hp, hp_pct())
# ---- 内部构建(标题/关闭固定于浮窗骨架,可扩展列表区居中滚动)----
func _build() -> void:
var shell: Dictionary = UiKit.modal_shell(self, UiKit.BG_OVERLAY, Vector2(640, 0), 8)
var box: VBoxContainer = shell.box
var title := UiKit.label("调试",UiKit.FS_TITLE, true, UiKit.WARM)
box.add_child(title)
var desc := UiKit.label(
"开发调试专用(DS_DEBUG=1 会话:启动即开;战斗屏/地图屏「调试」角标可再开)。当局数据=本局只读投影,种子即本局种子唯一查看位。\n百分比叠加基础值:攻击 %d / 生命 %d。影响所有模式(手动与模拟),不落盘。" % [_base_atk, _base_hp],
13, false, UiKit.WARM_SOFT)
box.add_child(desc)
box.add_child(HSeparator.new())
# 可扩展列表页(后续调试项按区块往下追加——超高滚动,滚轮下探)
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)
# 区块·当局数据(迭代 18 卡⑦ T3 P4 甲——六项只读投影:种子=本局种子唯一查看位〔adventure
# .master_seed 现值,非 _seed_value() 重算〕/关卡/节点/HP/GP/钥石;数据由组合根开窗与模态开合
# 时 update_run_data 喂入;纯标签零信号连接零动效——17卡③只读契约)
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)
_run_labels[item[0]] = val
run_grid.add_child(val)
list.add_child(HSeparator.new())
# 区块⓪胜利演出风格(P-C5 两套并存——纹章拼合[默认]/尘金粒子;会话级不落盘)
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)) # 迭代 18 卡⑦——lambda 转命名方法(只读巡检白名单方法名精确可判)
fx_row.add_child(fx_btn)
# 区块①随机种子(原开始覆盖层种子行迁入——种子三分判定归组合根 _seed_value 单点):
# 默认空框(迭代 18 卡⑦ C1——空=随全局规则:首局恒 42/非首局随机;显式填入全场景优先)
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())
# 区块②角色数值调整板(原 TunePanel 内容原样吸收)
var atk_row := _slider_row(list, "攻击加成")
_atk = atk_row.slider
_atk_val = atk_row.value_label
var hp_row := _slider_row(list, "生命加成")
_hp = hp_row.slider
_hp_val = hp_row.value_label
_eff = UiKit.label("",UiKit.FS_HINT, false)
list.add_child(_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)
var sim_row := HBoxContainer.new()
list.add_child(sim_row)
sim_row.add_child(UiKit.label("批量模拟第一关",UiKit.FS_BODY, false))
_sim_count = SpinBox.new()
_sim_count.min_value = 10
_sim_count.max_value = 200
_sim_count.step = 10
_sim_count.value = 30
_sim_count.custom_minimum_size = Vector2(90, 0)
sim_row.add_child(_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)
_sim_out = RichTextLabel.new()
_sim_out.bbcode_enabled = true
_sim_out.custom_minimum_size = Vector2(0, 300)
list.add_child(_sim_out)
list.add_child(HSeparator.new())
# 区块④两层调试对局入口(原开始覆盖层调试按钮迁入——HP 关内延续;迭代 18 卡⑦——对局中禁用)
_dbg_btn = UiKit.button("调试 · 连续关卡两层(HP 关内延续)")
_dbg_btn.pressed.connect(_emit_debug_levels)
list.add_child(_dbg_btn)
var close_btn := UiKit.button("关闭")
close_btn.pressed.connect(_emit_closed)
box.add_child(close_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) # 皮肤走全局主题(迭代 25:与设置屏音量滑轨同一构建方式,消双轨)
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:
_atk_val.text = "+%d%%" % atk_pct()
_hp_val.text = "+%d%%" % hp_pct()
_eff.text = "生效值:攻击 %d → %d · 生命 %d → %d" % [
_base_atk, effective_atk(), _base_hp, effective_max_hp()]
func _emit_simulate_requested() -> void:
simulate_requested.emit(int(_sim_count.value))
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()
# 胜利演出切换(原 _build 内 lambda——迭代 18 卡⑦ 转命名方法,只读巡检白名单承载)
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 "尘金粒子"