- d1 伤害控制器来源敌作废判定 `==`→`is_same`(同内容异实例不再误废;夹具双敌两断言〔wd1〕) - d11 shop 表新增 relic_base_price/spend_discount 两 knob+域检+ConfigDB 访问器+level_runner 读表; level_shop_economy_test 三表值断言+relic 现价读表与迁移前字面量逐位等价〔wd11〕 - d16a HeroTable.display_name+域检+访问器+unit_container 读表;spawn 真实链断言〔wd16〕 - d14 board_generator 目标雷数公式单点 target_mine_count(两调用点改引)+legality 去 db 参数(两调用点同批改)〔wd14〕 - d15 蓄力层上限 CHARGE_LAYER_CAP 常量(两处字面量+注释随动) - d16b unit_container/status_container 六处 Dictionary 取值显式 int() 包裹(读侧包) - d10 minesweeper 两信号按 §2.1 三要素补契约(载荷键集按发射位并集实查)+全库 28 处信号注释载体 #→## 撤销证明:回退 d1 hunk → 同容异实例断言红(A 单据被 B 死亡误废),还原复绿; 回退 d10 契约 → 契约在位扫描 undocumented=2 红,还原归零。等价类申报(d11/d14/d15/d16)见实现报告。 代拍板:#1 target_mine_count 签名(照接口块)、#2 契约措辞与载荷键并集(实查发射位)、#4 int() 读侧包写法。
220 lines
11 KiB
GDScript
220 lines
11 KiB
GDScript
## 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() -> void:
|
||
custom_minimum_size = Vector2(240, 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)
|
||
|
||
col.add_child(UiKit.label("探险者",UiKit.FS_SUB, 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("秘宝",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:
|
||
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)
|
||
_slot_panels.append(slot)
|
||
|
||
# 特性持有段(三选一特性兑付后常驻可读:逐件行「件名 ×N」;空态整节隐藏。
|
||
# 上移承载——构筑核心信息优先级高于临时强化,且防 C 区下部裁切不可见)
|
||
_feat_section = VBoxContainer.new()
|
||
_feat_section.add_theme_constant_override("separation", 4)
|
||
_feat_section.visible = false
|
||
col.add_child(_feat_section)
|
||
_feat_section.add_child(UiKit.label("专长",UiKit.FS_BODY, true))
|
||
_feat_list = VBoxContainer.new()
|
||
_feat_list.add_theme_constant_override("separation", 2)
|
||
_feat_section.add_child(_feat_list)
|
||
# 地形变异常驻角标(Boss 图整图型战斗全程可见防中局遗忘;
|
||
# 空态整节隐藏=特性段先例;摆位/文案 provisional)
|
||
_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("地形变异",UiKit.FS_BODY, true))
|
||
_board_variant_list = VBoxContainer.new()
|
||
_board_variant_list.add_theme_constant_override("separation", 2)
|
||
_board_variant_section.add_child(_board_variant_list)
|
||
# 特性(预留):锻造/大奖励/关卡奖励节点的属性提升——中循环数据因改变战斗数学准入;
|
||
# 与战斗内状态效果(B 区站桩,随战斗消散)分区,防「战斗结束被清」误读;空态整节隐藏
|
||
_curio_section = VBoxContainer.new()
|
||
_curio_section.add_theme_constant_override("separation", 4)
|
||
_curio_section.visible = false
|
||
col.add_child(_curio_section)
|
||
_curio_section.add_child(UiKit.label("属性值提升",UiKit.FS_BODY, true))
|
||
_curio_list = VBoxContainer.new()
|
||
_curio_list.add_theme_constant_override("separation", 2)
|
||
_curio_section.add_child(_curio_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)))
|
||
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 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,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)
|
||
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)))
|
||
# note=积点进度行(自动兑付件悬停浮窗补「积点 N/M」,进度随挖格经组合根重查刷新)
|
||
var note := String(rec.get("note", ""))
|
||
slot.tooltip_text = "%s%s%s" % [String(rec.get("name", "")),
|
||
"(%s)" % note if note != "" else "",
|
||
"(已用)" if used else ("(不可用——当前无有效目标)" if not usable else "")]
|