# 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()