From 6578b284b1318825c01da7df4605abcc8ba5c206 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 29 Sep 2026 04:16:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(t5):=20=E4=B8=BB=E8=A7=92=E5=8F=97?= =?UTF-8?q?=E5=87=BB+=E6=AD=BB=E4=BA=A1=E6=BC=94=E5=87=BA=E2=80=94?= =?UTF-8?q?=E2=80=94=E6=8B=86=E4=B8=A4=E5=A4=84=E6=97=A9=E9=80=80,?= =?UTF-8?q?=E6=BC=94=E5=87=BA=E5=AF=86=E5=BA=A6=E4=B8=8E=E6=95=8C=E6=96=B9?= =?UTF-8?q?=E5=AF=B9=E7=AD=89=20=E2=91=A0battle=5Fstage=5Fview.on=5Fstep?= =?UTF-8?q?=5Frow=5Ffx=20hero=20=E6=97=A9=E9=80=80=E6=8B=86=E9=99=A4:?= =?UTF-8?q?=E5=8F=97=E5=87=BB=E6=80=81+=E5=BC=B9=E9=81=93=E5=88=B0?= =?UTF-8?q?=E8=BE=BE=E6=8B=8D(0.33s)=E9=97=AA=E7=99=BD+=E6=89=93=E5=87=BB?= =?UTF-8?q?=E7=81=AB=E8=8A=B1+=E5=87=BB=E6=9D=80=E5=A1=8C=E9=99=B7(killed?= =?UTF-8?q?=20=E8=A1=8C)=E2=80=94=E2=80=94=E4=B8=8E=E6=95=8C=E6=96=B9?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E5=90=8C=E6=9E=84=E5=90=8C=E5=AF=86=E5=BA=A6?= =?UTF-8?q?;=E6=A0=91=E5=A4=96=E5=AE=88=E5=8D=AB=E5=8D=B3=E6=97=B6?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=20=E2=91=A1main.=5Fplay=5Fcross=5Fzone=5Ffx?= =?UTF-8?q?=20hero=20=E6=97=A9=E9=80=80=E6=8B=86=E9=99=A4:=E6=95=8C?= =?UTF-8?q?=E2=86=92=E4=B8=BB=E8=A7=92=E4=BC=A4=E5=AE=B3=E8=A1=8C=20?= =?UTF-8?q?=E2=9A=94=20=E5=8D=B1=E9=99=A9=E7=BA=A2=E5=BC=B9=E9=81=93?= =?UTF-8?q?=E9=A3=9E=E5=90=91=E4=B8=BB=E8=A7=92=E7=AB=99=E6=A1=A9;crit=20?= =?UTF-8?q?=E5=8F=AA=E5=8A=A0=E9=87=8D=E5=AD=97=E5=8F=B7=E4=B8=8D=E5=8A=A0?= =?UTF-8?q?=E9=87=91(=E9=87=91=3D=E6=94=B6=E7=9B=8A/=E8=83=9C=E5=88=A9?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E4=BD=8D,=E4=B8=8D=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E6=95=8C=E6=96=B9=E8=BE=93=E5=87=BA=E2=80=94=E2=80=94=E4=BB=A3?= =?UTF-8?q?=E6=8B=8D=E6=9D=BF=E5=BE=85=E8=BF=BD=E8=AE=A4)=20=E2=91=A2stand?= =?UTF-8?q?ee=5Fbody=20flash=5Fhit=20=E5=A2=9E=20=5Fflash=5Fcount=20?= =?UTF-8?q?=E6=8E=A2=E9=92=88=E8=AE=A1=E6=95=B0;fx=5Fprobe=20=E6=89=A9?= =?UTF-8?q?=E7=AC=AC=E5=85=AD=E6=AE=B5=3D=E4=B8=BB=E8=A7=92=E5=8F=97?= =?UTF-8?q?=E5=87=BB=E8=B7=AF=E7=94=B1(=E9=97=AA=E7=99=BD=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E5=A2=9E=E9=87=8F+=E7=81=AB=E8=8A=B1)=E4=B8=8E?= =?UTF-8?q?=E5=87=BB=E6=9D=80=E5=A1=8C=E9=99=B7=E6=96=AD=E8=A8=80(BattleSt?= =?UTF-8?q?ageView=20=E5=AE=9E=E4=BD=93=E7=BA=A7=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=B5=8B=E8=AF=95)=20=E8=BD=BB=E9=97=B8=3D=E5=86=92=E7=83=9F?= =?UTF-8?q?=20576/0+=E4=B8=89=E6=8E=A2=E9=92=88=20PASS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/main.gd | 6 +++++- game/presentation/views/battle_stage_view.gd | 13 +++++++++++- game/presentation/views/standee_body.gd | 8 +++++--- game/smoke/fx_probe.gd | 21 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) 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/结算光带+缘线/主角三态切换 全链路通过")