Files
gplan-2/game/presentation/views/hero_info_panel_view.gd
T
DKChan c571af226f merge: 奇物概念收口与词族对齐(迭代15②号卡·文档与数据层)
概念层由用户逐轮裁定定稿(《奇物》v1.0→v2.7),本批只做落地对齐。

主要内容:
- 新建 design/gdd/systems/冒险循环/奇物.md v2.7(本概念唯一定义文档)
- 新建 design/research/任务书-奇物词族对齐与渠道分离批-2026-10-05.md
  (loop engineering 八件成文 + 12 条批末验收断言 + 11 条设计契约含 6 条停机级)
- 代码词族对齐:feature_* → FEATURE_KINDS/feature_quota_*/feature_value_*
  (adventure_table.gd 24 字段 + 方法 + 常量,adventure.tres 21 键,值零变更)
- 三个 schema 补概念指路注释;relic_table.gd / adventure_table.gd 概念注释指向奇物.md
- ux/battle.md、level-map.md、HUD框架.md、06-待办清单.md、02-系统清单.md、
  registry/entities.yaml 同步称谓与元信息

六处冲突解法:
- 02-系统清单.md:版本历史整段重编(master v0.1-v1.3 原号复原,奇物系列从 v1.4 起)
- ux/battle.md:取分支侧新称谓,但「魔法道具槽」须写「秘宝槽」(master v1.3 已更名)
- adventure_table.gd:合成并集(取本批改名 + 保留 master SR-1 商店刷新三字段)
- level_map_screen.gd:块1 取 master M5 底栏结构,块2 取本批配色与称谓
- registry/entities.yaml:7 处三方混战,块1-5/7 取 master,块6 取本批
- roadmap.md:迭代 15 两卡并存(①调试 已落籍 / ②奇物 待开工)

合并后自查修正:
- 批量替换误伤 70 处(清图奖励/安全格奖励/关卡大奖励/奖励身份预读 等
  属地图战斗资源域;「奖励道具经济」旧文件名与「迭代 10 奖励重构」属历史留痕)→ 全部回退
- battle.md 历史修订行误改 → 恢复 master 原文
- 奇物.md 5 处死链(../ux/ 少一层、魔法道具.md 已更名)→ 修
- registry 条目 markdown 链接 → 改纯路径(同文件惯例)
- 奇物.md R1 散文段 656 字 → 拆段,现在零结构性告警

验证读数(合并后主区全量复跑):
- 冒烟 755 项 / 0 失败,三闸全过
- ui_input_probe 十六断言全绿
- ui_layout_probe 四区尺寸逐位吻合 battle.md 规格
- doc_link 死链 0;doc_term 与基线同;gdd_lint 零新增
- 旧标识符残留全 0;_status_box 未成死代码,四个投喂键齐全
2026-10-05 07:24:15 +08:00

