265 lines
9.4 KiB
GDScript
265 lines
9.4 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 _arm_timer := ArmedTimer.new() # 弃图刷新确认窗(T3/D3 P4 乙——tween 秒表机制收口;窗口=REFRESH_CONFIRM_WINDOW_S)
|
||
|
||
|
||
const REFRESH_CONFIRM_WINDOW_S := 4.0 # 弃图刷新确认窗(原 tween_interval 字面量升常量——语义零变化)
|
||
|
||
|
||
func _init() -> void:
|
||
custom_minimum_size = Vector2(0, 44)
|
||
_arm_timer.expired.connect(_disarm)
|
||
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: Dot = _dots.get_child(i)
|
||
d.lit = i < ap
|
||
d.queue_redraw()
|
||
_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: Dot = _rap_dots.get_child(i)
|
||
d.lit = i < shown
|
||
d.queue_redraw()
|
||
|
||
|
||
# 探针/装配复用:刷新按钮可用性(预判 = 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 _arm_timer.is_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
|
||
_arm_timer.arm(REFRESH_CONFIRM_WINDOW_S)
|
||
|
||
|
||
func _disarm_and_fire() -> void:
|
||
_disarm()
|
||
refresh_confirmed.emit()
|
||
|
||
|
||
func _disarm() -> void:
|
||
_arm_timer.disarm()
|
||
_confirm_row.visible = false
|
||
|
||
|
||
# 帧驱动(T3/D3——超时回落走 ArmedTimer;unarmed 零开销直返)
|
||
func _process(delta: float) -> void:
|
||
_arm_timer.tick(delta)
|
||
|
||
|
||
# 资源面拒绝反馈(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) -> Dot:
|
||
var d := Dot.new(px, shrink_center)
|
||
return d
|
||
|
||
|
||
# M8(UI 落码批):点阵样式件——方点+亮心(像素宝石观感 demo ⑦);AP=烛光黄族 / RAP=小人蓝族
|
||
class Dot:
|
||
extends Control
|
||
var px := 10.0
|
||
var lit := false
|
||
var lit_color := Color8(246, 198, 103)
|
||
var core_color := Color8(248, 226, 168)
|
||
var off_color := UiKit.DOT_OFF
|
||
func _init(p: float, rap := false) -> void:
|
||
px = p
|
||
custom_minimum_size = Vector2(p, p)
|
||
size_flags_vertical = Control.SIZE_SHRINK_CENTER if rap else Control.SIZE_FILL
|
||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
if rap:
|
||
lit_color = Color8(105, 161, 232)
|
||
core_color = Color8(156, 194, 240)
|
||
func _draw() -> void:
|
||
draw_rect(Rect2(Vector2.ZERO, Vector2(px, px)), lit_color if lit else off_color)
|
||
if lit:
|
||
draw_rect(Rect2(Vector2(px * 0.28, px * 0.28), Vector2(px * 0.3, px * 0.3)), core_color)
|
||
|
||
|
||
func _emit_stop_pressed() -> void:
|
||
stop_pressed.emit()
|
||
|
||
|
||
func _emit_legend_toggled() -> void:
|
||
legend_toggled.emit()
|