## VERTICAL SLICE - NOT FOR PRODUCTION ## C 区角色信息面板:攻击/AC 数据框+秘宝槽+特性持有段(空态隐藏)。 ## 装配与文案(修正来源角标/AC 投喂)归 HudPresenter——本类零文案装配。 class_name HeroInfoPanelView extends PanelContainer const COL_INK := UiKit.INK var _atk_label: Label var _armor_label: Label signal treasure_slot_clicked(handle: int) ## 槽点击(组合根消费:AP 类即用/空间类进选中态) var item_slots: HBoxContainer # 秘宝槽(库存条常读+两步交互第一步) var _slot_panels: Array[Variant] = [] # 槽 PanelContainer 引用(选中高亮/置灰重刷) var _selected_handle := 0 # 选中态(0=无;组合根回写维持单源) var _slot_handles: Array[Variant] = [] # 槽位→handle 映射(set_treasure_inventory 刷新) var _curio_section: VBoxContainer # 特性(预留——空态隐藏) var _curio_list: VBoxContainer var _feat_section: VBoxContainer # 特性持有段(空态隐藏) var _feat_list: VBoxContainer var _board_variant_section: VBoxContainer # 地形变异常驻角标(空态隐藏) var _board_variant_list: VBoxContainer func _init(layout: BattleLayoutTable) -> void: custom_minimum_size = Vector2(layout.info_panel_w, 0) clip_contents = true # 主面板穿石板戏服(纹理本色;数据框仍素面 §7.1;缺位回退 flat) var sb := UiKit.panel_skinned(Vector4(8, 6, 8, 6)) add_theme_stylebox_override("panel", sb) var col := VBoxContainer.new() col.add_theme_constant_override("separation", 14) # 布局充实(区段间距放大) add_child(col) _build_data_row(col) _build_slots(col) _build_side_sections(col) ## 数据行域(直角细线数据框——battle.md Component Inventory C 区行规格落形;同时消化纵向空白) func _build_data_row(col: VBoxContainer) -> void: col.add_child(UiKit.label("探险者",UiKit.FS_SUB, true)) var data_box := PanelContainer.new() data_box.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, 0, 1, Vector4(6, 4, 6, 4))) col.add_child(data_box) var data_col := VBoxContainer.new() data_col.add_theme_constant_override("separation", 8) data_box.add_child(data_col) _atk_label = _data_line() data_col.add_child(_atk_label) _armor_label = _data_line() data_col.add_child(_armor_label) ## 秘宝槽域(预留):随身战斗用道具的唯一展示面——空槽占位承诺格子在此, ## 不预设交互(挖掘期指向交互归 E 区既有裁决) func _build_slots(col: VBoxContainer) -> void: col.add_child(UiKit.label("秘宝",UiKit.FS_BODY, true)) item_slots = HBoxContainer.new() item_slots.add_theme_constant_override("separation", 10) col.add_child(item_slots) for i in 4: _slot_panels.append(_build_slot(i)) ## 单槽构建域:48px 槽牌+点击路由(空槽/越界槽点击=取消选中 handle 0;有效槽发其 handle) func _build_slot(i: int) -> PanelContainer: var slot := PanelContainer.new() slot.custom_minimum_size = Vector2(48, 48) slot.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, 0, 1, Vector4(4, 4, 4, 4))) var idx := i slot.gui_input.connect(func(ev: InputEvent): if ev is InputEventMouseButton and ev.pressed and ev.button_index == MOUSE_BUTTON_LEFT: # 空槽/越界槽点击=取消选中(handle 0);有效槽发其 handle(组合根判 AP 即用/进选中态) treasure_slot_clicked.emit(_slot_handles[idx] if idx < _slot_handles.size() else 0)) item_slots.add_child(slot) return slot ## 三区段域(特性持有段上移承载——构筑核心信息优先级高于临时强化,且防 C 区下部裁切不可见/ ## 地形变异常驻角标 Boss 图整图型战斗全程可见防中局遗忘/特性预留段防「战斗结束被清」误读;空态整节隐藏) func _build_side_sections(col: VBoxContainer) -> void: _feat_section = _labeled_section(col, "专长") _feat_list = _list_of(_feat_section) _board_variant_section = _labeled_section(col, "地形变异") _board_variant_list = _list_of(_board_variant_section) _curio_section = _labeled_section(col, "属性值提升") _curio_list = _list_of(_curio_section) ## 区段骨架(标题+隐藏容器) func _labeled_section(col: VBoxContainer, title: String) -> VBoxContainer: var section := VBoxContainer.new() section.add_theme_constant_override("separation", 4) section.visible = false col.add_child(section) section.add_child(UiKit.label(title,UiKit.FS_BODY, true)) return section ## 行板容器(separation 2) func _list_of(section: VBoxContainer) -> VBoxContainer: var list := VBoxContainer.new() list.add_theme_constant_override("separation", 2) section.add_child(list) return list func set_attack(atk: int, mod_text: String) -> void: # 修正来源角标:mod_text 形如「(+50% 增伤)」——来源 = 效果 display_name(增益状态 投影) _atk_label.text = "攻击 %d %s" % [atk, mod_text] func set_armor(ac: int) -> void: # 护甲 AC——我方 AC 唯一展示面;掷点判定轴受击侧可读性 _armor_label.text = "护甲 AC %d" % ac # 特性持有段:lines = 逐件文案行(「疼痛记忆 ×2」式)——空数组 = 整节隐藏 func set_feat_holdings(lines: Array) -> void: for c2 in _feat_list.get_children(): _feat_list.remove_child(c2) c2.queue_free() for line in lines: var l2 := UiKit.label(String(line),UiKit.FS_BODY, false, COL_INK) _feat_list.add_child(l2) _feat_section.visible = not lines.is_empty() # 地形变异常驻角标:lines = 文案行(整图变异 display_name+体感短语)——空数组 = 整节隐藏 func set_board_variants(lines: Array) -> void: for c3 in _board_variant_list.get_children(): _board_variant_list.remove_child(c3) c3.queue_free() for line in lines: var l3 := UiKit.label(String(line),UiKit.FS_BODY, false, COL_INK) _board_variant_list.add_child(l3) _board_variant_section.visible = not lines.is_empty() # 特性列表(预留接线面):lines = 文案行数组(如「锻造 +2 攻击」)——空数组 = 整节隐藏 func set_level_bonuses(lines: Array) -> void: for c in _curio_list.get_children(): _curio_list.remove_child(c) c.queue_free() for line in lines: var l := UiKit.label(String(line),UiKit.FS_BODY, false, COL_INK) # 字号下限(正文 ≥16) _curio_list.add_child(l) _curio_section.visible = not lines.is_empty() func attack_text() -> String: return _atk_label.text func armor_text() -> String: return _armor_label.text func _data_line() -> Label: # 非粗体 label 默认浅灰对米色数据框仅 1.31:1(实测=ui-redesign 对比报告 §一): # 显式主墨(对 PANEL_DATA ≈10:1) var l := UiKit.label("",UiKit.FS_BODY, false, UiKit.INK) l.clip_text = true l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS return l # ---- 秘宝库存条(C 区四槽常读+两步交互第一步)---- # 投影行 = {handle, glyph, name, used, usable}(组合根组装:字形=道具名首字;数据源 runner 级库存只读面)。 # 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(整图零信息); # selected=金框高亮(两步交互第一步的可见态)。tooltip=全名+维度语义。 func set_treasure_inventory(items: Array, selected_handle: int) -> void: _selected_handle = selected_handle _slot_handles.clear() for i in 4: var slot: PanelContainer = _slot_panels[i] for c in slot.get_children(): c.queue_free() if i >= items.size(): _slot_handles.append(0) slot.tooltip_text = "秘宝槽(空)" slot.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER, 0, 1, Vector4(4, 4, 4, 4))) continue var rec: Dictionary[Variant, Variant] = items[i] _slot_handles.append(int(rec.get("handle", 0))) _fill_slot(slot, rec, selected_handle) ## 单槽内容填充域:图标优先(像素图标未映射/used/不可用回退字形+条件墨色)+选中金框+悬停浮窗 func _fill_slot(slot: PanelContainer, rec: Dictionary, selected_handle: int) -> void: var used := bool(rec.get("used", false)) var usable := bool(rec.get("usable", true)) var selected := selected_handle != 0 and int(rec.get("handle", 0)) == selected_handle # 槽内容=手绘像素图标(demo ⑦;glyph 字符→图标键映射,未映射/used/不可用回退字形+条件墨色) var itex := _slot_icon_tex(String(rec.get("glyph", "?"))) if itex != null and not used and usable: slot.add_child(_slot_icon_rect(itex)) else: slot.add_child(_slot_glyph_label(String(rec.get("glyph", "?")), used, usable)) var border_col: Color = UiKit.GOLD if selected else UiKit.PANEL_BORDER slot.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, border_col, 0, 1 if not selected else 2, Vector4(4, 4, 4, 4))) slot.tooltip_text = _slot_tooltip(rec, used, usable) ## 字形→图标纹理映射(demo ⑦ 键表单点) func _slot_icon_tex(glyph_ch: String) -> Texture2D: var icon_map := {"爆": UiKit.TEX_ICON_BLAST, "医": UiKit.TEX_ICON_MEDKIT, "铲": UiKit.TEX_ICON_SHOVEL, "袋": UiKit.TEX_ICON_COINBAG, "钥": UiKit.TEX_ICON_KEYSHARD, "剑": UiKit.TEX_ICON_SWORD, "盾": UiKit.TEX_ICON_SHIELD, "心": UiKit.TEX_ICON_HEART, "行": UiKit.TEX_ICON_POUCH} var icon_path: String = icon_map.get(glyph_ch, "") return UiKit.load_tex(icon_path) if icon_path != "" else null ## 图标矩形域(40px 素描框,像素过滤) func _slot_icon_rect(itex: Texture2D) -> TextureRect: var tr := TextureRect.new() tr.texture = itex tr.custom_minimum_size = Vector2(40, 40) tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED tr.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST tr.set_anchors_preset(Control.PRESET_FULL_RECT) tr.mouse_filter = Control.MOUSE_FILTER_IGNORE return tr ## 字形回退标签域(used=灰石深化墨 5.6:1/不可用=标签深墨 10.4:1 虚位失效更强置灰——语义分档双墨) func _slot_glyph_label(glyph_ch: String, used: bool, usable: bool) -> Label: var glyph := UiKit.label(glyph_ch,UiKit.FS_SUB, true) glyph.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER glyph.vertical_alignment = VERTICAL_ALIGNMENT_CENTER glyph.set_anchors_preset(Control.PRESET_FULL_RECT) glyph.mouse_filter = Control.MOUSE_FILTER_IGNORE if used: # 「已用」占位墨=灰石深化(STONE_FAINT 对 PANEL_DATA 槽牌 2.7:1; # STONE_DEEP = 5.6:1;语义分档保持双墨) glyph.add_theme_color_override("font_color", UiKit.STONE_DEEP) elif not usable: # 「不可用」置灰墨=标签深墨(STONE 对槽牌 3.4:1;INK_LABEL = 10.4:1,虚位/失效更强置灰语义) glyph.add_theme_color_override("font_color", UiKit.INK_LABEL) return glyph ## 悬停浮窗域(note=积点进度行——自动兑付件悬停浮窗补「积点 N/M」,进度随挖格经组合根重查刷新) func _slot_tooltip(rec: Dictionary, used: bool, usable: bool) -> String: var note := String(rec.get("note", "")) return "%s%s%s" % [String(rec.get("name", "")), "(%s)" % note if note != "" else "", "(已用)" if used else ("(不可用——当前无有效目标)" if not usable else "")]