Files
gplan-2/game/smoke/fx_probe.gd
T
DKChan 8723d9d826 feat(t4): 命中特效终稿+敌方演出链复核——打击火花六枚+弹道到达拍对齐
①standee_body.play_hit_impact:立绘盒中心六枚短火花放射(程序化色块,确定性方向 i*60°,亮金GOLD_LIT/暖白WARM 交替=物理打击语义不占 危险红=雷/奖励金=收益 位;0.12s 一拍即回收);fx_probe 增 hit_sparks 计数+第六断言段
②弹道到达拍对齐:on_step_row_fx 敌方路径 闪白/火花/击杀塌陷 延迟 0.33s≈弹道飞行(0.16+0.19)——呈现链序=弹道→闪白+火花→血条→灰化;突发多定时器互不干扰(flash 重启/死亡幂等),卡片释放 is_instance_valid 守卫,树外即时执行
③flash_hit 强度时长复核=维持现行(1.6,1.4,1.2)/0.08s+回0.2s——白光可读不糊,±30% 白名单内零调整;④伤害飘字子项代拍板=不做(血条已有「当前/最大」即时数字面,飘字=同一伤害第二呈现触 UI 禁重复展示铁律;审计备注项未立项)
2026-09-29 04:12:42 +08:00

118 lines
5.4 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
# 演出密度探针(样张 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://smoke/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 命中火花(迭代 6 T4 终稿——打击瞬间特效计数)
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("flag_dots", 0)) != 8:
problems.append("旗光收回应 8 点,实得 %s" % [fp.get("flag_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)
# ---- ⑤ 主角三态切换(迭代 6 T3——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)")
# ---- 汇总 ----
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)