Files
gplan-2/game/presentation/views/hero_info_panel_view.gd
T
DKChan 4b174877be 补提交(布局几何单点化批·代码面+工具面+architecture行·跨会话遗留): battle_layout.tres 新表单行五字段(stage_h/info_panel_w/ap_bar_h/log_band_h_dig/log_band_h_settle)经 schema battle_layout_table.gd+域容器 config_domain_presentation.gd 收进 ConfigDb.presentation(ConfigDB 三容器→四容器,manifest 增 battle_layout 键,B区高度裁决 2026-10-30 用户裁=取代码标准 stage_h);views 五件构造签名增 layout 参(BattleStageView/HeroInfoPanelView/BoardZoneView/ApBarView/BattleLogView 常量高度改表值驱动)+ui_assembler 装配传 layout+probes 两件改址+main.gd 结算带注释去数值;tools/layout_config.py 新件+render_sample_sheet 两版读布局表现算(样张工具面);文档面: architecture.md ConfigDB 行换代(四容器成员 7/7/5/1、类变量 10)+HUD框架.md 三区表/装板不等式/调参表去数值化改指布局表+数据配置.md+interaction-patterns.md 明细带句去数值(battle.md 区表与待办清单登记面已随 1cdef6ba 在库);消费漏网 L1 就地补改九处(构造签名换型与 iter45 断点件收编的测试改址漏网,判据零变化): theme_projection_test:386/player_copy_sweep_test:44,98/adventure_feats_test:772/standee_fx_sweep_test:75/feat_vouchers_test:292 六处补 layout 实参,adventure_feats_test:791/level_shop_economy_test:1480/feat_vouchers_test:81 三处断点成员直读改经 a._bp_store(iter45卡②六成员搬移漏网,02 闸合回后首全量暴露);load_tables 超函数长键清偿(容器装配块抽 _assign_loaded 私有协函数——表行按域收进四容器+resolver 注入单一职责,与 _cross_validate 委托先例同型,S13 新增红修至基线口径内);窄验: 四件修复套件单跑全绿+gdstyle 基线外新违规 0
2026-10-31 05:05:08 +08:00

253 lines
11 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
## 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 "")]