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

209 lines
5.7 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 修复批重构)
# 测试用角色数值调整板(模拟天赋树属性提升——仅测试,正式侧归元进度)。
# 纯视图边界(规范 1.3):本类只承载滑杆 UI 与生效值预览;加成应用 / 批量模拟编排
# 全部经信号外发由组合根承接(apply_requested / simulate_requested / closed);
# 模拟进度与报告经 setter 注入——视图零核心写、零 smoke 脚本依赖、不拼报告文案(装配归 presenter)。
# 探针接缝(ADR-0004"合成输入走真实翻译出口"口径):press_apply_for_probe 等三键 = 按钮真实信号路径。
class_name TunePanel
extends Control
signal apply_requested(atk_pct: int, hp_pct: int) # 应用并关闭——加成落表归组合根单一应用路径
signal simulate_requested(count: int) # 批量模拟编排归组合根(表基线复位 + 单路径施加)
signal closed
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()
# ---- 对外语义面(组合根/探针)----
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 box: VBoxContainer = UiKit.modal_shell(self, UiKit.BG_OVERLAY, Vector2(640, 0), 8).box
var title := UiKit.label("测试用 · 角色数值调整板", 26, true, UiKit.WARM)
box.add_child(title)
var desc := UiKit.label(
"模拟玩家在循环中解锁天赋获得的属性提升(仅测试工具,不落盘)。\n百分比叠加基础值:攻击 %d / 生命 %d。影响所有模式(手动与模拟)。" % [_base_atk, _base_hp],
13, false, UiKit.WARM_SOFT)
box.add_child(desc)
box.add_child(HSeparator.new())
var atk_row := _slider_row(box, "攻击加成")
_atk = atk_row.slider
_atk_val = atk_row.value_label
var hp_row := _slider_row(box, "生命加成")
_hp = hp_row.slider
_hp_val = hp_row.value_label
_eff = UiKit.label("", 14, false)
box.add_child(_eff)
var btn_row := HBoxContainer.new()
box.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)
# 减动效开关已转正迁出(迭代 8 卡④——设置面唯一入口,本面板退役该开关;motion_scale 引擎态由设置面承载)
box.add_child(HSeparator.new())
var sim_row := HBoxContainer.new()
box.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)
box.add_child(_sim_out)
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_closed() -> void:
closed.emit()
func _on_slider_value_changed(_value: float) -> void:
_sync_labels()