# VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(C 区角色信息 240×548〔底行 520→548——停点前移批 2026-09-23, # 原 A 区顶栏空间并入〕——HP/攻击(+修正)/RAP 直角细线数据框 # 〔修正来源角标〕、状态效果槽 ×4、小人立绘占位;RAP 清零可见反馈〔行动经济 义务〕; # 状态槽空态 = 槽隐藏上移补位〔EC12〕)。 # Date: 2026-09-12 # 高内聚边界:纯视图 setter 面——装配(含修正文案)归 hud_presenter.gd;定长槽 + clip_contents # (缺陷①回归约束)。 class_name HeroInfoPanelView extends PanelContainer const COL_INK := UiKit.INK const COL_GOLD := UiKit.GOLD const COL_DOT_OFF := UiKit.DOT_OFF var _hp_bar: ProgressBar var _hp_label: Label var _atk_label: Label var _rap_dots: HBoxContainer var _rap_label: Label var _rap_feedback: Label var _status_feedback: Label var slot_row: StatusSlotRow var gp_label: Label # 冒险钱包余额常驻位(停点前移批 2026-09-23——原 A 区顶栏迁入 C 区底行) var keystone_label: Label # 钥石余额常驻位(同上——文案装配归 HudPresenter) var _last_rap := 0 # 上次投影值(清零反馈判据——禁解析自身 label 文本当状态) func _init() -> void: custom_minimum_size = Vector2(240, 0) clip_contents = true var sb := UiKit.panel_flat(UiKit.PANEL_HERO, UiKit.PANEL_BORDER, 0, 1, Vector4(8, 6, 8, 6)) add_theme_stylebox_override("panel", sb) var col := VBoxContainer.new() col.add_theme_constant_override("separation", 6) add_child(col) col.add_child(UiKit.label("探险者", 16, true)) _hp_bar = ProgressBar.new() _hp_bar.custom_minimum_size = Vector2(0, 12) _hp_bar.show_percentage = false col.add_child(_hp_bar) _hp_label = _data_line() col.add_child(_hp_label) _atk_label = _data_line() col.add_child(_atk_label) # RAP 框(pattern 5 点阵资源显示——点阵图形 + 数字精确值双承载) var rap_head := HBoxContainer.new() rap_head.add_theme_constant_override("separation", 6) col.add_child(rap_head) _rap_dots = HBoxContainer.new() _rap_dots.add_theme_constant_override("separation", 2) rap_head.add_child(_rap_dots) _rap_label = UiKit.label("RAP 0", 13, true) rap_head.add_child(_rap_label) _rap_feedback = UiKit.label("", 12, false) _rap_feedback.add_theme_color_override("font_color", UiKit.STONE_FAINT) col.add_child(_rap_feedback) col.add_child(UiKit.label("状态效果", 14, true)) slot_row = StatusSlotRow.new() col.add_child(slot_row) _status_feedback = UiKit.label("", 12, false) _status_feedback.add_theme_color_override("font_color", UiKit.STONE_FAINT) col.add_child(_status_feedback) # 小人立绘占位(程序化色块——真实立绘归 战斗表现) var portrait := ColorRect.new() portrait.color = Color(UiKit.HERO_BLUE, 56.0 / 255.0) # 原 Color8(46, 110, 142, 56) portrait.custom_minimum_size = Vector2(0, 120) portrait.size_flags_vertical = Control.SIZE_EXPAND_FILL col.add_child(portrait) var portrait_glyph := UiKit.label("小人立绘占位", 13, false) portrait_glyph.add_theme_color_override("font_color", COL_INK) portrait_glyph.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER portrait_glyph.vertical_alignment = VERTICAL_ALIGNMENT_CENTER portrait_glyph.mouse_filter = Control.MOUSE_FILTER_IGNORE portrait_glyph.set_anchors_preset(Control.PRESET_FULL_RECT) portrait.add_child(portrait_glyph) # 钱包行(停点前移批 2026-09-23:A 区 28px 顶栏退役——GP/钥石余额常驻位迁本面板底行, # 建节点归视图、文案装配归 HudPresenter,句柄经 ui_assembler 透传组合根) var wallet_row := HBoxContainer.new() wallet_row.add_theme_constant_override("separation", 10) col.add_child(wallet_row) gp_label = UiKit.label("金币 0", 15, true) gp_label.add_theme_color_override("font_color", UiKit.GOLD_DEEP) wallet_row.add_child(gp_label) keystone_label = UiKit.label("钥石 0", 15, true) keystone_label.add_theme_color_override("font_color", UiKit.GOLD) keystone_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL keystone_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT wallet_row.add_child(keystone_label) func set_hero(hp: int, max_hp: int) -> void: _hp_bar.max_value = maxi(1, max_hp) _hp_bar.value = hp _hp_label.text = "HP %d / %d" % [hp, max_hp] func set_attack(atk: int, mod_text: String) -> void: # 修正来源角标:mod_text 形如「(+50% 增伤)」——来源 = 效果 display_name(增益状态 投影) _atk_label.text = "攻击 %d %s" % [atk, mod_text] func set_rap(rap: int, cap_hint: int) -> void: var prev := _last_rap _last_rap = rap _rap_label.text = "RAP %d(攒 %d 刷图)" % [rap, cap_hint] _render_rap_dots(rap) if prev > 0 and rap == 0: # battle_over RAP 清零可见反馈(行动经济 D1 义务):数字滚动归零 + 微文案占位 _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 = "" func set_status_effects(effects: Array) -> void: slot_row.set_effects(effects) _status_feedback.text = "" # battle_over:状态槽全清可见反馈(逐槽化光 + 微文案——增益状态×4④) func set_status_cleared_feedback() -> void: slot_row.set_cleared_feedback() _status_feedback.text = "状态随战斗结束消散" func set_idle() -> void: _hp_bar.value = 0 _hp_label.text = "HP —" _atk_label.text = "攻击 —" _last_rap = 0 _render_rap_dots(0) _rap_label.text = "RAP —" slot_row.set_effects([]) func hp_text() -> String: return _hp_label.text func rap_text() -> String: return _rap_label.text # 资源面拒绝反馈(pattern 1 resource_face 腿):RAP 数字脉冲 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 _data_line() -> Label: var l := UiKit.label("", 14, false) l.clip_text = true l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS return l