# VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(§Transitions 结算逐行兑现——B 区因果演出链骨架: # 结算轮光带自棋盘区横扫 → 伤害/治疗弹道自棋盘飞向敌我站桩 → 受击闪白与血条递减由站桩侧承接)。 # 位置语义:全屏覆盖层(A–D 区之上、模态之下),只做跨区飞行的演出载体——棋盘区内演出 # (清图金粒/完美排雷横幅/标中旗弹道/Boss 影子)仍归 board_fx_view。 # 高内聚边界:纯演出——组合根按结算信号调用 play_*,传全局坐标(组合根持有棋盘与站桩两域句柄); # 本类零核心依赖、不自持游戏状态、mouse_filter 全忽略(不劫持输入)。 # 动画密度/缓动曲线归战斗表现样张批终稿——本批落"因果全程可见"骨架(规划兑现 P2 义务)。 # Date: 2026-09-20(结算演出链批) class_name SettlementFxView extends Control func _init() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE set_anchors_preset(Control.PRESET_FULL_RECT) # 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)—— # "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带)。 # 终稿(样张 v2 冻结批 T3):加 2px 亮金前导缘线——扫带运动方向可读(兑现光带逐行横扫 §2.2) func play_round_sweep(board_rect: Rect2) -> void: if board_rect.size == Vector2.ZERO: return var band := ColorRect.new() band.color = Color(UiKit.GOLD, 0.22) band.mouse_filter = Control.MOUSE_FILTER_IGNORE band.position = Vector2(board_rect.position.x, board_rect.position.y) band.size = Vector2(board_rect.size.x, 34.0) add_child(band) var edge := ColorRect.new() edge.color = Color(UiKit.GOLD_LIT, 0.7) edge.mouse_filter = Control.MOUSE_FILTER_IGNORE edge.size = Vector2(board_rect.size.x, 2.0) edge.position = band.position add_child(edge) var tw := band.create_tween() tw.tween_property(band, "position:y", board_rect.end.y - 34.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE) tw.parallel().tween_property(band, "modulate:a", 0.0, UiKit.motion(0.5)).set_delay(UiKit.motion(0.15)) tw.tween_callback(band.queue_free) var etw := edge.create_tween() etw.tween_property(edge, "position:y", board_rect.end.y - 2.0, UiKit.motion(0.5)).set_trans(Tween.TRANS_SINE) etw.parallel().tween_property(edge, "modulate:a", 0.0, UiKit.motion(0.2)).set_delay(UiKit.motion(0.3)) etw.tween_callback(edge.queue_free) # 跨区弹道:glyph 字形自 from 飞向 to(0.35s + 轻微上抛弧线 + 终点淡出,自动回收)—— # 开格之因 → 站桩之果的因果可视承载(P2);crit 弹道由调用方传加重标记 func play_projectile(from: Vector2, to: Vector2, glyph: String, col: Color, crit: bool = false) -> void: var shot := UiKit.label(glyph, 22 if crit else 16, true) shot.add_theme_color_override("font_color", col) shot.mouse_filter = Control.MOUSE_FILTER_IGNORE shot.size = Vector2(24, 24) shot.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER shot.vertical_alignment = VERTICAL_ALIGNMENT_CENTER shot.position = from - shot.size * 0.5 shot.pivot_offset = shot.size * 0.5 shot.z_index = 5 add_child(shot) var mid := (from + to) * 0.5 + Vector2(0, -36.0) # 轻微上抛弧线 var tw := shot.create_tween() if crit: tw.tween_property(shot, "scale", Vector2(1.5, 1.5), UiKit.motion(0.08)) tw.tween_property(shot, "position", mid, UiKit.motion(0.16)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) tw.tween_property(shot, "position", to - shot.size * 0.5, UiKit.motion(0.19)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN) tw.parallel().tween_property(shot, "modulate:a", 0.0, UiKit.motion(0.12)).set_delay(UiKit.motion(0.23)) tw.tween_callback(shot.queue_free) # ---- 轮切换横幅(迭代 8 卡⑦——「第 N 轮」驻留 0.6s 后消隐进下一轮;白名单 0.5–0.8s 内取值)---- # 减动效=静态呈现(驻留后即隐,零位移/零透明度动画——行呈现不跳过同语义,卡⑦ AC③)。 const BANNER_DWELL := 0.6 var _fx_banners := 0 # 演出探针计数(fx_probe 断言面) var _fx_damage_marks := 0 # 三轮 #3——伤害/治疗数字浮现计数 var _fx_status_pulses := 0 # 三轮 #3——增益/减益状态脉冲计数 # 演出探针出口(卡⑦——轮横幅发射计数) func probe_banners() -> int: return _fx_banners # 演出探针出口(三轮 #3——逐条结算演出计数) func probe_settlement_marks() -> int: return _fx_damage_marks + _fx_status_pulses # 伤害/治疗数字浮现(三轮 #3——用户裁决「打中谁就标出多少伤害/主角被打标出多少」): # 站桩头顶浮现数字,上飘 34px 渐隐 0.7s(减动效=时长折半同演不跳过);crit 加大加重。 func play_damage_number(pos: Vector2, text: String, col: Color, crit: bool = false) -> void: _fx_damage_marks += 1 var lab := UiKit.label(text, 26 if crit else 20, true) lab.add_theme_color_override("font_color", col) lab.add_theme_color_override("font_outline_color", Color(0.1, 0.08, 0.06, 0.9)) lab.add_theme_constant_override("outline_size", 4) lab.mouse_filter = Control.MOUSE_FILTER_IGNORE lab.z_index = 6 add_child(lab) await get_tree().process_frame # 等一帧取实际尺寸(文本度量) lab.position = pos - lab.size * 0.5 var tw := lab.create_tween() tw.tween_property(lab, "position:y", lab.position.y - 34.0, UiKit.motion(0.7)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) tw.parallel().tween_property(lab, "modulate:a", 0.0, UiKit.motion(0.35)).set_delay(UiKit.motion(0.35)) tw.tween_callback(lab.queue_free) # 增益/减益状态脉冲(三轮 #3——用户裁决「先上 Buff/Debuff 特效,再显基础伤害」): # 目标站桩处色环扩散淡出 0.5s(增益绿/减益红由调用方传色)。 func play_status_pulse(pos: Vector2, col: Color) -> void: _fx_status_pulses += 1 var ring := Panel.new() ring.mouse_filter = Control.MOUSE_FILTER_IGNORE var sb := UiKit.panel_flat(Color(col, 0.0), col, 100, 3) ring.add_theme_stylebox_override("panel", sb) ring.size = Vector2(22, 22) ring.position = pos - ring.size * 0.5 ring.z_index = 5 add_child(ring) var tw := ring.create_tween() tw.tween_property(ring, "scale", Vector2(2.4, 2.4), UiKit.motion(0.5)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) tw.parallel().tween_property(ring, "modulate:a", 0.0, UiKit.motion(0.5)) tw.tween_callback(ring.queue_free) func play_round_banner(ri: int) -> void: _fx_banners += 1 var banner := Panel.new() banner.mouse_filter = Control.MOUSE_FILTER_IGNORE banner.add_theme_stylebox_override("panel", UiKit.panel_flat(Color(UiKit.BG_OVERLAY, 0.88), UiKit.GOLD_DEEP, 8, 2)) var lab := UiKit.label("第 %d 轮" % ri, 22, true) lab.add_theme_color_override("font_color", UiKit.GOLD) lab.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) lab.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER lab.vertical_alignment = VERTICAL_ALIGNMENT_CENTER banner.add_child(lab) banner.size = Vector2(190, 46) banner.position = Vector2((size.x - banner.size.x) * 0.5, size.y * 0.30) # 棋盘中带上缘——不压明细带 banner.z_index = 4 add_child(banner) if UiKit.motion_scale < 1.0: var st := get_tree().create_timer(BANNER_DWELL) st.timeout.connect(banner.queue_free) return banner.modulate.a = 0.0 var tw := banner.create_tween() tw.tween_property(banner, "modulate:a", 1.0, UiKit.motion(0.15)) tw.tween_interval(UiKit.motion(BANNER_DWELL)) tw.tween_property(banner, "modulate:a", 0.0, UiKit.motion(0.25)) tw.tween_callback(banner.queue_free)