diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 4c0b7df3..386f87a8 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -505,11 +505,15 @@ func _play_cross_zone_fx(row: Dictionary) -> void: match String(row.get("type", "")): "damage": var target := String(row.get("target", "")) + var crit := bool(row.get("crit", false)) if target == "" or target == "hero": + # 主角受击弹道(迭代 6 T5 拆「敌→主角零反馈」早退):⚔ 危险红飞向主角站桩; + # crit 只加重字号不加金(金=收益/胜利语义位,不用于敌方对主角输出) + settlement_fx.play_projectile(from_v, stage.hero_standee_center(), "⚔", + UiKit.RED_MINE, crit) return var to_v = stage.enemy_standee_center(target) if to_v != null: - var crit := bool(row.get("crit", false)) settlement_fx.play_projectile(from_v, to_v, "⚔", UiKit.GOLD if crit else UiKit.RED_MINE, crit) "heal": diff --git a/game/presentation/views/battle_stage_view.gd b/game/presentation/views/battle_stage_view.gd index aa89d201..839df109 100644 --- a/game/presentation/views/battle_stage_view.gd +++ b/game/presentation/views/battle_stage_view.gd @@ -285,7 +285,18 @@ func on_step_row_fx(row: Dictionary) -> void: return var target := String(row.get("target", "")) if target == "hero" or target == "": - hero_standee.set_hero_state("hit") # 受击态(短驻自回 idle;闪白/弹道归迭代 6 T5 拆早退) + # 主角受击演出(迭代 6 T5 拆早退——旧「敌→主角零反馈」):弹道到达拍对齐, + # 受击态+闪白+打击火花+击杀塌陷与敌方路径同密度(演出对等——计划书 §1.4) + hero_standee.set_hero_state("hit") # 受击态(短驻自回 idle) + var hero_arrived := func(): + hero_standee.play_hit_impact() + hero_standee.flash_hit() + if bool(row.get("killed", false)): + hero_standee.mark_dead() + if get_tree() != null: + get_tree().create_timer(UiKit.motion(0.33)).timeout.connect(hero_arrived) + else: + hero_arrived.call() return hero_standee.set_hero_state("attack") # 我方攻击行——attack 态(短驻自回) var card: Node2D = _card_by_name(target) diff --git a/game/presentation/views/standee_body.gd b/game/presentation/views/standee_body.gd index 748433c5..20643d3a 100644 --- a/game/presentation/views/standee_body.gd +++ b/game/presentation/views/standee_body.gd @@ -264,6 +264,7 @@ const DEATH_SHARD_N := 5 # 碎块数(§8.4 冻结值) const HIT_SPARK_N := 6 # 命中火花数(迭代 6 T4 终稿——打击瞬间可读性一拍) var _death_shards := 0 # 探针出口计数(碎块节点回收后仍保留读数) var _hit_sparks := 0 # 探针出口计数(命中火花累计) +var _flash_count := 0 # 探针出口计数(受击闪白触发次数——主角路由断言面) # 死亡碎块弹飞:立绘盒中心五块碎石抛物线散落(程序化色块——确定性散布,禁全局随机) @@ -287,9 +288,9 @@ func _spawn_death_shards() -> void: stw.tween_callback(shard.queue_free) -# 演出探针出口(fx_probe——死亡碎裂/命中火花断言面) +# 演出探针出口(fx_probe——死亡碎裂/命中火花/闪白计数断言面) func fx_probe() -> Dictionary: - return {"death_shards": _death_shards, "hit_sparks": _hit_sparks} + return {"death_shards": _death_shards, "hit_sparks": _hit_sparks, "flash_count": _flash_count} # 命中瞬间打击特效(迭代 6 T4 终稿):立绘盒中心六枚短火花放射(程序化色块,确定性方向 @@ -317,8 +318,9 @@ func is_dead() -> bool: return _dead -# 受击闪白(结算演出占位——§2.2 受击反馈) +# 受击闪白(结算演出占位——§2.2 受击反馈;强度时长复核迭代 6 T4=维持现行) func flash_hit() -> void: + _flash_count += 1 if _hp_flash_tw != null and _hp_flash_tw.is_valid(): _hp_flash_tw.kill() _hp_flash_tw = create_tween() diff --git a/game/smoke/fx_probe.gd b/game/smoke/fx_probe.gd index 47ceb5fd..f64a0102 100644 --- a/game/smoke/fx_probe.gd +++ b/game/smoke/fx_probe.gd @@ -106,6 +106,27 @@ func _ready() -> void: if hero_sb.is_dead() or String(hero_sb.hero_state_probe().get("state", "")) != "idle": problems.append("主角死亡后 set_hero 复活失败(is_dead 应假+态回 idle)") + # ---- ⑥ 主角受击/死亡路由(迭代 6 T5——battle_stage 主角早退拆除的呈现链断言) ---- + var db2 = load("res://core/config_db.gd").new() + if db2.load_tables("res://data/tables", false): + var stage2 = BattleStageView.new(db2) + 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/结算光带+缘线/主角三态切换 全链路通过")