220 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
# 实现 GDD:design/ux/battle.md(C 区角色信息 240×548——2026-09-26 信息归属拍板精简:
# 攻击(+修正来源角标)/护甲 AC 数据框 + 魔法道具槽(预留)+ 特性列表(预留)。
# HP 条/立绘占位/状态效果槽/RAP 池/GP/钥石退役——HP 与状态唯一展示面 = B 区站桩(同一数据
# hud_presenter 双投喂 B/C 两区的重复展示);RAP 迁 D 区 AP 条(贴弃图刷新消费口);
# GP(中循环)/钥石(大循环)迁出战斗屏)。
# Date: 2026-09-12(2026-09-26 C 区精简批重写)
# 高内聚边界:纯视图 setter 面——装配(含修正文案/AC 投喂)归 hud_presenter.gd;定长槽 + clip_contents
# (缺陷①回归约束)。
class_name HeroInfoPanelView
extends PanelContainer
const COL_INK := UiKit.INK
var _atk_label: Label
var _armor_label: Label
signal aux_slot_clicked(handle: int) # 迭代 7 T4——槽点击(组合根消费:AP 类即用/空间类进选中态)
var item_slots: HBoxContainer # 魔法道具槽(迭代 7 T4 接线——库存条常读+两步交互第一步)
var _slot_panels: Array = [] # 槽 PanelContainer 引用(选中高亮/置灰重刷)
var _selected_handle := 0 # 选中态(0=无;组合根回写维持单源)
var _slot_handles: Array = [] # 槽位→handle 映射(set_aux_inventory 刷新)
var _bonus_section: VBoxContainer # 特性(预留——空态隐藏,接线归大奖励/锻造呈现批)
var _bonus_list: VBoxContainer
var _relic_section: VBoxContainer # 特性持有段(迭代 12 呈现补齐——空态隐藏)
var _relic_list: VBoxContainer
var _board_variant_section: VBoxContainer # 地形变异常驻角标(全局作用域变异首发批——空态隐藏)
var _board_variant_list: VBoxContainer
func _init() -> void:
custom_minimum_size = Vector2(240, 0)
clip_contents = true
# 主面板穿石板戏服(样张 v2 冻结批 T2——纹理本色;数据框仍素面 §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) # 布局充实(2026-09-26 反馈③:区段间距放大)
add_child(col)
col.add_child(UiKit.label("探险者", 17, true))
# 数据行装框(直角细线数据框——battle.md Component Inventory C 区行规格落形;同时消化纵向空白)
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 区既有裁决);终稿样式/槽位数归魔法道具批
col.add_child(UiKit.label("魔法道具", 16, true))
item_slots = HBoxContainer.new()
item_slots.add_theme_constant_override("separation", 10)
col.add_child(item_slots)
for i in 4:
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 即用/进选中态)
aux_slot_clicked.emit(_slot_handles[idx] if idx < _slot_handles.size() else 0))
item_slots.add_child(slot)
_slot_panels.append(slot)
# 特性持有段(迭代 12 呈现补齐 2026-10-04——三选一特性兑付后常驻可读:逐流派聚合行+成型档位;
# 空态整节隐藏。用户试玩反馈「局内奖励选了什么看不到」缺口承载;2026-10-04 二次反馈上移——
# 构筑核心信息优先级高于临时强化,且防 C 区下部裁切不可见)
_relic_section = VBoxContainer.new()
_relic_section.add_theme_constant_override("separation", 4)
_relic_section.visible = false
col.add_child(_relic_section)
_relic_section.add_child(UiKit.label("特性", 16, true))
_relic_list = VBoxContainer.new()
_relic_list.add_theme_constant_override("separation", 2)
_relic_section.add_child(_relic_list)
# 地形变异常驻角标(全局作用域变异首发批 2026-10-04——Boss 图整图型战斗全程可见防中局遗忘;
# 空态整节隐藏=特性段先例;摆位/文案 provisional 归 HUD 框架批与本地化批)
_board_variant_section = VBoxContainer.new()
_board_variant_section.add_theme_constant_override("separation", 4)
_board_variant_section.visible = false
col.add_child(_board_variant_section)
_board_variant_section.add_child(UiKit.label("地形变异", 16, true))
_board_variant_list = VBoxContainer.new()
_board_variant_list.add_theme_constant_override("separation", 2)
_board_variant_section.add_child(_board_variant_list)
# 特性(预留):锻造/大奖励/关卡奖励节点的属性提升——中循环数据因改变战斗数学准入;
# 与战斗内状态效果(B 区站桩,随战斗消散)分区,防「战斗结束被清」误读;空态整节隐藏
_bonus_section = VBoxContainer.new()
_bonus_section.add_theme_constant_override("separation", 4)
_bonus_section.visible = false
col.add_child(_bonus_section)
_bonus_section.add_child(UiKit.label("特性", 16, true))
_bonus_list = VBoxContainer.new()
_bonus_list.add_theme_constant_override("separation", 2)
_bonus_section.add_child(_bonus_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(2026-09-26 二轮拍板补入)——我方 AC 唯一展示面;掷点判定轴受击侧可读性
_armor_label.text = "护甲 AC %d" % ac
# 特性持有段(迭代 12 呈现补齐):lines = 聚合文案行(「魔法 ×2 · 档一」式)——空数组 = 整节隐藏
func set_relic_holdings(lines: Array) -> void:
for c2 in _relic_list.get_children():
_relic_list.remove_child(c2)
c2.queue_free()
for line in lines:
var l2 := UiKit.label(String(line), 16, false, COL_INK)
_relic_list.add_child(l2)
_relic_section.visible = not lines.is_empty()
# 地形变异常驻角标(首发批 2026-10-04):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), 16, 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 _bonus_list.get_children():
_bonus_list.remove_child(c)
c.queue_free()
for line in lines:
var l := UiKit.label(String(line), 16, false, COL_INK) # 字号下限(v3.5 批 B3——正文 ≥16)
_bonus_list.add_child(l)
_bonus_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:
# 对比度修复(v3.5 批 B3 2026-09-30——非粗体 label 默认浅灰对米色数据框仅 1.31:1,
# 实测=ui-redesign 对比报告 §一):显式主墨(对 PANEL_DATA ≈10:1)
var l := UiKit.label("", 16, false, UiKit.INK)
l.clip_text = true
l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
return l
# ---- 魔法道具库存条(迭代 7 T4——C 区四槽常读+两步交互第一步)----
# 投影行 = {handle, glyph, name, used, usable}(组合根组装:字形=道具名首字;数据源 runner 级库存只读面)。
# 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(板级零信息);
# selected=金框高亮(两步交互第一步的可见态)。tooltip=全名+维度语义。
func set_aux_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 = items[i]
_slot_handles.append(int(rec.get("handle", 0)))
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
# M8(UI 落码批):槽内容=手绘像素图标(demo ⑦;glyph 字符→图标键映射,未映射/used/不可用回退字形+条件墨色——T4 分支保留)
var glyph_ch := String(rec.get("glyph", "?"))
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, "")
var itex := UiKit.load_tex(icon_path) if icon_path != "" else null
if itex != null and not used and usable:
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
slot.add_child(tr)
else:
var glyph := UiKit.label(glyph_ch, 20, 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:
glyph.add_theme_color_override("font_color", UiKit.STONE_FAINT)
elif not usable:
glyph.add_theme_color_override("font_color", UiKit.STONE)
slot.add_child(glyph)
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 = "%s%s" % [String(rec.get("name", "")), "(已用)" if used else ("(不可用——当前无有效目标)" if not usable else "")]