184 lines
5.9 KiB
GDScript
184 lines
5.9 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 实现 GDD:design/ux/battle.md(D 区 AP 条 44px——点阵+数字〔pattern 5 点阵资源显示〕、
|
||
# 停手按钮居左、图例+弃图刷新按钮居右〔雷图右上角〕;弃图刷新确认微交互与常规确认不同位
|
||
# 〔pattern 7 破坏性确认异位:RAP 扣费 · 旧板旗作废显式代价〕)。
|
||
# Date: 2026-09-12
|
||
# 高内聚边界:纯视图——意图经信号外发(stop_pressed/refresh_confirmed/legend_toggled),
|
||
# 状态由组合根经 setter 注入;刷新可用性预判 = RAP≥8 读数(行动经济 无推送信号——每次调用返回后重查)。
|
||
class_name ApBarView
|
||
extends PanelContainer
|
||
|
||
signal stop_pressed
|
||
signal refresh_confirmed
|
||
signal legend_toggled
|
||
|
||
const COL_GOLD := UiKit.GOLD
|
||
const COL_DOT_OFF := UiKit.DOT_OFF
|
||
const COL_INK := UiKit.INK
|
||
|
||
var _dots: HBoxContainer
|
||
var _ap_label: Label
|
||
var _btn_stop: Button
|
||
var _btn_legend: Button
|
||
var _btn_refresh: Button
|
||
var _refresh_sub: Label
|
||
var _confirm_row: HBoxContainer
|
||
var _confirm_lab: Label
|
||
var refresh_cost: int = 0 # 表值驱动(装配经 set_refresh_cost 注入 ActionEconomyTable.rap_refresh_cost)
|
||
var _confirm_armed := false
|
||
var _arm_tween: Tween
|
||
|
||
|
||
func _init() -> void:
|
||
custom_minimum_size = Vector2(0, 44)
|
||
var sb := UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, 0, 1, Vector4(8, 4, 8, 4))
|
||
add_theme_stylebox_override("panel", sb)
|
||
|
||
var row := HBoxContainer.new()
|
||
row.add_theme_constant_override("separation", 8)
|
||
add_child(row)
|
||
|
||
_dots = HBoxContainer.new()
|
||
_dots.add_theme_constant_override("separation", 3)
|
||
_dots.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
row.add_child(_dots)
|
||
_ap_label = UiKit.label("AP 0/0", 16, true)
|
||
_ap_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
row.add_child(_ap_label)
|
||
|
||
_btn_stop = UiKit.button("停手")
|
||
_btn_stop.custom_minimum_size = Vector2(64, 0)
|
||
_btn_stop.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
_btn_stop.pressed.connect(func(): stop_pressed.emit())
|
||
row.add_child(_btn_stop)
|
||
|
||
var spacer := Control.new()
|
||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
row.add_child(spacer)
|
||
|
||
# 破坏性确认异位(pattern 7):确认 chips 出现在刷新按钮左方——与常规"按钮自身确认"不同位
|
||
_confirm_row = HBoxContainer.new()
|
||
_confirm_row.add_theme_constant_override("separation", 4)
|
||
_confirm_row.visible = false
|
||
row.add_child(_confirm_row)
|
||
_confirm_lab = UiKit.label("RAP−%d · 旧板旗作废" % refresh_cost, 14, true)
|
||
_confirm_lab.add_theme_color_override("font_color", COL_INK)
|
||
_confirm_row.add_child(_confirm_lab)
|
||
var confirm_btn := UiKit.button("确认弃图")
|
||
confirm_btn.pressed.connect(_disarm_and_fire)
|
||
_confirm_row.add_child(confirm_btn)
|
||
|
||
_btn_legend = UiKit.button("图例")
|
||
_btn_legend.custom_minimum_size = Vector2(56, 0)
|
||
_btn_legend.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
_btn_legend.pressed.connect(func(): legend_toggled.emit())
|
||
row.add_child(_btn_legend)
|
||
|
||
var refresh_box := VBoxContainer.new()
|
||
refresh_box.add_theme_constant_override("separation", 0)
|
||
refresh_box.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
row.add_child(refresh_box)
|
||
_btn_refresh = UiKit.button("弃图刷新")
|
||
_btn_refresh.pressed.connect(_on_refresh_pressed)
|
||
refresh_box.add_child(_btn_refresh)
|
||
_refresh_sub = UiKit.label("RAP−%d · 剩 0 次" % refresh_cost, 14, false)
|
||
_refresh_sub.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
refresh_box.add_child(_refresh_sub)
|
||
|
||
|
||
# ---- setter 面(组合根每次核心调用返回后重查注入——行动经济 无推送信号) ----
|
||
|
||
func set_ap(ap: int, ap_max: int) -> void:
|
||
while _dots.get_child_count() < ap_max:
|
||
_dots.add_child(_make_dot())
|
||
while _dots.get_child_count() > ap_max:
|
||
var last: Control = _dots.get_child(_dots.get_child_count() - 1)
|
||
_dots.remove_child(last)
|
||
last.queue_free()
|
||
for i in _dots.get_child_count():
|
||
var d: ColorRect = _dots.get_child(i)
|
||
d.color = COL_GOLD if i < ap else COL_DOT_OFF
|
||
_ap_label.text = "AP %d/%d" % [ap, ap_max]
|
||
|
||
|
||
func set_refresh_cost(cost: int) -> void:
|
||
refresh_cost = cost
|
||
_confirm_lab.text = "RAP−%d · 旧板旗作废" % cost
|
||
|
||
|
||
func set_refresh_state(remaining: int, ready: bool) -> void:
|
||
_refresh_sub.text = "RAP−%d · 剩 %d 次" % [refresh_cost, remaining]
|
||
_btn_refresh.disabled = not ready
|
||
if not ready:
|
||
_disarm()
|
||
|
||
|
||
func set_stop_enabled(on: bool) -> void:
|
||
_btn_stop.disabled = not on
|
||
|
||
|
||
func set_legend_enabled(on: bool) -> void:
|
||
_btn_legend.disabled = not on
|
||
|
||
|
||
# 探针/装配复用:刷新按钮可用性(预判 = RAP 足额 + 上限未到)
|
||
func is_refresh_ready() -> bool:
|
||
return not _btn_refresh.disabled
|
||
|
||
|
||
# 探针接缝(ADR-0004"合成输入走真实翻译出口"口径):刷新按钮点击 → 确认 chips 全链路
|
||
func press_refresh_for_probe() -> void:
|
||
_on_refresh_pressed()
|
||
|
||
|
||
func confirm_visible() -> bool:
|
||
return _confirm_row.visible
|
||
|
||
|
||
func press_confirm_for_probe() -> void:
|
||
if _confirm_armed:
|
||
_disarm_and_fire()
|
||
|
||
|
||
func refresh_remaining_text() -> String:
|
||
return _refresh_sub.text
|
||
|
||
|
||
# ---- 确认微交互(pattern 7 异位:常规确认 = 按钮自身;本确认 = 左侧独立 chips) ----
|
||
|
||
func _on_refresh_pressed() -> void:
|
||
if _btn_refresh.disabled:
|
||
return
|
||
_confirm_row.visible = true
|
||
_confirm_armed = true
|
||
if _arm_tween != null and _arm_tween.is_valid():
|
||
_arm_tween.kill()
|
||
_arm_tween = create_tween()
|
||
_arm_tween.tween_interval(4.0)
|
||
_arm_tween.tween_callback(_disarm)
|
||
|
||
|
||
func _disarm_and_fire() -> void:
|
||
_disarm()
|
||
refresh_confirmed.emit()
|
||
|
||
|
||
func _disarm() -> void:
|
||
_confirm_armed = false
|
||
_confirm_row.visible = false
|
||
|
||
|
||
# 资源面拒绝反馈(pattern 1 resource_face 腿):AP 数字脉冲
|
||
func pulse_ap() -> void:
|
||
var tw := create_tween()
|
||
tw.tween_property(_ap_label, "modulate", Color(1.0, 0.4, 0.3), UiKit.motion(0.1))
|
||
tw.tween_property(_ap_label, "modulate", Color(1, 1, 1), UiKit.motion(0.25))
|
||
|
||
|
||
func _make_dot() -> ColorRect:
|
||
var d := ColorRect.new()
|
||
d.custom_minimum_size = Vector2(10, 10)
|
||
d.color = COL_DOT_OFF
|
||
d.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
return d
|