merge: 状态图标接入批——定稿九枚入 HUD 状态槽(2026-09-24,台账 156)

This commit is contained in:
DKChan committed 2026-09-24 15:13:01 +08:00
commit 71ae51cb35
12 files changed
+86 -57

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

+25 -10
View File
@@ -2,7 +2,8 @@
# 实现 GDD:design/ux/battle.md(B 区全站桩形态——2026-09-20 用户看对照样张拍板,台账 99:
# 主角与敌人均为无框立绘+底座,英雄卡/敌人卡卡片式整体退役禁再提)。
# 形态契约:无框立绘(程序占位字形;set_portrait_texture = 资产挂点,战斗表现样张批落位零代码换装)
# + 底座;血条悬浮头顶;状态小图标一排挂血条正下方(形状+色相双通道、层数角标同 C 区状态槽键——
# + 底座;血条悬浮头顶;状态小图标一排挂血条正下方(2026-09-24 状态图标接入批:定稿 PNG 纹理
# 取代程序化占位字形,极性染色退役——绿↑红↓极性语言内化于图标本体;层数 ×N 角标保留;
# 词汇单点复用 StatusSlotRow.EFFECT_ICON,未知键同款 fail-fast + ? 占位)。
# 敌方专属:顺位徽标悬浮头顶;意图三档动作动画占位(表现层升级批拍板:不显示技能名——普攻零表示 /
# 攻击系 ⚔ / 增益系 ✦ / 妨害系 ☠ / 蓄力 = ⚔ 金档;开轮摇定播一次性短动画后静帧保持,
@@ -151,18 +152,32 @@ func set_idle() -> void:
set_effects([])
# 主角/敌方共用——状态小图标行重建(B 区迷你版:icon + 层数角标;完整 tooltip 义务归 C 区状态槽)
# 主角/敌方共用——状态小图标行重建(B 区迷你版:定稿纹理 16px + 层数 ×N 角标;
# 完整 tooltip 义务归 C 区状态槽。2026-09-24 状态图标接入批:占位字形+极性染色退役)
func set_effects(effects: Array) -> void:
for c in _status_row.get_children():
_status_row.remove_child(c)
c.queue_free()
for eff in effects:
var cfg: Array = _icon_cfg(String(eff.get("effect_id", "")))
var tex := _icon_tex(String(eff.get("effect_id", "")))
var layers := maxi(1, int(eff.get("layers", 1)))
var lab := UiKit.label(String(cfg[0]) + ("×%d" % layers if layers > 1 else ""), 12, true)
lab.add_theme_color_override("font_color", cfg[1])
lab.mouse_filter = Control.MOUSE_FILTER_IGNORE
_status_row.add_child(lab)
if tex != null:
var icon := TextureRect.new()
icon.texture = tex
icon.custom_minimum_size = Vector2(16, 16) # 32px 源 ÷2 整数档缩放
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
_status_row.add_child(icon)
else:
var ph := UiKit.label(StatusSlotRow.ICON_PLACEHOLDER, 12, true) # release 回退占位(debug 已断言)
ph.mouse_filter = Control.MOUSE_FILTER_IGNORE
_status_row.add_child(ph)
if layers > 1:
var lab := UiKit.label("×%d" % layers, 12, true)
lab.mouse_filter = Control.MOUSE_FILTER_IGNORE
_status_row.add_child(lab)
func set_order(i: int) -> void:
@@ -283,10 +298,10 @@ func _refresh_portrait_glyph() -> void:
# 状态图标键解析(词汇单点复用 C 区 StatusSlotRow——HUD框架:查不到的键按错误处理,
# 立即断言 + ? 占位纹样 + 诊断,禁止静默显示空图标)
func _icon_cfg(effect_id: String) -> Array:
# 立即断言 + ? 占位 + 诊断,禁止静默显示空图标)
func _icon_tex(effect_id: String) -> Texture2D:
if StatusSlotRow.EFFECT_ICON.has(effect_id):
return StatusSlotRow.EFFECT_ICON[effect_id]
push_error("[站桩] 未知状态图标键(映射表外键——立即断言): %s" % effect_id)
assert(false, "未知状态图标键: " + effect_id)
return ["?", UiKit.PANEL_BORDER, "?"]
return null
+46 -33
View File
@@ -1,7 +1,9 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 实现 GDD:design/ux/battle.md(C 区状态效果槽 ×4〔增益状态 D1×①〕——effect_id→icon 形状+色相双通道、
# 实现 GDD:design/ux/battle.md(C 区状态效果槽 ×4〔增益状态 D1×①〕——effect_id→定稿 PNG 纹理图标、
# 层数角标、剩余结算次数点、悬停 tooltip(来源+语义+剩余);空态 = 槽隐藏(数据框上移补位,
# EC12 空态裁决);极性映射 = 战斗结算 SettlementTable 效果语义而非 magnitude 符号〔增益状态 D1×③〕)。
# 2026-09-24 状态图标接入批:程序化占位字形(▲◇▼◎⚔✕☾⇧▣)+ 按极性染色退役——定稿九枚 PNG
# (白剑/白盾/白眼+绿↑红↓箭头语言,用户逐键验收,圣经 §4.6.1 v2.2 纹样系统)自带极性,裸图标呈现。
# Date: 2026-09-12
# 高内聚边界:纯视图——效果投影装配归 hud_presenter.gd;增益状态 纯被动容器零信号——刷新绑 战斗结算
# 结算编排完成时点(battle.md 更新触发注记)。
@@ -9,22 +11,28 @@ class_name StatusSlotRow
extends HBoxContainer
const MAX_SLOTS := 4
# 极性/语义映射(数据源 = SettlementTable.effect_pool_map 效果语义 + StatusEffectTable display_name):
# 益 = ▲/◆/▼/◇(金/蓝族);害 = ⚡/⚔/✕/☾/⇧/▣(旗朱族;⇧/▣ = 敌方增益威胁纹样)——形状+色相双通道,
# 文字名常驻第三通道。状态表九行全映射(attack_up/ac_up/weaken/alert/enrage/off_guard/sapped/empower/fortify)
# ——HUD框架 图标键契约"一键一枚专属纹样,查不到的键立即断言 + ? 占位 + 诊断"
# 纹理映射(2026-09-24 状态图标接入批):effect_id→定稿纹理,全局恒定禁换图(图标键契约 §4.6 的
# 文件侧延伸——文件键 = status_<effect_id>.png,命名规范圣经 §8.3「对象键 = 数据表主键」)。
# 状态表九行全映射(attack_up/ac_up/weaken/alert/enrage/off_guard/sapped/empower/fortify)
# ——HUD框架 图标键契约"一键一枚专属纹样,查不到的键立即断言 + 占位 + 诊断"
# (2026-09-15 格面资源批补 sapped;2026-09-16 护甲等级掷点批 defense_up → ac_up 换装;
# 同日 敌人技能池批补 empower/fortify 敌方增益两键——敌方增益显示 = 敌卡状态行文本 + 本图标键词汇)。
const EFFECT_ICON := {
"attack_up": ["▲", UiKit.GOLD_DEEP, "益"],
"ac_up": ["◇", UiKit.HERO_BLUE, "益"],
"weaken": ["▼", UiKit.HERO_BLUE, "益"],
"alert": ["◎", UiKit.FLAG_RED, "害"],
"enrage": ["⚔", UiKit.FLAG_RED, "害"],
"off_guard": ["✕", UiKit.FLAG_RED, "害"],
"sapped": ["☾", UiKit.FLAG_RED, "害"],
"empower": ["⇧", UiKit.FLAG_RED, "害"],
"fortify": ["▣", UiKit.PANEL_BORDER, "害"],
"attack_up": preload("res://assets/ui/icons/status_attack_up.png"),
"ac_up": preload("res://assets/ui/icons/status_ac_up.png"),
"weaken": preload("res://assets/ui/icons/status_weaken.png"),
"alert": preload("res://assets/ui/icons/status_alert.png"),
"enrage": preload("res://assets/ui/icons/status_enrage.png"),
"off_guard": preload("res://assets/ui/icons/status_off_guard.png"),
"sapped": preload("res://assets/ui/icons/status_sapped.png"),
"empower": preload("res://assets/ui/icons/status_empower.png"),
"fortify": preload("res://assets/ui/icons/status_fortify.png"),
}
# 极性词(tooltip 第三通道;运行时不再按极性染色/画框——绿↑红↓极性语言已内化于定稿图标本体)
const EFFECT_POLARITY := {
"attack_up": "益", "ac_up": "益", "weaken": "益",
"alert": "害", "enrage": "害", "off_guard": "害",
"sapped": "害", "empower": "害", "fortify": "害",
}
const EFFECT_SEMANTIC := {
"attack_up": "主角攻击加成(hero_offense 池加算)",
@@ -38,8 +46,8 @@ const EFFECT_SEMANTIC := {
"fortify": "敌方防御 −50(enemy_defense 池——硬化技施放,主角攻击 ×0.5;持续 1 个有效结算)",
}
const SOURCE_NAMES := {"safe_cell": "安全格", "flag_hit": "标中旗", "mine_hit": "踩雷", "clear_reward": "清图奖励"}
# 未知图标键的占位纹样(HUD框架:占位也要可见,禁静默空图标)
const ICON_PLACEHOLDER := ["?", UiKit.PANEL_BORDER, "?"]
# 未知图标键的占位(HUD框架:占位也要可见,禁静默空图标——debug 断言先行,release 回退槽内 ? 文本)
const ICON_PLACEHOLDER := "?"
var _tip: PanelContainer
var _tip_label: Label
@@ -71,15 +79,13 @@ func set_effects(effects: Array) -> void:
_slots[i].visible = true
# battle_over 反馈(增益状态×4④):逐槽化光收回 + 微文案(下次 set_effects 恢复常态)
# battle_over 反馈(增益状态×4④):逐槽化光收回 + 微灰化(下次 set_effects 恢复常态;
# 2026-09-24 状态图标接入批:原字形 "✕ " 前缀随占位字形退役,灰化即清场反馈)
func set_cleared_feedback() -> void:
_cleared_feedback = true
for slot in _slots:
slot.visible = true
slot.modulate = Color(0.7, 0.7, 0.7, 0.5)
var col := slot.get_child(0) as VBoxContainer
var icon := col.get_child(0) as Label
icon.text = "✕ " + icon.text
func _sync_slots(n: int) -> void:
@@ -105,8 +111,13 @@ func _make_slot() -> PanelContainer:
var col := VBoxContainer.new()
col.alignment = BoxContainer.ALIGNMENT_CENTER
slot.add_child(col)
var icon := UiKit.label("", 16, true)
icon.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
# 定稿纹理图标(32px 源 → 24px 槽内呈现;线性过滤匹配抗锯齿矢量风源图)
var icon := TextureRect.new()
icon.custom_minimum_size = Vector2(24, 24)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
col.add_child(icon)
var meta := UiKit.label("", 11, false)
meta.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
@@ -118,17 +129,19 @@ func _fill_slot(slot: PanelContainer, eff: Dictionary) -> void:
slot.modulate = Color(1, 1, 1)
slot.set_meta("eff", eff)
var col := slot.get_child(0) as VBoxContainer
var icon := col.get_child(0) as Label
var icon := col.get_child(0) as TextureRect
var meta := col.get_child(1) as Label
var icon_cfg: Array = _icon_cfg(String(eff.effect_id))
icon.text = String(icon_cfg[0])
icon.add_theme_color_override("font_color", icon_cfg[1])
# 层数角标 + 剩余结算次数点(● 未耗余量)
# 层数角标 + 剩余结算次数点(● 未耗余量)——叠层数字呈现逻辑保持不动
var dots := ""
var left: int = int(eff.get("remaining_settlements", 0))
for i in maxi(left, 1):
dots += "●" if i < left else "○"
meta.text = "×%d %s" % [int(eff.get("layers", 1)), dots]
var tex := _icon_tex(String(eff.effect_id))
icon.visible = tex != null
icon.texture = tex
if tex == null:
meta.text = ICON_PLACEHOLDER + " " + meta.text # release 回退占位(debug 已断言)
func _on_slot_hover(slot: Control) -> void:
@@ -136,11 +149,11 @@ func _on_slot_hover(slot: Control) -> void:
if not slot.has_meta("eff"):
return
var eff: Dictionary = slot.get_meta("eff")
var icon_cfg: Array = _icon_cfg(String(eff.effect_id))
var eid := String(eff.effect_id)
var src: String = String(SOURCE_NAMES.get(String(eff.get("source_class", "")), String(eff.get("source_class", ""))))
var semantic: String = String(EFFECT_SEMANTIC.get(String(eff.effect_id), String(eff.get("display_name", ""))))
var semantic: String = String(EFFECT_SEMANTIC.get(eid, String(eff.get("display_name", ""))))
_tip_label.text = "%s〔%s〕\n%s\n剩余结算 %d 次\n来源:%s(第 %d 轮)" % [
String(eff.get("display_name", "")), String(icon_cfg[2]), semantic,
String(eff.get("display_name", "")), String(EFFECT_POLARITY.get(eid, ICON_PLACEHOLDER)), semantic,
int(eff.get("remaining_settlements", 0)), src, int(eff.get("round_index", 0))]
if not _tip.is_inside_tree():
add_child(_tip)
@@ -159,10 +172,10 @@ func _on_slot_hover(slot: Control) -> void:
_tip.visible = true
# 图标键解析(HUD框架:查不到的键按错误处理——立即断言 + ? 占位纹样 + 诊断,禁止静默显示空图标)
func _icon_cfg(effect_id: String) -> Array:
# 图标键解析(HUD框架:查不到的键按错误处理——立即断言 + 占位 + 诊断,禁止静默显示空图标)
func _icon_tex(effect_id: String) -> Texture2D:
if EFFECT_ICON.has(effect_id):
return EFFECT_ICON[effect_id]
push_error("[状态槽] 未知图标键(映射表外键——立即断言): %s" % effect_id)
assert(false, "未知图标键: " + effect_id)
return ICON_PLACEHOLDER
return null
+15 -14
View File
@@ -137,20 +137,21 @@ func _ready() -> void:
elif slots_row._slots.size() != 1 or slots_row._slots[0] == slots_row._tip:
problems.append("BUG-0001 回归:tooltip 入树后槽引用错位")
else:
var icon_lab: Label = slots_row._slots[0].get_child(0).get_child(0)
if icon_lab.text != "▲":
problems.append("BUG-0001 回归:悬停后重刷槽未填充(icon=%s)" % icon_lab.text)
var icon_rect: TextureRect = slots_row._slots[0].get_child(0).get_child(0)
if icon_rect.texture == null:
problems.append("BUG-0001 回归:悬停后重刷槽未填充(texture=null)")
slots_row.set_effects([probe_eff[0], probe_eff[0], probe_eff[0], probe_eff[0], probe_eff[0], probe_eff[0]])
if slots_row._slots.size() != 4:
problems.append("BUG-0001 回归:超上限效果未截断至 4 槽(%d)" % slots_row._slots.size())
slots_row.set_effects([])
if not slots_row._slots.is_empty():
problems.append("BUG-0001 回归:空态未清槽(%d)" % slots_row._slots.size())
# ---- 图标键回归(HUD框架 图标键契约:状态表七行全映射、格面资源 cell_type 九型全映射〔gp 键 = 九型批 2026-09-20〕;
# 查不到的键 fail-fast + ? 占位——此处钉死映射不残缺,未知键断言由 _icon_cfg/_badge_glyph 承担)----
if not StatusSlotRow.EFFECT_ICON.has("enrage") or not StatusSlotRow.EFFECT_ICON.has("off_guard") \
or not StatusSlotRow.EFFECT_ICON.has("sapped"):
problems.append("状态图标表缺暴走/失衡/乏力键(states 表七行须全映射)")
# ---- 图标键回归(HUD框架 图标键契约:状态表九行全映射〔定稿 PNG 纹理,2026-09-24 状态图标接入批〕、
# 格面资源 cell_type 九型全映射〔gp 键 = 九型批 2026-09-20〕;查不到的键 fail-fast + ? 占位
# ——此处钉死映射不残缺,未知键断言由 _icon_tex/_badge_glyph 承担)----
for eid in ["attack_up", "ac_up", "alert", "weaken", "enrage", "off_guard", "sapped", "empower", "fortify"]:
if not StatusSlotRow.EFFECT_ICON.has(eid) or StatusSlotRow.EFFECT_ICON[eid] == null:
problems.append("状态图标九键纹理映射缺失(%s)" % eid)
for cat in ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic", "action", "gp"]:
if not BoardView.REWARD_BADGE.has(cat):
problems.append("奖励图标表缺键(%s)" % cat)
@@ -162,12 +163,12 @@ func _ready() -> void:
if slots_row._slots.size() != 2:
problems.append("图标键回归:enrage/off_guard 未建槽(%d)" % slots_row._slots.size())
else:
var ic_enrage: Label = slots_row._slots[0].get_child(0).get_child(0)
var ic_off: Label = slots_row._slots[1].get_child(0).get_child(0)
if ic_enrage.text == "?" or ic_enrage.text == "":
problems.append("暴走图标键未映射(icon=%s)" % ic_enrage.text)
if ic_off.text == "?" or ic_off.text == "":
problems.append("失衡图标键未映射(icon=%s)" % ic_off.text)
var ic_enrage: TextureRect = slots_row._slots[0].get_child(0).get_child(0)
var ic_off: TextureRect = slots_row._slots[1].get_child(0).get_child(0)
if ic_enrage.texture == null:
problems.append("暴走图标键未映射(texture=null)")
if ic_off.texture == null:
problems.append("失衡图标键未映射(texture=null)")
slots_row.set_effects([])
# ---- L2 单场段(debug_session 单场原语 + 去饱和接口可用性;雾格机制已整体移除,不再断言变体呈现)----
# 2026-09-13 单场用户入口已删——探针直调 debug_session 原语(battle_created/ready 仍经组合根接线;