Files
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

166 lines
7.8 KiB
GDScript
Raw Permalink 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
# 演出密度探针(样张 v2 冻结批 T3——演出四项可触发+探针断言覆盖):
# ①死亡碎裂(standee mark_dead → 碎块计数)②意图三档动画(damage/buff/debuff/charge 摇定不炸+徽标可见)
# ③胜利纹章+失败掸灰+封印光收回(battle_end open_victory/open_defeat → fx_probe 计数)
# ④结算轮光带(settlement_fx play_round_sweep → 子节点落位)。
# 运行:Godot_console.exe --headless --path . res://probe/fx_probe.tscn
extends Node
func _ready() -> void:
await get_tree().process_frame
var problems: Array = []
# ---- ①② 站桩侧:死亡碎裂 + 意图三档 ----
var sb_script := load("res://presentation/views/standee_body.gd")
var sb = sb_script.new("enemy")
sb.box = Vector2(150, 176) # 站桩盒几何(Node2D 自定义字段——非 Control.size)
add_child(sb)
sb.set_data({"display_name": "地精", "enemy_id": "goblin", "hp": 10, "max_hp": 10})
await get_tree().process_frame
# 意图三档:特殊准备(damage/buff/debuff)与蓄力档(heavy)摇定——动画触发+徽标可见
for tier in [["damage", "slash"], ["buff", "atk_up"], ["debuff", "weaken_hit"], ["damage", "heavy"]]:
sb.set_pending(tier[0], tier[1])
if not sb.intent_probe().visible:
problems.append("意图徽标应可见(%s/%s)" % [tier[0], tier[1]])
await get_tree().process_frame
sb.set_pending("basic", "") # 档①普通攻击=零表示
if sb.intent_probe().visible:
problems.append("普通攻击徽标应零表示(§4.6.4 档①)")
# 死亡碎裂
sb.mark_dead()
await get_tree().process_frame
var dp: Dictionary = sb.fx_probe()
if int(dp.get("death_shards", 0)) != 5:
problems.append("死亡碎块应 5 块(§8.4 冻结值),实得 %s" % [dp.get("death_shards")])
if not sb.is_dead():
problems.append("mark_dead 后 is_dead 应真")
# ①b 命中火花(打击瞬间特效计数)
var sb2 = sb_script.new("enemy")
sb2.box = Vector2(150, 176)
add_child(sb2)
sb2.play_hit_impact()
await get_tree().process_frame
var hp2: Dictionary = sb2.fx_probe()
if int(hp2.get("hit_sparks", 0)) != 6:
problems.append("命中火花应 6 枚(迭代 6 T4),实得 %s" % [hp2.get("hit_sparks")])
# ---- ③ 终局侧:胜利纹章 / 失败掸灰+封印光 ----
var end_ov := BattleEndOverlay.new()
add_child(end_ov)
var br := Rect2(300, 300, 400, 300)
end_ov.open_victory("胜利", "继续", br)
await get_tree().process_frame
var vp: Dictionary = end_ov.fx_probe()
if int(vp.get("crest_shards", 0)) != 10:
problems.append("胜利纹章碎砖应 10 枚(§2.4),实得 %s" % [vp.get("crest_shards")])
end_ov.close()
end_ov.open_defeat("失败", "重来", br)
await get_tree().process_frame
var fp: Dictionary = end_ov.fx_probe()
if int(fp.get("dust_motes", 0)) != 8:
problems.append("失败掸灰尘絮应 8 枚(§2.5),实得 %s" % [fp.get("dust_motes")])
if int(fp.get("seal_dots", 0)) != 8:
problems.append("封印光收回应 8 点,实得 %s" % [fp.get("seal_dots")])
end_ov.close()
# ---- ④ 结算轮光带(含前导亮缘) ----
var sfx := SettlementFxView.new()
add_child(sfx)
sfx.play_round_sweep(br) # 光带几何全由 board_rect 驱动——无需容器尺寸
await get_tree().process_frame
var n_fx := 0
for ch in sfx.get_children():
if ch is ColorRect:
n_fx += 1
if n_fx < 2:
problems.append("结算光带应含带体+前导缘线 ≥2 节点,实得 %d" % n_fx)
# ---- ④b 轮切换横幅双态(常态=淡入驻留消隐动画链;减动效=静态呈现零动画)----
sfx.play_round_banner(3)
await get_tree().process_frame
var banner_nodes := 0
for ch in sfx.get_children():
if ch is Panel:
banner_nodes += 1
if banner_nodes != 1:
problems.append("轮横幅应落位 1 面(卡⑦),实得 %d" % banner_nodes)
if int(sfx.probe_banners()) != 1:
problems.append("轮横幅计数失守(probe_banners=%s)" % sfx.probe_banners())
var saved_motion: float = UiKit.motion_scale
UiKit.motion_scale = 0.5
sfx.play_round_banner(4)
await get_tree().process_frame
if int(sfx.probe_banners()) != 2:
problems.append("减动效轮横幅未发射(卡⑦ AC③——静态化仍呈现不跳过)")
UiKit.motion_scale = saved_motion
# ---- ④c 逐条结算标出(三轮 #3——状态脉冲+伤害数字浮现断言面) ----
sfx.play_status_pulse(Vector2(120, 120), Color("7cc47c"))
sfx.play_damage_number(Vector2(120, 120), "-7", Color("d9402a"), false)
await get_tree().process_frame
await get_tree().process_frame
if int(sfx.probe_settlement_marks()) != 2:
problems.append("逐条结算标出未落位(状态脉冲+数字浮现——三轮 #3),实得 %s" % sfx.probe_settlement_marks())
# ---- ⑤ 主角三态切换(attack/hit 短驻自回 + 死亡复活态回 idle) ----
var hero_sb = sb_script.new("hero")
hero_sb.box = Vector2(150, 176)
add_child(hero_sb)
var t_idle: Texture2D = UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT)
var t_atk: Texture2D = UiKit.load_tex(UiKit.TEX_HERO_ATTACK)
var t_hit: Texture2D = UiKit.load_tex(UiKit.TEX_HERO_HIT)
if t_idle == null or t_atk == null or t_hit == null:
problems.append("主角三态素材缺位(portrait/attack/hit——迭代 6 T3)")
else:
hero_sb.set_portrait_texture(t_idle, true)
hero_sb.register_hero_state("idle", t_idle)
hero_sb.register_hero_state("attack", t_atk)
hero_sb.register_hero_state("hit", t_hit)
hero_sb.set_hero_state("attack")
if String(hero_sb.hero_state_probe().get("state", "")) != "attack":
problems.append("主角攻击态切换失败(set_hero_state attack)")
await get_tree().create_timer(UiKit.motion(0.5)).timeout
if String(hero_sb.hero_state_probe().get("state", "")) != "idle":
problems.append("attack 态未短驻自回 idle(0.35s)")
hero_sb.set_hero_state("hit")
if String(hero_sb.hero_state_probe().get("state", "")) != "hit":
problems.append("主角受击态切换失败(set_hero_state hit)")
hero_sb.set_hero_state("idle")
hero_sb.mark_dead()
hero_sb.set_hero(6, 10) # 新战斗开场 HP 回正——死亡残位复活(敌侧 set_data 同款语义)
if hero_sb.is_dead() or String(hero_sb.hero_state_probe().get("state", "")) != "idle":
problems.append("主角死亡后 set_hero 复活失败(is_dead 应假+态回 idle)")
# ---- ⑥ 主角受击/死亡路由(battle_stage 主角走完整呈现链的断言) ----
var db2 = load("res://core/config_db.gd").new()
if db2.load_tables("res://data/tables", false):
var stage2 = BattleStageView.new(db2, db2.presentation.battle_layout)
add_child(stage2)
var flash_before: int = int(stage2.hero_standee.fx_probe().get("flash_count", 0))
stage2.on_step_row_fx({"type": "damage", "target": "hero", "killed": false})
await get_tree().create_timer(UiKit.motion(0.45)).timeout # >到达拍 0.33s
var hfx: Dictionary = stage2.hero_standee.fx_probe()
if int(hfx.get("flash_count", 0)) <= flash_before:
problems.append("主角受击闪白未触发(on_step_row_fx hero 路由——迭代 6 T5)")
if int(hfx.get("hit_sparks", 0)) < 6:
problems.append("主角受击火花未触发(演出对等——迭代 6 T5)")
stage2.on_step_row_fx({"type": "damage", "target": "hero", "killed": true})
await get_tree().create_timer(UiKit.motion(0.45)).timeout
if not stage2.hero_standee.is_dead():
problems.append("主角击杀塌陷未触发(killed 行——迭代 6 T5)")
stage2.queue_free()
else:
problems.append("fx_probe⑥ 表加载失败(config_db)")
# ---- 汇总 ----
if problems.is_empty():
print("[fx_probe] PASS — 死亡碎裂5块/命中火花6/意图三档+零表示/胜利纹章10/失败掸灰8+封印光8/结算光带+缘线/轮横幅双态/逐条结算标出/主角三态切换 全链路通过")
get_tree().quit(0)
else:
for p in problems:
print("[fx_probe] FAIL — " + p)
print("[fx_probe] FAIL — %d 项断言未过" % problems.size())
get_tree().quit(1)