242 lines
8.6 KiB
GDScript
242 lines
8.6 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 实现 GDD:design/ux/battle.md(D 区 AP 条 44px——点阵+数字〔pattern 5 点阵资源显示〕、
|
||
# 停手按钮居左、图例+弃图刷新按钮居右〔雷图右上角〕;弃图刷新确认微交互与常规确认不同位
|
||
# 〔pattern 7 破坏性确认异位:RAP 扣费 · 旧板旗作废显式代价〕)。
|
||
# 2026-09-26 信息归属批:RAP 读数(点阵+数字)自 C 区迁入本条、置弃图刷新按钮左邻
|
||
# (RAP 唯一消费口 = 弃图刷新——就近原则;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 _rap_dots: HBoxContainer
|
||
var _rap_label: Label
|
||
var _rap_feedback: Label
|
||
var _last_rap := 0 # 上次投影值(清零反馈判据——禁解析自身 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(_emit_stop_pressed)
|
||
row.add_child(_btn_stop)
|
||
|
||
var spacer := Control.new()
|
||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
row.add_child(spacer)
|
||
|
||
# RAP 读数(2026-09-26 信息归属拍板自 C 区迁入):RAP 唯一消费口 = 弃图刷新——资源数字贴消费按钮
|
||
# (就近原则);扣费文案「RAP−%d」已在刷新按钮副行,此处不重复代价提示(单一展示面原则)
|
||
_rap_dots = HBoxContainer.new()
|
||
_rap_dots.add_theme_constant_override("separation", 2)
|
||
_rap_dots.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
row.add_child(_rap_dots)
|
||
_rap_label = UiKit.label("RAP 0", 14, true)
|
||
_rap_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
row.add_child(_rap_label)
|
||
_rap_feedback = UiKit.label("", 11, false, UiKit.STONE_FAINT)
|
||
_rap_feedback.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
row.add_child(_rap_feedback)
|
||
|
||
# 破坏性确认异位(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, 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(_emit_legend_toggled)
|
||
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, UiKit.INK_LABEL)
|
||
_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
|
||
|
||
|
||
# RAP 活值投影(点阵+数字——pattern 5 双承载);清零反馈分支(行动经济 D1 义务)依赖持续投喂可达
|
||
func set_rap(rap: int) -> void:
|
||
var prev := _last_rap
|
||
_last_rap = rap
|
||
_rap_label.text = "RAP %d" % rap
|
||
_render_rap_dots(rap)
|
||
if prev > 0 and rap == 0:
|
||
# battle_over RAP 清零可见反馈(行动经济 D1 义务):数字脉冲 + 微文案(载体自 C 区迁入)
|
||
_rap_feedback.text = "随战斗结束消散"
|
||
var tw := create_tween()
|
||
tw.tween_property(_rap_label, "modulate", Color(1.4, 1.2, 0.7), UiKit.motion(0.12))
|
||
tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), UiKit.motion(0.3))
|
||
elif rap > 0:
|
||
_rap_feedback.text = ""
|
||
|
||
|
||
# 资源读数拒绝脉冲单点(pattern 1 resource_face 腿——RAP/AP 两份逐字重复合并:朱红脉冲一拍回白)
|
||
func _pulse(label: Label) -> void:
|
||
var tw := create_tween()
|
||
tw.tween_property(label, "modulate", Color(1.0, 0.4, 0.3), UiKit.motion(0.1))
|
||
tw.tween_property(label, "modulate", Color(1, 1, 1), UiKit.motion(0.25))
|
||
|
||
|
||
func pulse_rap() -> void:
|
||
_pulse(_rap_label)
|
||
|
||
|
||
func _render_rap_dots(rap: int) -> void:
|
||
var shown := mini(rap, 16)
|
||
while _rap_dots.get_child_count() < shown:
|
||
_rap_dots.add_child(_make_dot(8.0, true)) # RAP 点阵 8px 窄档(随读数字号)——建点单点共用
|
||
for i in _rap_dots.get_child_count():
|
||
var d: ColorRect = _rap_dots.get_child(i)
|
||
d.color = COL_GOLD if i < shown else COL_DOT_OFF
|
||
|
||
|
||
# 探针/装配复用:刷新按钮可用性(预判 = 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:
|
||
_pulse(_ap_label)
|
||
|
||
|
||
# 点阵建点单点(AP 10px / RAP 8px 两套建点参数化合并;初始色=熄灭档——投影循环当拍覆写)
|
||
func _make_dot(px := 10.0, shrink_center := false) -> ColorRect:
|
||
var d := ColorRect.new()
|
||
d.custom_minimum_size = Vector2(px, px)
|
||
d.size_flags_vertical = Control.SIZE_SHRINK_CENTER if shrink_center else Control.SIZE_FILL
|
||
d.color = COL_DOT_OFF
|
||
d.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
return d
|
||
|
||
|
||
func _emit_stop_pressed() -> void:
|
||
stop_pressed.emit()
|
||
|
||
|
||
func _emit_legend_toggled() -> void:
|
||
legend_toggled.emit()
|