Files
gplan-2/game/presentation/views/debug_overlay.gd
T

260 lines
8.3 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 整体吸收退役)
# 开发调试浮窗:入口 = 开始覆盖层右下角「调试」角标(DS_DEBUG=1 门控归组合根)——非主按钮组平级;
# 仅开始菜单可开(浮于菜单之上的 Dialog 形态,不隐藏开始层——与确认弹窗同款;对局中不可达)。
# 列表页形态可扩展:后续调试项按区块往下追加,超高滚动。
# 纯视图边界(规范 1.3):数值应用/批量模拟/对局启动全部经信号外发由组合根承接
#(apply_requested / simulate_requested / debug_levels_requested / closed);
# 模拟进度与报告经 setter 注入——零核心写、不拼报告文案(装配归 HudPresenter)。
# 探针接缝(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 _sim_out: RichTextLabel
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_btn.disabled
func set_sim_running(running: bool) -> void:
_sim_btn.disabled = running
_sim_btn.text = "模拟中…" if 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("调试", 26, 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)
# 区块⓪胜利演出风格(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("胜利演出", 14, false, UiKit.WARM))
var fx_btn := UiKit.button("纹章拼合" if UiKit.victory_fx_style == "crest" else "尘金粒子")
fx_btn.tooltip_text = "两套演出并存——点击切换(会话级,不落盘)"
fx_btn.pressed.connect(func() -> void:
UiKit.victory_fx_style = "dust" if UiKit.victory_fx_style == "crest" else "crest"
fx_btn.text = "纹章拼合" if UiKit.victory_fx_style == "crest" else "尘金粒子")
fx_row.add_child(fx_btn)
# 区块①随机种子(原开始覆盖层种子行迁入——非法定值回退 42 的口径归组合根 _seed_value)
var seed_row := HBoxContainer.new()
list.add_child(seed_row)
seed_row.add_child(UiKit.label("随机种子", 15, false))
seed_edit = LineEdit.new()
seed_edit.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("", 14, 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("批量模拟第一关", 15, 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("次(理性玩家策略,含面板当前加成)", 13, 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 关内延续)
var 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, 15, 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%", 15, 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()