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

240 lines
8.5 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
# 实现 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(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)
# 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)
_rap_feedback.add_theme_color_override("font_color", 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)
_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
# 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 数字脉冲(RAP<8 弃图刷新被拒)
func pulse_rap() -> void:
var tw := create_tween()
tw.tween_property(_rap_label, "modulate", Color(1.0, 0.4, 0.3), UiKit.motion(0.1))
tw.tween_property(_rap_label, "modulate", Color(1, 1, 1), UiKit.motion(0.25))
func _render_rap_dots(rap: int) -> void:
var shown := mini(rap, 16)
while _rap_dots.get_child_count() < shown:
var d := ColorRect.new()
d.custom_minimum_size = Vector2(8, 8)
d.size_flags_vertical = Control.SIZE_SHRINK_CENTER
d.mouse_filter = Control.MOUSE_FILTER_IGNORE
_rap_dots.add_child(d)
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
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