Files
gplan-2/game/smoke/ui_probe.gd
T
DKChan 171a2ddee5 fix(ui): 敌方站桩护甲类型图标化+AC数值退役(2026-09-26 用户终裁)
- 底座下名字行改 HBox 组合:护甲类型图标占位 Label + 名字 Label;文本格式「名字 · 类型·AC%d」退役
- 图标形状+色相双通道占位(◇轻甲·灰石 / ◆魔法护甲·小人蓝 / ▣重甲·深金;避开 Boss 专属紫赤 #8F2F6A 与妨害紫),终稿图标资产归美术批
- _armor_class 字段与 set_data 读取删除(站桩视图零消费方;核心/数据层 armor_class 不动);_armor 保留供图标
- battle_stage_view._card_by_name 与 smoke/ui_probe 名字行断言同步新节点口径(ui_probe 增护甲图标非空检查)
- 全量冒烟 557 项 0 失败退出码 0
2026-09-25 20:41:35 +08:00

217 lines
13 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(场景级 headless 探针——四区布局实例化断言;entry 17②③)。
# Date: 2026-09-12(战斗屏改造批扩展;基础 = 2026-09-11 首战棋盘探针——既有断言全保留)
# 项目运行模式(autoload 在位)实例化 main.tscn,直接驱动 _start_adventure_from_menu()(完整冒险——唯一试玩入口),
# 断言真实构建(棋盘/主角站桩/敌方站桩/AP 点阵/明细带/数据框;A 区进度条断言随 停点前移批 2026-09-23 退役;
# E 区道具面板已删除;2026-09-20 全站桩拍板——英雄卡/敌人卡断言随站桩改造批换装)。
# 运行:Godot_console.exe --headless --path . res://smoke/ui_probe.tscn
extends Node
func _ready() -> void:
await get_tree().process_frame # autoload 就绪
var main = load("res://presentation/main.tscn").instantiate()
add_child(main)
await get_tree().process_frame
var problems: Array = []
# ---- 放弃入口(2026-09-21 用户拍板——台账 119):默认隐藏(非冒险模式无冒险可放弃——开始层/调试两层)----
if main.route_overlay.abandon_btn == null or main.route_overlay.abandon_btn.visible:
print("[ui_probe] FAIL — 放弃按钮默认应隐藏(非冒险模式关)")
get_tree().quit(1)
return
# ---- 营地覆盖层(元进度实现批——主菜单入口/开合/余额文案/购买禁用态;
# 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json——SaveManager 路径注入纪律)----
var iso = load("res://core/save_manager.gd").new("user://smoke_ui_probe_camp.json")
iso.load_from_disk()
main.meta = load("res://core/meta_progression.gd").new(main.db, iso)
var campbtn: Button = _find_button_by_prefix(main._start_overlay, "营地")
if campbtn == null:
problems.append("营地入口按钮缺失(开始层 campbtn)")
else:
campbtn.pressed.emit() # 合成信号路径(真实按钮 → 组合根 _open_camp)
await get_tree().process_frame
if not main.camp_overlay.visible:
problems.append("营地层未打开(campbtn 路径)")
elif main._start_overlay.visible:
problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)")
else:
if not String(main.camp_overlay._balance_label.text).begins_with("钥石"):
problems.append("营地余额文案缺失(%s)" % main.camp_overlay._balance_label.text)
if main.camp_overlay._forge_rows.size() != 3:
problems.append("锻造柜台行数错误(%d ≠ 3)" % main.camp_overlay._forge_rows.size())
else:
var zero_disabled := true
for card in main.camp_overlay._forge_rows:
zero_disabled = zero_disabled and bool(card.btn.disabled) # 零余额全禁用
if not zero_disabled:
problems.append("零余额下锻造购买按钮未全禁用(余额不足禁用态失守)")
if main.camp_overlay._workshop_rows.size() != 6:
problems.append("工坊柜台行数错误(%d ≠ 6)" % main.camp_overlay._workshop_rows.size())
main._on_camp_closed()
await get_tree().process_frame
if main.camp_overlay.visible or not main._start_overlay.visible:
problems.append("营地关闭后未恢复开始层(开合往返失守)")
# 换回全新零档(营地段购买态不泄入后续冒险段——确定性;先删后建防陈旧档泄入)
for p in ["user://smoke_ui_probe_camp.json", "user://smoke_ui_probe_camp.json.tmp"]:
if FileAccess.file_exists(p):
DirAccess.remove_absolute(p)
var iso_zero = load("res://core/save_manager.gd").new("user://smoke_ui_probe_camp.json")
iso_zero.load_from_disk()
main.meta = load("res://core/meta_progression.gd").new(main.db, iso_zero)
main._start_adventure_from_menu()
await get_tree().process_frame
await get_tree().process_frame
# ---- 入口选路停点(停点前移批 2026-09-23:关卡开始即出节点图——level_started 后、首战前弹选路层;
# 真实按钮路径 pick 入口单候选进首战)----
if not main.route_overlay.visible:
problems.append("关卡开始未弹入口选路停点(route_overlay 应可见)")
elif main.runner == null or not main.runner.awaiting_choice() \
or main.runner.next_candidate_ids().size() != 1:
problems.append("入口停点状态失守(选路态 + 入口单候选)")
else:
main.route_overlay.press_candidate(0)
await get_tree().process_frame
await get_tree().process_frame
# ---- 放弃入口·冒险模式可见 + 战斗中按下被拒(真实按钮路径——入口挂选路停点,战斗中拒绝为防御分支)----
if main.route_overlay.abandon_btn == null or not main.route_overlay.abandon_btn.visible:
problems.append("冒险模式放弃按钮未开放(选路停点入口在案)")
else:
main.route_overlay.abandon_btn.pressed.emit()
await get_tree().process_frame
if main.adventure == null or String(main.adventure.state) != "running":
problems.append("首战中放弃未被拒(战斗域单事务在飞——state 应保持 running)")
# ---- 既有断言(回归基线;A 区进度条断言随 停点前移批 2026-09-23 退役——节点链进度由关卡画面选路覆盖层承载)----
if main.board_view._cells.is_empty():
problems.append("棋盘格未生成(cells=0)")
if main.board_view.board() == null:
problems.append("board 未装载")
if main.info_panel.hp_text().begins_with("HP —") or main.info_panel.hp_text().contains("—"):
problems.append("C 区血量未显示(%s)" % main.info_panel.hp_text())
if main.settlement == null or main.settlement.battle_result != "":
problems.append("首战未处于进行中")
if not main.log_view.parsed_text().contains("节点 1/"):
problems.append("节点头日志缺失")
# ---- 四区布局断言(battle.md §Layout)----
if not main.stage.round_label.text.contains("第 1 轮"):
problems.append("B 区轮次·阶段指示缺失(%s)" % main.stage.round_label.text)
if main.stage.enemy_box.get_child_count() < 1:
problems.append("B 区敌方站桩未构建(%d 个)" % main.stage.enemy_box.get_child_count())
if main.ap_bar._dots.get_child_count() != int(main.db.action_economy.ap_per_round):
problems.append("D 区 AP 点阵数量错误(%d)" % main.ap_bar._dots.get_child_count())
if main.ap_bar.refresh_remaining_text() == "":
problems.append("D 区弃图刷新按钮文案缺失")
if main.board_zone.toast == null:
problems.append("D 区 toast 槽缺失")
if main.info_panel.slot_row == null:
problems.append("C 区状态效果槽缺失")
if main.log_view.mode() != "dig" or main.log_view.custom_minimum_size.y != 24.0:
problems.append("B 区明细带挖掘期应收起 24px(mode=%s h=%s)" % [main.log_view.mode(), str(main.log_view.custom_minimum_size.y)])
# E 区道具面板已随可选类删除下线(格面资源批 2026-09-15)——四区构建由其余断言覆盖
# ---- 敌方站桩投影(顺位徽标/意图三档动作动画占位——敌人系统×4;2026-09-20 全站桩拍板)----
var standee = main.stage.enemy_box.get_child(0)
if standee != null and (standee._name_label.text == "" or standee._armor_icon.text == ""):
problems.append("敌立绘名字行缺失(名字或护甲类型图标占位为空——2026-09-26 终裁:AC 不展示、类型图标化)")
if standee != null and standee._intent_badge == null:
problems.append("敌立绘意图指示缺失")
elif standee != null:
# 三档动作动画占位契约(表现层升级批拍板:不显示技能名——图标档位 + 一次性动画)
standee.set_pending("damage", "")
if not standee._intent_badge.visible or standee._intent_badge.text != "⚔":
problems.append("攻击系意图未呈现(visible=%s text=%s)" % [str(standee._intent_badge.visible), standee._intent_badge.text])
standee.set_pending("basic", "")
if standee._intent_badge.visible:
problems.append("普攻应零表示(意图指示应隐藏)")
if main.stage.hero_standee == null or main.stage.hero_standee._hp_bar == null:
problems.append("B 区主角站桩缺失")
# ---- BUG-0001 回归(tooltip 入树后 set_effects 槽位不错位——结算软锁修复沉淀)----
var slots_row = main.info_panel.slot_row
var probe_eff: Array = [{"effect_id": "attack_up", "display_name": "增伤", "layers": 1,
"remaining_settlements": 1, "source_class": "safe_cell", "round_index": 1, "effective_value": 50}]
slots_row.set_effects(probe_eff)
if slots_row._slots.size() != 1:
problems.append("BUG-0001 回归:set_effects 未建槽(%d)" % slots_row._slots.size())
slots_row._on_slot_hover(slots_row._slots[0]) # 悬停 → tooltip _tip 入子节点表(野外崩溃前置态)
slots_row.set_effects(probe_eff) # 修复前:_tip 被按位序当作槽 0 → _fill_slot 崩溃 → 结算协程中断软锁
if not (slots_row._tip in slots_row.get_children()):
problems.append("BUG-0001 回归:悬停后 tooltip 未入树(回归前置失真)")
elif slots_row._slots.size() != 1 or slots_row._slots[0] == slots_row._tip:
problems.append("BUG-0001 回归:tooltip 入树后槽引用错位")
else:
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框架 图标键契约:状态表九行全映射〔定稿 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)
var probe_kb: Array = [
{"effect_id": "enrage", "display_name": "暴走", "layers": 1, "remaining_settlements": 1, "source_class": "flag_miss", "round_index": 1, "effective_value": 25},
{"effect_id": "off_guard", "display_name": "失衡", "layers": 1, "remaining_settlements": 1, "source_class": "flag_miss", "round_index": 1, "effective_value": 25},
]
slots_row.set_effects(probe_kb)
if slots_row._slots.size() != 2:
problems.append("图标键回归:enrage/off_guard 未建槽(%d)" % slots_row._slots.size())
else:
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 仍经组合根接线;
# _reset_run_scope 复现原 _start_single 的提示去重复位)
main._reset_run_scope()
main._debug.start_single(2)
await get_tree().process_frame
await get_tree().process_frame
if main.board_view.board() == null:
problems.append("L2 单场未启动(board=null)")
else:
# 地形变异 首批类型定稿批(2026-09-19):L2 板允许带变异(岩浆/烬覆)——布置域断言 =
# 变异格全在初始展开区外(closed_only 结构保障)
var b0 = main.board_view.board()
var r0p := {}
for c in b0.compute_r0():
r0p[c] = true
for c in b0.variants.keys():
if r0p.has(c):
problems.append("L2 板变异格落在初始展开区(地形变异 布置域违例)")
break
main.board_view.set_desaturated(true)
main.board_view.set_desaturated(false)
if problems.is_empty():
print("[ui_probe] PASS — cells=%d board=%dx%d hp='%s' enemies=%d ap_dots=%d" % [
main.board_view._cells.size(), main.board_view.board().width, main.board_view.board().height,
main.info_panel.hp_text(),
main.stage.enemy_box.get_child_count(), main.ap_bar._dots.get_child_count()])
get_tree().quit(0)
else:
for p in problems:
push_error("[ui_probe] FAIL — " + p)
print("[ui_probe] FAIL — " + "; ".join(problems))
get_tree().quit(1)
# 开始层按钮按文案前缀寻址(campbtn 句柄未存组合根成员——树遍历只读;text 前缀匹配首枚按钮)
func _find_button_by_prefix(root: Node, prefix: String) -> Button:
if root is Button and String(root.text).begins_with(prefix):
return root
for child in root.get_children():
var found := _find_button_by_prefix(child, prefix)
if found != null:
return found
return null